我编写了以下代码(这些类是用单独的.h文件编写的):
class A
{
public:
A(){
cout << "This is the constructor of A!" << endl;
foo();
}
virtual ~A(){
cout << "Destroyed A type" << endl;
}
virtual void foo();
};
void A::foo()
{
cout << "foo()::A" << endl;
}
class B: public A
{
public:
B(){}
~B(){
cout << "Destroyed B type" << endl;
}
void foo();
};
void B::foo()
{
cout << "foo()::B" << endl;
}
以下主要功能:
int main()
{
A b = B();
A *bp = &b;
A &ra = b;
bp->foo();
ra.foo();
return 0;
}
当我运行它时,我得到以下输出:
This is the constructor of A!
foo()::A
Destroyed B type
Destroyed A type
foo()::A
foo()::A
Destroyed A type
我的问题是 - 为什么B()会被立即销毁?不是假设指向它,是一个B型对象?如何将b初始化为新B?也许我对Java感到困惑,我们会说b的静态类型是A而动态类型是B.这不是同样的情况吗?我很想了解这段代码背后的机制。
答案 0 :(得分:3)
为什么B()会被立即销毁?
在这一行:
A b = B();
你做了几件事。以下是步骤:
您创建了一个B类型的临时值,它调用默认的基类构造函数,该构造函数输出:
This is the constructor of A!
foo()::A
然后调用B
之后,创建类型为A
的对象,并调用编译器生成的带有A
参数(A(const A& a)
)的复制构造函数,并对临时值进行切片。 (顺便说一句,你应该遵循the rule of three)
然后,临时值被销毁,因为它是B
类型的值,它首先调用B析构函数,然后是析构函数,这就是你得到它的原因:
Destroyed B type
Destroyed A type
不想指向它,是B型对象吗?
不,一点也不。 b
不是指向A
对象的指针,它是A
值类型。
如何将b初始化为新B
它是一种值类型,因此您无法成为&#34; B类型&#34;,因为对象切片。如果您不知道什么是对象切片,您应该阅读:What is object slicing ?
也许我对Java感到困惑
是
我们会说b的静态类型是A而动态类型是B.这不是同样的情况吗?
在java中,它是。在C ++中,如果你想做同样的事情,你需要这样做:
A* b = new B();
这里,b的静态类型是A,但它指向B对象。当然,在使用它之后不要忘记释放内存,如下所示:
delete b;
因为C ++不会为你管理内存。或者更好:使用智能指针。
我还有另一个问题:这一行会发生什么:&#34; A&amp; ra = b;&#34;?
创建名为A
的{{1}}引用类型,并引用对象ra
ra究竟是什么?
b
是A. See there
我知道它的类型是A但幕后会发生什么?例如,当我尝试&#34; A a = b;&#34;一个对象被创建了。 ra不是这种情况,但ra是一个A对象(例如,如果我们将使foo()不是虚拟的,它将选择A的foo)。你知道解释一下吗?
不,不,不。 ra
类型是ra
的引用类型,而不是A
值类型
引用类型基本上是别名,是值的另一个名称。如this tutorial中所述:
C ++引用允许您为变量创建第二个名称,您可以使用该名称来读取或修改存储在该变量中的原始数据。虽然这一开始可能听起来不太吸引人,但这意味着当您声明一个引用并为其赋值变量时,它将允许您将引用视为原始变量,以便访问和修改该值原始变量 - 即使第二个名称(引用)位于不同的范围内。这意味着,例如,如果你使函数参数引用,你将有效地改变传递给函数的原始数据。
在您的情况下,A
是引用对象ra
的引用类型,因此b
是对象ra
的另一个名称。因此,当您执行b
时,与ra.foo()
完全相同,因为b.foo()
引用了对象ra
,引用了相同的数据存储器中。
在幕后,它通常使用指针(see this SO answer)实现。也许在你的情况下,它可以简单地删除(我不知道,我试图查看汇编文件,但它很难阅读)
答案 1 :(得分:2)
B()
创建类型为B
的对象,A b = B()
然后将创建的对象复制到变量b
中。此时存在2个对象,一个类型为B
的临时对象在复制后被删除,另一个对象类型为A
,因为在复制到{{{{}}}之后,您的B
对象被切片1}}。
答案 2 :(得分:1)
编译此源时,编译器会自动为A
和B
类生成复制构造函数运算符。执行A b = B();
时,会发生以下情况:
B
的临时实例由构造函数B::B()
创建。A
的自动生成的复制构造函数。这将从步骤1生成的临时实例b
构造实例B
。B::~B()
的地方)