关于c ++:vector.push_back(B)和vector.push_back(new A((* B))),为什么不一样呢?

vector.push_back(B) and vector.push_back(new A((*B))), why not act as the same?

本问题已经有最佳答案,请猛点这里访问。

我有这两门课:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class A
{
public:
    A();
    virtual ~A();

    virtual void TellMyName();
};

class B : public A
{
private:
    std::string szName;
public:
    B();
    ~B();

    void TellMyName();

    void SetName(std::string val){ szName = val; }
};

这是我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
void main()
{
    std::vector<A*> List_A;

    B* B_Instance = new B();
    B_Instance->SetName("B");

    List_A.push_back(B_Instance); // Way 1
    List_A.push_back(new A((*B_Instance))); // Way 2

    List_A[0]->TellMyName();
    List_A[1]->TellMyName();
}

TellMyName()只是提示一个消息框。如果我使用"方式1",它没有问题,但是如果我使用"方式2",它将提示消息没有文本,这意味着B类的所有成员都是空的,就像从来没有被任何东西填满一样。我用std::shared_ptr解决了这个问题,但是有没有其他方法不使用智能指针,因为我必须在一个大项目中实现这个方法,并且会有很多变化和失败。顺便问一下,"2号路"的原因是什么?


1
List_A.push_back(new A((*B_Instance))); // Way 2

这条线并没有做你认为的事情。

通过调用C++生成的默认复制构造函数,您正在堆上创建类"A"的新实例。该复制构造函数使用B的实例(视为A的实例)并逐字段复制,在本例中,该实例不起任何作用。

您实际上是在创建的空实例并将其推到列表中。

正如许多人所提到的,这个正式术语称为对象切片。


你应该打电话来

1
List_A.push_back(new B((*B_Instance)));

由于B继承A,也可以添加到列表中,但由于B的复制构造函数也会复制B中的所有字段,所以它可以工作。

调用A的复制构造函数不会复制B中的字段,因为A不"知道"B的字段。


1
2
3
std::vector<A*> List_A;
// ...
List_A.push_back(new A((*B_Instance))); // Way 2

你正在遭受一种物体切片的痛苦,有点。

对象切片发生在用派生类型的值实例化(或分配)一个基类,并丢失(或"切片")派生类唯一的所有内容时。

上面,您正在实例化一个new A,而不是一个new B。但是由于B是从A派生出来的,所以您可以简单地实例化new B并将返回的指针强制转换为A*

1
List_A.push_back(new B(*B_Instance)); // Way 2

尽管如此,我还是强烈建议您考虑重构代码以使用智能指针。


that means all members of the B class are empty like they're never being filled with anything

不,这并不意味着它们是空的。要想空无一人,首先他们必须在那里。

List_A[1]->TellMyName();执行动态调度,最后调用A::TellMyName,因为class A是对象的实际类型。B类的成员甚至不存在。

你碰到了切片问题。