C ++中的多态性和类型转换

时间:2013-01-18 03:36:19

标签: c++ casting polymorphism

我遇到了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(可能是因为这样的问题)。但是,我无法看到解决问题的方法。我也意识到我可能在多态性方面做错了(必须创建一个无用的虚函数)。任何意见将是有益的。提前谢谢。

3 个答案:

答案 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来查找你真正想要的正确的多态调用。

所以,如果你想要多态,那么外卖就是使用引用类型。