派生自抽象基类并调用另一个类中的方法的 C++ 包装类,该类也派生自抽象基类
Posted
技术标签:
【中文标题】派生自抽象基类并调用另一个类中的方法的 C++ 包装类,该类也派生自抽象基类【英文标题】:C++ wrapper class that is derived from an abstract base class and calls a method in another class that is also derived from the abstract base class 【发布时间】:2018-08-03 11:04:42 【问题描述】:我有一个名为 Animal
的抽象类和一个名为 cow
的派生类。
Cow
在Animal
中提供了纯虚函数的定义。
我想创建一个名为AnimalWrapper
的类,它继承自Animal
。我希望方法AnimalWrapper.speak()
简单地调用cow.speak()
。
我是否只需要在animalwrapper 类中引用cow
对象,以便调用非静态方法speak?
我该怎么做?
#include<iostream>
#include<cstdlib>
using namespace std;
#include <string>
class Animal // This Animal is an abstract base class
public:
Animal();
virtual const char* speak() = 0; //pure virtual function
;
class Cow: public Animal
public:
Cow();
virtual const char* speak()
cout << "I am a cow" << endl;
return "Moo ";
;
class AnimalWrapper: public Animal
public:
AnimalWrapper()
virtual const char* speak()
cout << "Calling cow class speak() method" << endl;
//Call Cow::speak()
return "Moo";
;
int main()
AnimalWrapper AnimalWrapper_obj ;
std::cout << AnimalWrapper_obj.speak() << '\n';
【问题讨论】:
是什么阻止了您从Cow
而不是Animal
派生CowWrapper
?在这种情况下,您可以从 CowWrapper
中直接调用 Cow::speak()
(或者根本不覆盖它)...
我很好奇为什么你需要包装器?它旨在解决什么问题?
至于你的问题,如果你想在一个对象上调用一个非静态成员函数,你需要一个该对象的实例。也许包装器类应该通过组合包含一个被包装对象的实例(否则它现在不是一个 包装器 吗?;))
您有一个 CowWrapper,它是一种动物,但它不是牛。您的代码中没有任何奶牛。哪头牛应该说话()?你可以在CowWrapper::speak() return cow().speak();
创建一头新牛
您的请求非常接近常见的设计模式,如适配器、桥接器,尤其是抽象工厂(即稍后您可能不仅希望奶牛发言,还希望其他人发言)。
【参考方案1】:
根据您的评论,这里有一个 AnimalWrapper 类的示例,它使用移动语义获取 Cow 实例的外部引用。由于它是移动构造函数而不是复制构造函数,因此在使用原始对象时应该小心。
如果您想保留 Cow 的原始实例,那么您可能需要一个复制构造函数(但您将复制您的 Cow 实例)。
您也可以在 Cow * cowInstance
或 Cow& cowInstance
类中存储指针或引用),但您可能会遇到悬空指针/引用(这就是我在这里使用移动语义的原因)。
#include<iostream>
#include<cstdlib>
#include <memory>
// using namespace std; // <= Using namespace std is considered bad practice
#include <string>
class Animal // This Animal is an abstract base class
public:
Animal()
virtual ~Animal() // Do some research about the difference between virtual and non virtual destructor in C++
virtual std::string speak() = 0; //pure virtual function
;
class Cow: public Animal
public:
Cow()
Cow(const Cow& cow) = delete;
Cow(Cow&& _cow) std::cout << "Move constructor " << std::endl;
virtual ~Cow()
// virtual const char* speak() // <= Use std::string in c++
virtual std::string speak()
++m;
std::cout << "I am a cow : "<< m << std::endl;
return std::string("Mooo");
private:
int m = 0;
;
class AnimalWrapper // : public Animal // <= This is unnecessary
public:
AnimalWrapper(std::unique_ptr<Cow> _cow) : cowInstance((std::move(_cow)))
std::string speak()
std::cout << "Calling cow class speak() method" << std::endl;
return cowInstance->speak();
private:
std::unique_ptr<Cow> cowInstance;
;
int main()
std::unique_ptr<Cow> cow(new Cow);
cow->speak();
std::cout << "Hello, world!" << std::endl;
AnimalWrapper AnimalWrapper_obj(std::move(cow));
std::cout << AnimalWrapper_obj.speak() << '\n';
由于我仍然不清楚您要解决的问题,这可能不是一个理想/完美的解决方案。尽管如此,根据我的理解,这是你问的一个例子。
【讨论】:
感谢您的友好解释。这就是我想要理解的。移动语义对我来说有点太高级了,尽管我有点理解你在做什么。要求查看在类中存储引用的相同示例是否太过分了。我想了解的是,您会在AnimalWrapper
类中存储的 Cow& cowInstance
上调用 speak()
方法。然后我将继续移动语义。谢谢
存储引用的问题在于,您要么存储对外部 Cow
对象的引用,而您无法控制该对象的生命周期,并且您可能会因悬空引用而导致程序崩溃(引用已删除的对象)。或者您必须复制 Cow
object 但您随后拥有 2 个独立且独立的 Cow
实例。【参考方案2】:
假设您想扩展正在使用的 API,但无法自行修改,我的结论是您希望提供并行层次结构:
namespace wrapped
class Animal
public:
virtual ~Animal();
virtual std::string speak();
;
class Cow : public Animal
public:
std::string speak() override;
unsigned int giveMilk() return 77;
;
namespace wrapping
class Animal
protected:
wrapped::Animal& animal;
Animal(wrapped::Animal& animal)
: animal(animal)
public:
virtual ~Animal()
// does not need to be virtual, we can profit from
// polymorphism of referred object...
//
// still it CAN be virtual, if you want to allow derived
// wrappers as well to modify the inherited speak!
std::string speak()
return animal.speak();
;
class Cow : public Animal
public:
Cow(wrapped::Cow& cow)
: Animal(cow)
// speak is inherited
unsigned int giveMilk()
// don't need dynamic cast as we passed a reference
// to cow object to base class constructor...
return static_cast<wrapped::Cow&>(animal).giveMilk();
;
如果Cow
从Animal
继承虚拟,您可以使用的变体允许您编写更少的代码:
namespace wrapping
class Animal : private virtual wrapped::Animal
public:
using wrapped::Animal::speak;
;
class Cow : public Animal, private wrapped::Cow
public:
using wrapped::Cow::speak;
;
如果Cow
没有虚拟继承,那么您的包装Cow
将继承wrapped::Animal
的两个实例,这将导致一些问题暂时不在这里讨论。
如果您在wrapping::animal
中覆盖speak
,则需要在wrapping::Cow
中提供一个最终覆盖器,以解决speak
的两个继承覆盖变体之间的歧义。
实际上,您可以公开地从包装的基础继承,然后您就不再需要 using 声明(但最终的覆盖问题仍然存在),但这将允许两者之间的狂野混合Wrapped 和 wrapping 类,你很可能想要阻止它们。
【讨论】:
说实话,有一个错字(CowWrapper 而不是 Cow 的构造函数名称,此外,wrapping
命名空间尚未关闭)。实际上,您应该通过检查编译器的错误消息很容易发现自己。学习理解这些将在未来为您带来很多麻烦,并且可能会有很多关于 SO...的问题......以上是关于派生自抽象基类并调用另一个类中的方法的 C++ 包装类,该类也派生自抽象基类的主要内容,如果未能解决你的问题,请参考以下文章