类默认构造函数可能执行错误的操作
今天看C++ Primer中关于构造函数时,第一次了解到合成的默认构造函数可能执行错误的操作的说法(因为每次定义类时都有写默认构造函数的习惯,所以不知道使用合成的默认构造函数或许会带来问题)。
与内置类型或复合类型的局部对象默认初始化情况相似
书中进一步说明如果定义在块中的内置类型或复合类型(比如数组和指针)的对象被默认初始化,则它们的值将是未定义的。该准则同样适用于默认初始化的内置类型成员。
测试
这里我定义了一个test1类,内部有4个内置类型包括2个int类型和2个char类型,其中两个内置类型提供类内初始值,同时定义了两个string类型,一个有类内初始值而另一个没有。
class test1 {
private:
int int_mem1;
int int_mem2 = 0;
char char_mem1;
char char_mem2 = 'b';
string str_mem1;
string str_mem2 = "str_mem2";
public:
void print_self() {
cout << "int_mem1:" << int_mem1 << endl;
cout << "int_mem2:" << int_mem2 << endl;
cout << "char_mem1:" << char_mem1 << endl;
cout << "char_mem2:" << char_mem2 << endl;
cout << "str_mem1:" << str_mem1 << endl;
cout << "str_mem2:" << str_mem2 << endl;
}
};
作为对比,又定义了test2类,与test2类与test1类的区别仅仅在于后者提供了默认初始化函数(只对没有类内初始值的变量进行初始化)。
class test2 {
private:
int int_mem1;
int int_mem2 = 0;
char char_mem1;
char char_mem2 = 'b';
string str_mem1;
string str_mem2 = "str_mem2";
public:
test2() :int_mem1(0), char_mem1('b'), str_mem1("str_mem1") {};
public:
void print_self() {
cout << "int_mem1:" << int_mem1 << endl;
cout << "int_mem2:" << int_mem2 << endl;
cout << "char_mem1:" << char_mem1 << endl;
cout << "char_mem2:" << char_mem2 << endl;
cout << "str_mem1:" << str_mem1 << endl;
cout << "str_mem2:" << str_mem2 << endl;
}
};
最后又定义了test3类,test3类仅提供了带参数的构造函数。
class test3 {
private:
int int_mem1;
int int_mem2 = 0;
char char_mem1;
char char_mem2 = 'b';
string str_mem1;
string str_mem2 = "";
public:
test3(const int val1, const int val2, const char c1, const char c2, const string &str1, const string& str2) :
int_mem1(val1), int_mem2(val2), char_mem1(c1), char_mem2(c2), str_mem1(str1), str_mem2(str2) {};
public:
void print_self() {
cout << "int_mem1:" << int_mem1 << endl;
cout << "int_mem2:" << int_mem2 << endl;
cout << "char_mem1:" << char_mem1 << endl;
cout << "char_mem2:" << char_mem2 << endl;
cout << "str_mem1:" << str_mem1 << endl;
cout << "str_mem2:" << str_mem2 << endl;
}
};
以下是执行测试的代码,首先我把对test3的定义和接口函数调用部分给屏蔽掉,这一点后面描述。
int main()
{
//test1
test1 t1;
cout << "test1:" << endl;
t1.print_self();
cout << "---------------------------------------" << endl;
//test2
test2 t2;
cout << "test2:" << endl;
t2.print_self();
cout << "---------------------------------------" << endl;
/*cout << "test3_1:" << endl;
test3 t3_1;
t3_1.print_self();
cout << "---------------------------------------" << endl;
cout << "test3_2:" << endl;
test3 t3_2(0, 0, 'a', 'b', "str_mem1", "str_mem2");
t3_2.print_self();
cout << "---------------------------------------" << endl;*/
}
}
以下是test1和test2对象的构造以及接口函数调用的结果:
可以发现test1输出结果中,int_mem1成员的值为随机值,char_mem1成员的值为空字符。我们可以把类内作用域看作一个块,类成员变量时这个块内的一个局部变量,当该类的对象被创建时,没有类内初始值的内置类型或复合类型成员执行默认初始化,其结果和局部变量执行默认初始化情况一致。
如果我们把test3部分的内容取消注释,编译器会报错:
我们没有为test3类提供默认构造函数,是因为只要类显示地声明了任何构造函数,编译器都不会提供合成的默认构造函数。因此,这里我们要为test3声明和定义默认构造函数。
修改后的test3如下所示,此时编译器通过。这里定义的默认构造函数与合成的默认构造函数功能一致。
class test3 {
private:
int int_mem1;
int int_mem2 = 0;
char char_mem1;
char char_mem2 = 'b';
string str_mem1;
string str_mem2 = "";
public:
test3() {};
test3(const int val1, const int val2, const char c1, const char c2, const string &str1, const string& str2) :
int_mem1(val1), int_mem2(val2), char_mem1(c1), char_mem2(c2), str_mem1(str1), str_mem2(str2) {};
public:
void print_self() {
cout << "int_mem1:" << int_mem1 << endl;
cout << "int_mem2:" << int_mem2 << endl;
cout << "char_mem1:" << char_mem1 << endl;
cout << "char_mem2:" << char_mem2 << endl;
cout << "str_mem1:" << str_mem1 << endl;
cout << "str_mem2:" << str_mem2 << endl;
}
};
以下时代码运行结果:
同样的,创建t3_1对象会产生值未定义行为,而t3_2调用带参数的构造函数,不会产生这种行为。
总结
使用合成的默认构造函数创建对象时,类内内置类型或复合类型会产生值未定义的行为,进而对后面的模块带来影响,因此我们定义一个类时,最好定义一个默认构造函数。