C++中临时对象的产生与优化

来源:互联网 发布:mac mini win10 编辑:程序博客网 时间:2024/06/10 16:30

C++中临时对象的产生与优化

本文主要介绍c++中临时对象产生的几种情况,同时介绍避免的策略。由于在C++中对象的创建和消除会调用该对象对应的构造和析构函数,是一个相对比较耗时的操作,从程序效率角度来说我们应该了解并消除临时对象;又如果类中有指针,更应该了解临时对象,避免指针悬挂和内存泄露等问题。

一、拷贝构造函数和赋值操作符

在深入讨论临时对象之前来看一下拷贝构造函数和赋值操作符,为什么呢?一般而言,临时对象的产生一般对象的赋值或者拷贝,这时就需要了解拷贝操作和赋值操作。

1.        拷贝操作:对象在定义时进行的初始化操作,此时调用的是拷贝构造函数完成。

2.        赋值操作:对象定以后的赋值操作,此时调用的是赋值操作符完成。

那么什么时候进行拷贝操作,什么时候进行赋值操作呢?如定义所示一个变量在第一次定义时进行的操作一般为拷贝操作,例如 A a; A a2 = 1; A a3(a2); A a4 = a3; 进行赋值操作的前提是变量左值已经定义,例如A a; A b; b=a;

// 验证拷贝构造函数 和 赋值操作符#include <iostream> class TestObject { public:  TestObject() {    std::cout << "TestObject() is called:"      << con_call_num_++<< std::endl;  }  TestObject(int _x) {    x_ = _x;    std::cout << "TestObject(int) is called:"      << con_call_num_++<< std::endl;  }  TestObject (TestObject & to) {    x_ = to.x_;    std::cout << "TestObject(TestObject) is called:"      << ccon_call_num_++ << std::endl;  }  TestObject (const TestObject& to) {    x_ = to.x_;    std::cout << "TestObject(const TestObject) is called:"      << ccon_call_num_++ << std::endl;  }  TestObject &operator =(const TestObject& to) {    if (&to == this)      return *this;    x_ = to.x_;    std::cout << "operator =(const) is called:"      << equal_call_num_++ << std::endl;    return *this;  }  TestObject &operator =(TestObject& to) {    if (&to == this)      return *this;    x_ = to.x_;    std::cout << "operator =() is called:"      << equal_call_num_++ << std::endl;    return *this;  }  ~TestObject() {    std::cout << "~TestObject() is called:"      << des_call_num_++ << std::endl;  } private:  int x_;  static int con_call_num_;  static int ccon_call_num_;  static int equal_call_num_;  static int des_call_num_;}; int TestObject::con_call_num_ = 0;int TestObject::ccon_call_num_ = 0;int TestObject::equal_call_num_ = 0;int TestObject::des_call_num_ = 0; int main() {  // copy  TestObject to_1;  TestObject to_2(1);  TestObject to_3 = 1;  TestObject to_4 = to_2;  TestObject to_5(to_4);  // assign  to_1 = to_2;  return 0;}

其对应的输出为:

TestObject() is called:0TestObject(int) is called:1TestObject(int) is called:2TestObject(TestObject) is called:0TestObject(TestObject) is called:1operator =() is called:0~TestObject() is called:0~TestObject() is called:1~TestObject() is called:2~TestObject() is called:3~TestObject() is called:4

 从输出中可以看出带有”=“进行的不一定是赋值操作符,也有可能是拷贝构造函数。另外解决对象的深浅拷贝问题,要同时注意重载拷贝构造函数和赋值操作符。记住默认的拷贝和赋值操作均为浅拷贝。

二、临时对象的产生与优化

总体来说,临时对象产生主要有以下三种情况,

1.        以值的方式给函数传参

2.        隐式类型转换

3.        函数返回一个对象时

2.1 以值的方式给函数传参

临时对象产生点在于给函数参数传值时,先生成该对象的临时对象,调用拷贝构造函数进行拷贝。消除方法是尽量使用引用或者传指针的方式。

void Fun1(TestObject to) {  std::cout << "Fun1(TestObject) is called" << std::endl;}void Fun1_1(TestObject & to) {  std::cout << "Fun1_1(TestObject&) is called" << std::endl;}int main() {  TestObject to_2;                                                                                std::cout << "-----Test type 1-------" << std::endl;  Fun1(to_2);  Fun1_1(to_2);  return 0;}

产生输出如下:

TestObject() is called:0-----Test type 1-------TestObject(TestObject) is called:0Fun1(TestObject) is called~TestObject() is called:0Fun1_1(TestObject&) is called~TestObject() is called:1

说明:在函数传入参数时调用的是拷贝构造函数,并且临时对象的释放在一个完整语句结束时释放,即第一个分号之后。

2.2 隐式类型转换

临时对象的产生点在于发送隐式类型转换时,先进行类型转换,在进行赋值。消除方法是尽量定义即进行初始化。

int main() {  TestObject to_2;  std::cout << "-----Test type 1-------" << std::endl;  Fun1(to_2);  Fun1_1(to_2);  std::cout << "-----Test type 2-------" << std::endl;  TestObject to_3;  to_3 = 1;  TestObject to_4 = 1;                                                                            return 0;}

输出结果为:

-----Test type 1-------TestObject(TestObject) is called:0Fun1(TestObject) is called~TestObject() is called:0Fun1_1(TestObject&) is called-----Test type 2-------TestObject() is called:1TestObject(int) is called:2operator =(const) is called:0~TestObject() is called:1TestObject(int) is called:3~TestObject() is called:2~TestObject() is called:3~TestObject() is called:4

在进行隐式类型转换进行的操作为:先调用构造函数生成临时对象,在进行赋值操作。

2.3函数返回一个对象时

临时对象产生点在于返回对象时先生成一个临时对象,在进行拷贝操作,消除方法是尽量不要返回对象,如果需要返回,可以考虑进行传入指针的方式进行赋值。

TestObject Fun2_1() {  std::cout << "Fun2_1() is called" << std::endl;  return 1;}TestObject Fun2_2() {  std::cout << "Fun2_2() is called" << std::endl;  TestObject to;  to.set_x(1);  return to;}int main() {  /*  TestObject to_2;  std::cout << "-----Test type 1-------" << std::endl;  Fun1(to_2);  Fun1_1(to_2);  std::cout << "-----Test type 2-------" << std::endl;  TestObject to_3;  to_3 = 1;  TestObject to_4 = 1;  */  std::cout << "-----Test type 3-------" << std::endl;  Fun2_1();  Fun2_2();  return 0;} 

输出结果为:

-----Test type 3-------Fun2_1() is calledTestObject(int) is called:0~TestObject() is called:0Fun2_2() is calledTestObject() is called:1~TestObject() is called:1

可以看到没有调用拷贝构造函数,主要因为现在编译器比较智能了能够对类似情况进行优化。

C++中的返回值优化(returnvalue optimization)

返回值优化(Return ValueOptimization,简称RVO),是这么一种优化机制:当函数需要返回一个对象的时候,如果自己创建一个临时对象用户返回,那么这个临时对象会消耗一个构造函数(Constructor)的调用、一个复制构造函数的调用(Copy Constructor)以及一个析构函数(Destructor)的调用的代价。

三、总结

理论结合实践才是真理。由于编译器的不同,产生结果可能会不同,在实际开发中遇到不确定的情况,多写测试用例。

原创粉丝点击