在Base *数据结构内部格式化和使用派生类的正确方法是什么?

时间:2018-11-13 01:32:48

标签: c++ inheritance vector derived-class base-class

简介

在保存多个派生类的数据结构的正确方法上,我有些失落。我是C ++的新手,如果我有愚蠢的错误/误解,敬请原谅。

此刻,我使用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:使用派生类的成员函数

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中的最后一个对象是类Derived的实例,但仅将其识别为类Base。这当然是很合理的。 objects按住Base*

虽然我了解错误发生的原因,但我对如何解决却感到困惑。在我以前的搜索中,我发现有两种通常提出(也是颇有争议的)解决方案:包括我的不同派生类在virtual中用作Base函数的每个成员函数,以及使用静态转换。 / p>

在包含Base*的数据结构中使用派生类的成员函数的正确方法/良好做法是什么?

#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
}

在这种情况下,除了“仅使用print()方法代替”之外,我发现很少有解决该问题的可靠建议。我知道我可以解决此问题,但我宁愿了解实现某些事物的正确方法,而不仅仅是避免使用它。

因此,当从vector<Base*>进行调用时,是否可以执行衍生类的重载插入运算符?有朋友功能吗?

#3:确定对象是哪个派生类

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;
}

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

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

我知道这是一个很长的问题,但是在我看来,这是如此紧密地联系在一起,我应该将三个子问题放在一起。

感谢您的帮助!

2 个答案:

答案 0 :(得分:1)

您正在实现一个非抽象基类。在多态中,尝试寻找基类/派生类中是否存在方法被认为是错误的程序实践。将常用方法声明为虚拟方法。像这样尝试。

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; 
}

答案 1 :(得分:1)

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

当每种派生类型共享一个通用接口时,该技术最有效。 动态多态性的全部要点是 调用代码 不需要知道或关心实际的类型是,它只在乎使用特定的 界面

在您的示例中:

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

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

3)在系统中动态多态性失败的部分(您需要了解对象的具体类型),建议的发现类型的方法是使用dynamic_cast

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

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

我在C++中发现,动态多态性确实不是解决许多问题的最佳解决方案。尝试使所有内容动态多态是一种诱惑,但是IMO仅对给定的面向对象设计问题子集有效。

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