我有一个类层次结构,在派生类中,声明了Dog类的对象。然后,该对象被提供给初始化列表中的基类。
由于在调用基类构造函数之前未构造Dog类对象,因此该对象不应该有任何用处。但是,我看到,该对象可用于以下代码:
#include <iostream>
using namespace std;
class Dog {
public:
Dog() {cout << "\n In Dog";}
void dfoo(){cout << "\n In dfoo";};
};
class Foo {
public:
Foo(Dog d){cout << "\n In Foo";
d.dfoo();
}
};
class Bar : public Foo {
Dog d;
public:
Bar() : Foo(d) {
cout << "\n In Bar";
}
};
int main() {
cout << "\nHello";
Bar b;
}
输出是:
Hello
In Foo
In dfoo
In Dog
In Bar
输出显示甚至在构造Dog对象之前调用了dfoo()。这是怎么回事?不应该&#39; d&#39;是垃圾,因为在调用Base构造函数之前没有初始化它?
答案 0 :(得分:2)
实际上,你的对象d包含垃圾。
当你传递d(按值)时,你隐式调用Dog的复制构造函数,并通过在Dog类型的垃圾对象上执行复制构造函数来创建一个类型为Dog的新对象。
所以你的问题是:为什么Dog的隐式复制构造函数不关心源对象从未构造过。
由于该对象甚至没有任何内容,因此编译器为了使其复制构造函数关心其源代码而浪费了大量精力。
我不确切知道有多少是未定义的行为(恰好因为实现是理智的)而不是你可能允许用未构造的对象做的事情。
考虑这个版本的狗:
class Dog {
public:
Dog() {cout << "\n In Dog";}
Dog(Dog const& source) { cout << "\n Trying to copy a Dog\n"; source.dfoo();}
virtual void dfoo() const {cout << "\n In dfoo";};
};
现在你对它的使用会以你预期的方式崩溃(因为复制构造函数的源代码从未构造过。)
您对Dog的复制构造函数的调用隐藏在将值传递给Foo的构造函数的操作中。注意它发生在实际到达Foo的构造函数之前。 Dog的复制结构(及其所有好处和/或问题)在有或没有Dog的复制构造函数的明确定义的情况下发生。如果您没有定义它,编译器会为您定义它。
答案 1 :(得分:1)
理论上,您的代码会受到未定义的行为的影响。
您正在将未初始化的d
传递给Foo::Foo()
。您没有看到任何问题,因为Dog
没有任何成员变量,Dog::dfoo()
不依赖于任何成员变量的值。
如果您将成员变量添加到Dog
并在Dog::dfoo()
中使用该成员变量,您可能会看到奇怪的输出:
class Dog {
int v1;
public:
Dog() {cout << "\n In Dog";}
void dfoo(){cout << "\n In dfoo, v1: " << v1 << endl;};
};
答案 2 :(得分:0)
如果您在clang中编译代码,您将收到警告,但您将获得相同的输出。
clang++ -std=c++11 -Wall -pedantic -pthread main.cpp && ./a.out
main.cpp:21:21:警告:字段“d”在此处使用时未初始化 [-Wuninitialized] Bar():Foo(d){
那发生了什么?
您正在将未初始化的Dog对象作为参数传递给Foo的构造函数。由于您的构造函数接受Dog参数作为值,它将尝试调用类Dog的复制构造函数来创建Dog的对象,即使它未初始化。并且系统成功使用Foo构造函数内的复制构造函数创建了一个本地Dog对象,并调用了函数dfoo()。
答案 3 :(得分:0)
是垃圾。
不确定您对此有何明显的症状。例如,Dog
没有数据成员。