【问题标题】:How to correctly create a base class in c++?如何在 C++ 中正确创建基类?
【发布时间】:2012-01-02 23:46:10
【问题描述】:

我正在自学 C++,“创建”一个 RPG。但是,我坚持创建一个名为 Item 的类,它是所有游戏项目的基类(或者如果它是 Java 中的一个接口)。这是我的代码:

class Item {

    public:

    virtual const char* GetName() = 0;
};

我阅读了一些关于我正在使用的“纯虚拟”函数的内容。

我的角色类,我在其中使用我的项目:

class Character {

  // some code...

  void ConsumeItem(Item item);

}

但是 gcc 给了我这个错误:

g++ -Wall -o adventure character.cpp adventure.cpp
In file included from adventure.cpp:3:0:
character.h:36:24: error: cannot declare parameter ‘item’ to be of abstract type ‘Item’
item.h:4:7: note:   because the following virtual functions are pure within ‘Item’:
item.h:11:22: note:     virtual const char* Item::GetName()

我对 C++ 中的 OOP 非常陌生,因为它与 Java 非常不同。我已经阅读了一些关于 C++ 中抽象类的文章,但我无法让它工作。

您能告诉我实现这一目标的正确方法吗?

【问题讨论】:

  • 您需要先学习基本的 C++ 结构,例如引用和指针。我希望这不会扼杀你的嗡嗡声,但你不能指望你从 C++ 中的 Java 学到的东西。语言完全不同。我会推荐a good introductory C++ book
  • 不要通过编写 RPG 来开始学习 C++。试试 Pong 或 Conway 的 Game of Life(如果您想坚持游戏)并首先学习基本概念。今天听起来可能令人失望,但有一天你会为此给我 +1。
  • 这个术语是“类型拼接”,本质上你是在基于可能的派生类型创建 item 的实例 - 编译器抱怨该类型无法实例化,因为它具有纯虚方法。
  • 其实我已经知道C了,但是由于没有OOP范式,所以我转向了C++。这就是我尝试创建这个 RPG 的原因(只是几个类,以查看 OOP 的实际应用)。但是,我将尝试阅读更多有关 C++ 的内容,以更好地了解该语言的背景。感谢您的提示!

标签: c++ oop interface


【解决方案1】:

在 Java 中,执行类似操作将创建对 Item 的引用,然后您可以为其分配具体子类的实例。

但是在 C++ 中,Item item创建Item 的实例,这是不允许的,因为Item 包含纯virtual 函数。

为避免此问题,您需要将item 设为指针或引用,例如:

void ConsumeItem(Item *item);

【讨论】:

  • 为了 OP 的利益,这被称为多态,即item 是一个多态指针。
  • 差别真的很大。但是经过你的解释,我看的更清楚了,毕竟是有道理的。谢谢大家!
【解决方案2】:

通常,如果您有一个类层次结构,您希望使用引用或 [智能] 指针指向您的对象。在您的代码摘录中,Item 是一个抽象基类,因此无法创建。但是,您的 ConsumeItem 函数的 Item 参数是按值传递的:没有“&”和“*”的对象是 C++ 中的值。您可能希望使用以下之一传递参数:

  • void ConsumeItem(Item& item) // 通过引用传递
  • void ConsumeItem(Item const& item) // 通过引用传递不可修改的对象
  • void ConsumeItem(Item* item) // 使用指针传递
  • void ConsumeItem(std::shared_ptr<Item> item) // 使用智能指针传递

顺便说一句,你不容易做到的是:如果你的基类有 any 虚函数,也让你的析构函数为虚函数!否则,您几乎肯定会遇到未定义的行为,这通常是一件坏事。也就是说,在您的Item 类中添加:

virtual ~Item() {} // can also be defined out of line but always has to be defined

【讨论】:

  • 你不是说std::shared_ptr<Item> item吗?
  • 呃,是的。我想,这是一个 HTML 的东西:我会更正答案。
  • 我还将析构函数添加到我的类中。感谢您的提示!
【解决方案3】:

由于Item 是一个抽象类,你不能使用它的实例,你应该使用指针* 或引用& 来指向它。

在你的情况下,你传递了它的一个实例,你应该传递一个指针:

class Character {

  // some code...

  void ConsumeItem(Item* item);

}

因此,您实际上是在传递一个指向非抽象类对象的指针,该类对象不是从Item 继承的。

【讨论】:

    【解决方案4】:

    使ConsumeAccept 接收到Item 的引用(或指针):

    void ConsumeItem(Item& item);
    

    Item 是多态的,它的确切大小取决于实现它的派生类,因此不允许通过值传递它(理论上如果它没有纯虚函数是可能的,但是效果不会是预期的)。

    确保您了解 C++ 中的指针、引用和值类型。在 Java 中,所有对象都被视为没有额外符号(例如&)的引用来表示这一点。差异至关重要。

    【讨论】:

    • 需要明确的是,Java 引用介于 C++ 引用和 C++ 指针之间。都是“句柄”。
    【解决方案5】:

    你不能实例化包含纯虚方法的类,Item item 就是这样的非实例。不过,您可以为它们创建指针,因此Item *item 将起作用。

    【讨论】:

      【解决方案6】:

      为了在 C++ 中使用多态性,您需要使用指针(或引用,但我现在将忽略它)。

      您必须将多态对象存储为指针,将它们作为指针传递,等等。

      修改后的 ConsumeItem 将是:

      class Character {
      
        // some code...
      
        void ConsumeItem(Item* item);
      
      }
      

      这将通过使用 shared_ptr 来进一步改进以避免内存泄漏。

      class Character {
      
        // some code...
      
        void ConsumeItem(std::shared_ptr<Item> item);
      
      }
      

      将所有内容作为 shared_ptr 传递实际上会非常接近 Java 语义(但要注意循环)。

      【讨论】:

      • "为了在 C++ 中使用多态性,您需要使用指针。" ...或参考!
      • @R.MartinhoFernandes 参考资料往往不能很好地处理资源管理,但我想值得一提。
      猜你喜欢
      • 2012-05-11
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多