代码之家  ›  专栏  ›  技术社区  ›  Runsva

继承类工厂?

  •  0
  • Runsva  · 技术社区  · 2 年前

    我试图了解如何在C++中正确地创建继承类工厂。我在这个例子中使用的是C++14。

    我举了一个简单的例子,试图理解这是如何工作的:

    #include <iostream>
    
    class Animal
    {
    public:
        virtual std::string Sound();
    };
    
    class Dog : Animal
    {
    public:
        std::string Sound() {return "Woff!";};
    };
    
    class Cat : Animal
    {
    public:
        std::string Sound() {return "Meow!";};
    };
    
    class Snake : Animal
    {
    public:
        std::string Sound() {return "Ssss!";};
    };
    
    Animal GetAnimal(std::string name)
    {
        if (name == "Dog")
            return Dog();
        else if (name == "Cat")
            return Cat();
        else if (name == "Snake")
            return Snake();
    
        return Animal();
    }
    
    int main()
    {
        std::cout << "Sound of a dog: \"" << GetAnimal("Dog").Sound() << "\"\n";
        std::cout << "Sound of a cat: \"" << GetAnimal("Cat").Sound() << "\"\n";
        std::cout << "Sound of a snake: \"" << GetAnimal("Snake").Sound() << "\"\n";
    
        return 0;
    }
    

    哪里 Dog , Cat Snake 都是从继承的类 Animal 。我想从中获取每种动物类型的本地实例 GetAnimal 函数,提供了动物名称作为该函数的自变量。

    上面的代码在尝试编译每一个时都会提示以下错误 return 中的行 GetAnimal 功能:

    E0269   conversion to inaccessible base class "Animal" is not allowed
    

    是否不能将继承的类对象分配给继承类的基类型的变量?在C++中做这样的事情的正确方法是什么?

    感谢您阅读我的文章,如有任何指导,不胜感激。

    0 回复  |  直到 2 年前
        1
  •  4
  •   Ted Lyngmo    2 年前

    您需要使用基类指针或引用,还需要使用 public 遗产制作基类析构函数通常是个好主意 virtual 如果你计划通过基类指针来破坏对象,这也是你想要做的。

    它可能看起来是这样的:

    #include <iostream>
    #include <memory>
    
    class Animal {
    public:
        virtual ~Animal() = default;
        virtual std::string Sound() = 0; // needs to be implemented by derived class
    };
    
    class Dog : public Animal {
    public:
        std::string Sound() override { return "Woff!"; };
    };
    
    class Cat : public Animal {
    public:
        std::string Sound() override { return "Meow!"; };
    };
    
    class Snake : public Animal {
    public:
        std::string Sound() override { return "Ssss!"; };
    };
    
    std::unique_ptr<Animal> GetAnimal(std::string name) {
        if (name == "Dog")
            return std::make_unique<Dog>();
        else if (name == "Cat")
            return std::make_unique<Cat>();
        else if (name == "Snake")
            return std::make_unique<Snake>();
    
        throw std::runtime_error("Invalid animal");
    }
    

    并且是这样使用的:

    int main() {
        std::cout << "Sound of a dog: \"" << GetAnimal("Dog")->Sound() << "\"\n";
        std::cout << "Sound of a cat: \"" << GetAnimal("Cat")->Sound() << "\"\n";
        std::cout << "Sound of a snake: \"" << GetAnimal("Snake")->Sound() << "\"\n";
    }
    

    输出:

    Sound of a dog: "Woff!"
    Sound of a cat: "Meow!"
    Sound of a snake: "Ssss!"
    
        2
  •  2
  •   James Kanze    2 年前

    这里有许多问题:

    • 默认情况下,C++中的继承是 private ,这意味着 基类仅在派生类中可见。外部的任何代码 派生类将看不到它。因此的实例 Dog 不会 转换为 Animal 的成员功能之外
    • C++中的继承只能通过指针或引用工作。什么时候 您返回的一个实例 动物 (不是指针或引用), 出现了一种称为切片的情况:的一个新实例 动物 是 创建,通过复制 动物 派生类的一部分。这 新实例的类型为 动物 ,以及虚拟函数 在中,它将解析为中的函数 动物 。这使我们
    • 动物 具有纯虚拟功能,因此是抽象的,不能 实例化。当您 尝试返回 动物

    要纠正这一点,必须解决以下每一点:

    • 声明继承 public
    • 返回引用或(可能)智能指针。鉴于事实 派生类是幂等的(因为它们不包含数据成员),我认为 可能会返回引用(可能 const :在这个 情况下,这真的没有什么区别,但这是一个好主意 总是返回 const 引用,除非您打算让调用者 修改对象)。
    • 无法实例化 动物 ,所以如果没有对手,你就有 去做别的事情。您可以返回一个指针,而不是 引用,如果没有匹配项,则返回一个空指针 name , 或者您可以引发异常,甚至中止(考虑到没有匹配 作为断言失败)——这里的正确解决方案取决于 将使用此代码的应用程序。如果没有匹配项 条件下,返回指针可能是最合适的。这 如果例如, 名称 来自用户输入。 或者,如果没有匹配被认为是异常的(比如 因为 名称 来自文件,只有当 文件已损坏),则异常将允许程序恢复, 或者至少输出具有更多信息的错误消息。最后 如果 名称 已经过预先审查,没有一个匹配是硬编程 错误,中止可能是适当的。(但这取决于 应用程序——在关键应用程序中,通常会中止,因此 备份将接管,但在具有重要用户的应用程序中 输入时,如果没有给定 应用程序保存数据的机会。)