C++编译器对于对象构造的优化
用临时对象生成新对象时, 临时对象就不产生了,直接构造新对象即可
class Test
{
public:
Test(int a = 10) :ma(a)
{
cout << "Test(int)" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
Test(const Test& t) :ma(t.ma)
{
cout << "Test(const Test&)" << endl;
}
Test& operator=(const Test& t)
{
cout << "operator=" << endl;
ma = t.ma;
return *this;
}
private:
int ma;
};
int main()
{
Test t1;
Test t2(t1);
Test t3 = t1;
// Test(20)显式生成临时对象
// C++ 编译器对于对象构造优化:用临时对象生成新对象时,不会产生临时对象,直接构造
Test t4 = Test(20); // 等价于 Test t4(20); 只调用一个构造函数
cout << "----------------------" << endl;
return 0;
}
输出结果
Test(int)
Test(const Test&)
Test(const Test&)
Test(int)
----------------------
~Test()
~Test()
~Test()
~Test()
但是如果是赋值就会创建临时对象
t4 = t2;
t4 = Test(30); // 会调用构造函数生成临时对象,并调用赋值运算符,该行语句执行完会调用析构函数析构临时对象
执行结果
operator=
Test(int)
operator=
~Test()
显式和隐式生成临时对象
// 显示生成临时对象
t4 = Test(30); // 会调用构造函数生成临时对象,并调用赋值运算符
// 隐式生成临时对象
// 下面两行代码都是去寻找带有int参数的Test构造函数来构造临时对象
t4 = (Test)30;
t4 = 30;
执行结果
Test(int)
operator=
~Test()
Test(int)
operator=
~Test()
Test(int)
operator=
~Test()
注意
- &要求左值:
Test* p = &Test(40); // error
- const引用可以接收临时构造的对象
const Test& ref = Test(50);
对象构造析构调用分析案例1
具体看其中注释
class Test
{
public:
// Test() Test(10) Test(10, 10)
Test(int a = 5, int b = 5)
:ma(a), mb(b)
{
cout << "Test(int, int)" << endl;
}
~Test()
{
cout << "~Test()" << endl;
}
Test(const Test& src)
:ma(src.ma), mb(src.mb)
{
cout << "Test(const Test&)" << endl;
}
void operator=(const Test& src)
{
ma = src.ma;
mb = src.mb;
cout << "operator=" << endl;
}
private:
int ma;
int mb;
};
Test t1(10, 10); // 1.Test(int, int)
int main()
{
Test t2(20, 20); // 3.Test(int, int)
Test t3 = t2; // 4.Test(const Test&)
// static Test t4(30, 30); static是在局部对象后面析构
static Test t4 = Test(30, 30); // 5.Test(int, int)
t2 = Test(40, 40); // 6.Test(int, int) operator= ~Test()
// (50, 50) = (Test)50; Test(int)
t2 = (Test)(50, 50); // 7.Test(int,int) operator= ~Test()
t2 = 60; //Test(int) 8.Test(int,int) operator= ~Test()
Test* p1 = new Test(70, 70); // 9. Test(int,int)
Test* p2 = new Test[2]; // 10. Test(int,int) Test(int,int)
//Test* p3 = &Test(80, 80); // 11. Test(int,int) ~Test()
const Test& p4 = Test(90, 90); // 11. Test(int,int),常引用扩展了对象的生命期
delete p1; // 12.~Test()
delete[]p2; // 13. ~Test() ~Test()
}
// 14. p4::~Test()
// 15. t3::~Test()
// 16. t2::~Test()
// 17. t4::~Test()
// 18. t5::~Test()
// 19. t1::~Test()
Test t5(100, 100); // 2.Test(int, int)
不能返回局部的或者临时对象的指针或引用
对象构造和析构调用顺序分析案例2
class Test
{
public:
Test(int data = 10) :ma(data)
{
cout << "Test(int)" << endl;
}
~Test()
{
cout << "~Test(int)" << endl;
}
Test(const Test &t) :ma(t.ma)
{
cout << "Test(const Test &t)" << endl;
}
void operator=(const Test& t)
{
cout << "operator=" << endl;
ma = t.ma;
}
int getData() const { return ma; }
private:
int ma;
};
Test GetObject(Test t)
{
int val = t.getData();
Test tmp(val);
return tmp;
}
int main()
{
Test t1;
Test t2;
t2 = GetObject(t1);
return 0;
}
调用顺序如下图所示
对象的三条优化原则
-
函数参数传递过程中,对象优先按引用传递,不要按值传递:
-
函数返回对象的时候,应该优先返回一个临时对象而不是返回一个定义过的对象
-
接收返回值是对象的函数调用,优先按初始化方式接收,不要按赋值方式接收
将上面的代码函数形参做修改为引用,将会节省一次的拷贝构造和析构的开销,原来t要拷贝构造和析构
Test GetObject(Test &t) // 这里参数被修改为引用
{
int val = t.getData();
Test tmp(val);
return tmp;
}
int main()
{
Test t1;
Test t2;
t2 = GetObject(t1);
return 0;
}
输出结果为
Test(int)
Test(int)
Test(int)
Test(const Test &t)
~Test(int)
operator=
~Test(int)
~Test(int)
~Test(int)
可以发现少了两次的调用
进一步进行优化,在GetObject
中返回临时对象
Test GetObject(Test &t)
{
int val = t.getData();
/*Test tmp(val);
return tmp;*/
// 这里正常情况会使用临时对象拷贝构造一个新对象
// 而c++编译器优化则会导致只调用构造函数生成main函数中GetObject(t1)返回的的临时对象
// 这样就又减少一对拷贝构造和析构
return Test(val);
}
输出结果
Test(int)
Test(int)
Test(int)
operator=
~Test(int)
~Test(int)
~Test(int)
再进行优化将main
函数中 GetObject(t1)
直接赋值给t2:
int main()
{
Test t1;
// 因为这里用临时对象构造同类型对象t2,所以会进行优化,只调用一次构造函数,而没有调用右边临时对象的构造
Test t2 = GetObject(t1);
//t2 = GetObject(t1);
return 0;
}
输出结果又少了一对构造临时对象的构造和析构
Test(int)
Test(int)
~Test(int)
~Test(int)