在保存多个派生类的数据结构的正确方法上,我有些失落。我是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;
};
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*
的数据结构中使用派生类的成员函数的正确方法/良好做法是什么?
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*>
进行调用时,是否可以执行衍生类的重载插入运算符?有朋友功能吗?
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 ++的新手,我不了解前一种选择或实现第二种选择的正确方法。 解决此问题的正确方法是什么?我有没有解释的示例视频或类似内容供我研究?
我知道这是一个很长的问题,但是在我看来,这是如此紧密地联系在一起,我应该将三个子问题放在一起。
感谢您的帮助!
答案 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++
在其他方面也很出色,例如静态多态性(使用模板引擎)和过程多态性(函数重载)。这些也很值得探索。