我遇到了C ++问题。我希望获取一个派生类,将其作为基类与其他一些基类一起使用。然后对所有只需要它们作为基类的类执行一些操作。在此之后,我希望再次获得该课程的派生部分。
我试图尽可能地简化问题并制作一个测试程序,如下所示
#include <vector> //Needed for the holding class and main
#include <iostream> //For output to terminal
class Base
{
int a; //Variable for all classes derived from base
public:
Base() { a = 13; }; //Set a to something
int get_a() { return a; }; //Access a
virtual void bugger_all() {}; //To make the class polymorphic (my first alarm that I might be doing something wrong with polymorphism
};
class Derived:public Base
{
int b;//not accessable by the base class
public:
Derived():Base() { b = 7; };//Set b and a to something
int get_b() { return b; };
void bugger_all() {}; //Implements to virtual function from the base class (just to make everything polymorphic)
};
//Holds several bases
class Holder
{
std::vector<Base> bases;//Holds a vector of base classes, not nessesarily derived classes but can be
public:
void add(Base to_add) { bases.push_back(to_add); };//Add something to the vector
Base get(int i) { return bases[i]; };//Get a certain vector
void print_all() { for(unsigned int i=0; i<bases.size(); i++) std::cout << bases[i].get_a() << "\n"; }; //Prints a for all bases, note that this is specific only to bases and can also be used for derived classes
std::vector<Base> get_all() { return bases; };//Returns the main vector
};
int main(int argc, char *argv[])
{
Derived higher = Derived(); //The derived class (extends the base class)
Base lower = Base(); //Simply a base class, for comparisons
Holder holding_class = Holder();//Will hold both the above objects
//Add the objects to the holder
holding_class.add(lower);
holding_class.add(higher);
//Prints everything in the holder
holding_class.print_all();
std::vector<Base> all_bases = holding_class.get_all(); //Get all the bases back again
std::cout << all_bases[1].get_a() << "\n"; //Test to see if we have retained a from the derived class
Derived higher_again = *(static_cast<Derived*>(&all_bases[1])); //Cast is done here, take a base class and change it to a derived class
std::cout << higher_again.get_b() << "\n"; //Output something specific to the derived class
return 0;//Exit
}
使用g ++编译,没有错误。程序运行,输出
13
13
13
0
如果程序按预期工作,我希望输出为
13
13
13
7
这向我表明,'higher_again'被错误地投射并且其'b'值以某种方式丢失,并且编译器只是将该值设置为0.
从环顾四周看来似乎不建议使用dynamic_cast和static_cast(可能是因为这样的问题)。但是,我无法看到解决问题的方法。我也意识到我可能在多态性方面做错了(必须创建一个无用的虚函数)。任何意见将是有益的。提前谢谢。
答案 0 :(得分:2)
这向我表明'high_again'被错误地投射并且其'b'值以某种方式丢失,并且编译器只是将该值设置为0.。
因为您在向量中存储了Base
类型,导致slicing
因此将Derived objects
切成'Base`对象。
尝试存储指向base的指针,派生对象不会被切片,多态通过指针工作:
std::vector<std::shared_ptr<Base>> bases;
答案 1 :(得分:1)
要按预期使用多态,您需要将Base *存储到动态分配的内存中。当你有...时
std::vector<Base> bases;//Holds a vector of base classes, not nessesarily derived classes but can be
...你正在创建一个向量 - 实际上是一个Base的压缩数组 - 其中没有物理空间用于派生类添加的b
数据成员。
找一本书! ;-P
答案 2 :(得分:1)
您需要区分值类型和引用类型。
值类型的示例:
Base base;
Derived derived;
参考类型
的示例Derived* p_derived = &derived;
Base& ref_base = *p_derived;
当您声明值类型时,它是完全您声明的内容,因为内存是为您指定类型的对象分配的。
当您声明引用类型时,您不会为对象分配更多内存。相反,您可以处理某些预先存在的对象。当您使用引用类型时,您可以使用句柄引用的多态。
我喜欢将值类型视为用于声明具体实例的紧密变量:它正是您所声明的那样。
相比之下,当我想抽象出混凝土时,我喜欢使用引用类型作为松散句柄。 引用类型背后的实际值可以是继承层次结构中引用类型的任何值。因此,如果您有Base *,则指向的实际对象可以是Base或从Base派生的任何内容,包括Derived。
回到你的问题。根据定义,所有std容器的工作都是基于值的。如果你想要多态,在这个上下文中,诀窍是存储指向Base的指针作为你的值。例如,std::vector<Base*>
,std::unique_ptr<Base>
等......而不是std::vector<Base>
本身。
如果存储std::vector<Base>
,则只能在向量中使用Base实例,而不能使用其他任何内容。因此,如果您尝试存储Derived实例,则称为 slicing ,因为您最终会通过存储Derived实例的副本来删除Derived中的所有内存!为什么?在std::vector<Base>
中,您只有足够的空间来存储Base
个对象,因为标准容器基于值。
OTOH,如果存储std::vector<Base*>
,则存储的值只是一个指针。 Base *的大小与Derived *相同。存储这些值时没有问题/没有切片。作为一个额外的好处,因为你使用更宽松的句柄,这允许编译器使用vtable来查找你真正想要的正确的多态调用。
所以,如果你想要多态,那么外卖就是使用引用类型。