派生自抽象基类并调用另一个类中的方法的 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 的派生类。

CowAnimal 中提供了纯虚函数的定义。

我想创建一个名为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 * cowInstanceCow&amp; 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&amp; cowInstance 上调用 speak() 方法。然后我将继续移动语义。谢谢 存储引用的问题在于,您要么存储对外部 Cow 对象的引用,而您无法控制该对象的生命周期,并且您可能会因悬空引用而导致程序崩溃(引用已删除的对象)。或者您必须复制 Cowobject 但您随后拥有 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();
    
;

如果CowAnimal 继承虚拟,您可以使用的变体允许您编写更少的代码:

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++ 包装类,该类也派生自抽象基类的主要内容,如果未能解决你的问题,请参考以下文章

基类 派生类 类的继承与约束

抽象派生类中的抽象方法覆盖/实现抽象基类的抽象或具体方法。如何以及为啥?

[ C++ ] 抽象类 虚函数 虚函数表 -- C++多态

初学C++之虚函数及抽象类

C++ | 类继承

在其派生类C++的构造函数中调用基类的构造函数[重复]