思考:
构造函数是一个特殊的函数
- 是否可以直接调用?
- 是否可以在构造函数中调用构造函数?
- 直接调用构造函数的行为是什么?
答:
直接调用构造函数将产生一个临时对象
临时对象的生命周期只有一条语句的时间
临时对象的作用域只在一条语句中
临时对象是C++中值得警惕的灰色地带
#include <stdio.h>
class Test {
int mi;
public:
Test(int i)
{
mi = i;
}
Test()
{
Test(0);
// 调用Test(int i),得到了一个临时对象,生命周期只有这句话,过了之后临时对象就被析构,临时对象没有名字,作用域也只有这句话,过了之后,不能再被访问。这里的临时对象几乎就没有作用。这个0根本就没有设置到mi上
}
// 等价于
Test(){
};
void print() {
printf("mi = %d
", mi);
}
};
int main()
{
Test t;
t.print();
return 0;
}
// 解决办法
// 提供一个私有的init函数,做初始设置
class Test {
int mi;
void init(int i)
{
mi = i;
}
public:
Test(int i) {
printf("Test(int i)
");
init(i);
}
Test(){
printf("Test()
");
init(0); // 调用的普通私有函数,不会产生临时对象
};
void print() {
printf("mi = %d
", mi);
}
~Test()
{
printf("~Test()
");
}
};
int main()
{
printf("main begin
");
Test(); // 直接调用构造函数,会产生一个临时对象
Test(10); // 产生临时对象
printf("main end
")
return 0;
}
/*结果
main begin
Test()
~Test()
Test(int i)
~Test()
*/
int main()
{
printf("main begin
");
Test().print(); // Test()结果是一个临时对象,是一个合法的对象,然后操作它的成员函数
Test(10); // 产生临时对象
printf("main end
")
return 0;
}
/*结果
main begin
Test()
mi = 0 // 临时对象的打印
~Test()
Test(int i)
mi = 10 // 临时对象的打印
~Test()
*/
编译器的行为:现代C++编译器在不影响最终执行结果的前提下,会尽力减少临时对象的产生!!!
#include <stdio.h>
class Test
{
int mi;
public:
Test(int i)
{
printf("Test(int i) : %d
", i);
mi = i;
}
Test(const Test& t)
{
printf("Test(const Test& t) : %d
", t.mi);
mi = t.mi;
}
Test()
{
printf("Test()
");
mi = 0;
}
int print()
{
printf("mi = %d
", mi);
}
~Test()
{
printf("~Test()
");
}
};
Test func()
{
return Test(20); // 应该是返回一个临时对象
}
int main()
{
Test t(10);
Test t = Test(10); // 1、生成一个临时对象;2、用临时对象初始化t
// ==> 调用拷贝构造函数
// 但是结果并没有打印拷贝构造函数,说明编译器没有生成临时变量
// 原因是现代编译器都会尽量减少临时变量的产生,
// 编译器将这句话等价成了 Test t(10)
// 临时变量的产生会带来性能上的问题
t.print();
Test tt = func();
tt.print(); // 会打印出20
/*
理论上应该是要调用拷贝构造函数,但是没有
编译器将这句话等价成了 ==> Test tt = Test(20) ==> Test tt = 20;
*/
return 0;
}
小结
直接调用构造函数将产生一个临时对象
临时对象是性能的瓶颈,也是BUG的来源之一
现代C++编译器会尽力避开临时对象
实际工程开发中需要认为地避开临时对象