0

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

4

3 回答 3

3

这向我表明'higher_again'被错误地转换并且它的'b'值以某种方式丢失并且编译器只是将该值设置为0。

因为您将Base类型存储在向量中,slicing因此Derived objects会被分割成“Base”对象。

尝试存储指向基址的指针,派生对象不会被切片,多态性通过指针起作用:

 std::vector<std::shared_ptr<Base>> bases;
于 2013-01-18T03:42:59.287 回答
3

您需要区分值类型引用类型

值类型示例:

Base base;
Derived derived;

引用类型的示例

Derived* p_derived = &derived;
Base& ref_base = *p_derived;

当您声明一个值类型时,它正是您声明的内容,因为内存是为您指定的类型的对象分配的。

当您声明引用类型时,您不会为对象分配更多内存。相反,您有一些预先存在的对象的句柄。当您使用引用类型时,您可以使用句柄所引用的多态性

我喜欢将值类型视为用于声明具体实例的紧密变量:这正是您所说的声明它。

相反,当我想从具体事物中抽象出来时,我喜欢使用引用类型作为松散的句柄。引用类型背后的实际值可以是继承层次结构中的引用类型中的任何内容。因此,如果您有 Base*,则指向的实际对象可能是 Base 或任何从 Base 派生的对象,包括 Derived。

所以回到你的问题。根据定义,所有标准容器的工作都是基于值的。如果您想要多态性,在这种情况下,诀窍是将指向 Base 的指针存储为您的值。例如, std::vector<Base*>, std::unique_ptr<Base>, 等等...而不是std::vector<Base>它本身。

如果您存储std::vector<Base>,则向量中只能有 Base 实例,而不能有其他实例。因此,如果您确实尝试存储 Derived 的实例,这称为切片,因为您最终会通过存储作为 Base 的 Derived 实例的副本来切断 Derived 中的所有内存!为什么?在std::vector<Base>中,您只有足够的空间来存储Base对象,因为 std 容器是基于值的

OTOH,如果您存储std::vector<Base*>,则存储的值只是一个指针。Base* 的大小与 Derived* 相同。存储这些值时没有问题/没有切片。作为一个额外的好处,因为您使用的是更宽松的句柄,这允许编译器使用 vtables 来查找您真正想要的正确的多态调用。

所以要点是,如果您想要多态性,请使用引用类型。

于 2013-01-18T03:55:04.040 回答
2

要按照您的意图使用多态性,您需要将 Base*s 存储到动态分配的内存中。当你有...

std::vector<Base> bases;//Holds a vector of base classes, not nessesarily derived classes but can be

...您正在创建一个向量 - 它实际上是一个打包的 Base 数组 - 其中没有空间用于b派生类添加的数据成员。

找书!;-P

于 2013-01-18T03:41:52.173 回答