在 Base* 数据结构中格式化和使用 derived 类 的正确方法是什么?

What is the proper method to format and use derived classes inside of a Base* data structure?

简介

我对保存多个派生 classes 的数据结构的正确方法有点迷茫。我是 C++ 的新手,很抱歉,如果我有白痴 errors/misconceptions.

目前,我使用 vector<Base*>*,这对我来说很有意义。但是,当我尝试使用向量中包含的对象时,我 运行 遇到了一些问题。

我运行遇到的问题

我将使用此示例设置来展示我的问题:

Base.h

class Base {

 public:
  Base();
  Base(int a, int b);
  virtual ~Base();

  friend std::ostream& operator<<(std::ostream& os, Base& base);

  int getA();
  int getB();

 protected:
  int a;
  int b;

};

Derived.h

class Derived : public Base {

 public:
  Derived(int a, int b, int c);
  ~Derived();

  friend std::ostream& operator<<(std::ostream& os, Derived& derived);

  getC();

 private:
  int c;

};

#1:使用派生的classes

的成员函数
int main() {
  vector<Base*>* objects = new vector<Base*>();

  Base* newbase = new Base(0, 1);
  Derived newderived = new Derived(2, 3, 4);
  objects.push_back(newbase);
  objects.push_back(newderived);

  cout << objects->front()->getA() << endl; // '0'
  cout << objects->back()->getA() << endl; // '2'

  cout << objects->back()->getC() << endl; // error: 'class Base' has no member named 'getC()'
}

尽管 objects 中的最后一个对象是 class Derived 的实例,但它仅被识别为 class Base。这当然是完全有道理的; objects 持有 Base*.

虽然我明白错误发生的原因,但我对如何解决它感到困惑。在我之前的搜索中,我发现有两个普遍提出的(也相当有争议的)解决方案:包括我的不同派生 classes 在 Base 中用作 virtual 函数的每个成员函数和使用静态转换。

在包含 Base* 的数据结构中使用派生 classes 的成员函数的正确 method/good 做法是什么?

#2:使用友元:即重载插入运算符

int main() {

  vector<Base*>* objects = new vector<Base*>();

  Base* newbase = new Base(0, 1);
  Derived newderived = new Derived(2, 3, 4);
  objects.push_back(newbase);
  objects.push_back(newderived);

  cout << *(objects->front()) << endl; // Executed `Base`'s overloaded insertion operator
  cout << *(objects->back()) << endl; // Also executed `Base`'s overloaded insertion operator
}

在这种情况下,除了 "just use a print() method instead." 之外,我发现很少有解决此问题的可靠建议。我知道我可以解决这个问题,但我宁愿了解实现某些东西的正确方法,而不仅仅是避免使用它。

那么,当从 vector<Base*> 调用时,是否有可能以某种方式执行派生的 class' 重载插入运算符?有友元函数吗?

#3:识别派生的 class 对象是

int main() {
  vector<Base*>* objects = new vector<Base*>();

  Derived newderived = new Derived(2, 3, 4);
  Derived2 newderived2 = new Derived2(5, 6, 7, 8);
  objects.push_back(newderived);
  objects.push_back(newderived2);

  if (/* objects->front() is a Derived */) cout << "Type check success << endl;
  if (/* objects->back() is a Derived2 */) cout << "Type check success << endl;
}

这个问题,具体来说,之前已经解决过好几次了。我看到的两个解决方案是以某种方式评估静态转换后发生的情况,并通过在基础 class 中存储某种形式的类型列表并在所有派生的 classes.[=26 中存储类型值=]

但是,正如我之前提到的,我是 C++ 的新手,我不了解前一个选项或实现第二个选项的正确方法。 解决这个问题的正确方法是什么,有没有解释的示例视频之类的供我研究?

我知道这是一个有点长的问题,但在我看来,它们之间的联系是如此紧密,以至于我应该将三个子问题放在一起。

感谢您的帮助!

您正在实现一个非抽象基础 class。在多态性中,试图寻找一个方法是否存在于 base/derived class 中被认为是错误的程序实践。将通用方法声明为虚拟方法。像这样试试。

class Base {

public:
    Base();
    Base(int a, int b);
    virtual ~Base();

    friend std::ostream& operator<<(std::ostream& os, Base& base);

    virtual int getA();
    virtual int getB();
    virtual int getC() { return 0; }

protected:
    int a;
    int b;

};

class Derived : public Base {

public:
    Derived(int a, int b, int c);
    ~Derived();

    friend std::ostream& operator<<(std::ostream& os, Derived& derived);

    virtual int getA();
    virtual int getB();
    virtual int getC();

private:
    int c;

};

int main() {
    vector<Base*> objects;

    Base* newbase = new Base(0, 1);
    Derived newderived =  Derived(2, 3, 4);
    Base* pnewderived = &newderived;
    objects.push_back(newbase);
    objects.push_back(pnewderived);

    cout << objects.front()->getA() << endl; 
    cout << objects.back()->getA() << endl; 

    cout << objects.back()->getC() << endl; 
}

IMO 你所有的问题都源于试图与你选择的解决方案作斗争 - 动态多态性

当每个 派生类型 共享一个 公共接口 时,该技术最有效。 动态多态性的全部意义在于调用代码不需要知道或关心actual类型是,它只关心它使用特定的接口.

在你的例子中:

1) 您正在尝试使用两个不同的 界面。你的动态 多态性通过 Base 接口起作用,而您想使用 Derived 接口。使用 动态多态性 选择您的界面。如果您需要了解系统给定部分的特定类型,那么 多态性 根本不是该代码区域的最佳解决方案。

2) 正确的方法是添加一个print()函数。这个想法是每个派生类型都知道如何打印自己,这样调用代码就不需要知道或关心它是如何完成的。

3) 在系统中 动态多态性 失败的部分(您需要知道对象的具体类型)然后发现类型的推荐方法是使用 dynamic_cast:

if(auto derived = dynamic_cast<Derived*>(objects->front()))
{
     derived->getC(); // Derived specific calls
}

如果指针类型错误,则返回 nullptr 并且 if() 失败。

我在 C++ 中发现 动态多态性 确实不是很多问题的最佳解决方案。可能会有尝试使所有内容都动态多态的诱惑,但 IMO 只适用于给定的面向对象设计问题的子集。

C++ 在其他领域也表现出色,例如 静态多态性 (使用模板引擎)和 过程多态性 (函数重载)。这些也很值得探索。