目录
拷贝对象时编译器做出的一些优化
参数传递优化
返回值优化
拷贝对象时编译器做出的一些优化
📌
下面的优化结果由编译器决定,不同的编译器优化结果可能不同,视具体情况而定
参数传递优化
- 在前面的
explicit
关键字部分提到过编译器会对在单行的构造+拷贝构造优化为构造
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){}void print(){cout << _num << endl;}
};int main()
{test t = 1;t.print();return 0;
}
输出结果:
1
- 在给函数形参传递实参时,如果直接传递已经创建的对象时,编译器不会对其进行优化
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}~test(){cout << "析构函数" << endl;}
};void func1(const test t)
{cout << "func1" << endl;
}int main()
{test t1(1);func1(t1);return 0;
}
输出结果:
构造函数
拷贝构造函数
func1
析构函数
析构函数
在上面的代码中,首先test
类创建了一个对象为t1
,此时调用构造函数,当t1
作为函数实参传递给func1
函数,此时会调用拷贝构造函数将t1
对象拷贝给形参t
,接着进入func1
函数栈帧空间执行func1
函数体的语句,当func1
函数结束执行后调用析构函数销毁形式参数对象t
,最后调用析构函数销毁局部对象t1
对上面的代码进行改进,直接传递整型1给func1
函数,如下面代码
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}~test(){cout << "析构函数" << endl;}
};void func1(const test t)
{cout << "func1" << endl;
}int main()
{func1(1);return 0;
}
输出结果:
构造函数
func1
析构函数
在上面的代码中,直接将1作为对象传递给自定义类型的形参t
时,常规的步骤为:调用构造函数用整型1初始化一个临时对象,再调用拷贝构造函数将临时对象中的内容拷贝给形参对象,但是此处编译器会对其进行优化为直接调用构造函数,用整型1初始化形参对象t
同理,使用匿名对象作为实际参数传递给自定义类型的形参时,编译器也会有所优化
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}~test(){cout << "析构函数" << endl;}
};void func1(const test t)
{cout << "func1" << endl;
}int main()
{func1(test(2));return 0;
}
输出结果:
构造函数
func1
析构函数
在上面的代码中,使用整型2创建了一个匿名对象,常规步骤为:调用构造函数使用整型2创建匿名对象,接着调用拷贝构造函数将匿名对象中的内容拷贝给形式参数,但是编译器优化为直接使用整型2为形式参数初始化
但是如果函数的形式参数为引用时,则不会有任何优化,直接调用构造函数进行初始化对象再由自定义类型的引用形参接收实参对象的地址
📌
注意:使用引用传参时一定要在形式参数处加const
修饰
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}~test(){cout << "析构函数" << endl;}
};void func1(const test& t)
{cout << "func1" << endl;
}int main()
{test t1(1);func1(t1);cout << endl;func1(1);cout << endl;func1(test(1));return 0;
}
输出结果:
构造函数
func1构造函数
func1
析构函数构造函数
func1
析构函数
析构函数
返回值优化
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}test& operator=(const test& t){cout << "赋值运算符重载函数" << endl;if (this != &t){_num = t._num;}return *this;}~test(){cout << "析构函数" << endl;}
};test func()
{cout << "func" << endl;test t(1);return t;
}int main()
{func();return 0;
}
- 当调用的函数有返回对象时,使用该对象初始化对象
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test(int num):_num(num){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}test& operator=(const test& t){cout << "赋值运算符重载函数" << endl;if (this != &t){_num = t._num;}return *this;}~test(){cout << "析构函数" << endl;}
};test func()
{cout << "func" << endl;test t(1);return t;
}int main()
{test t1 = func();return 0;
}
在上面的代码中,使用func
函数的返回值初始化t1
对象,常规的过程为:调用拷贝构造函数将func
函数的返回值放入一个自定义类型的临时变量中,再通过拷贝构造函数将临时变量中的内容拷贝给t1
对象,但是这里编译器会优化为调用一个构造函数将func
的返回值作为初始化值直接初始化t1
对象
但是如果将两个步骤分开,如下面的代码
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test():_num(){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}test& operator=(const test& t){cout << "赋值运算符重载函数" << endl;if (this != &t){_num = t._num;}return *this;}~test(){cout << "析构函数" << endl;}
};test func()
{cout << "func" << endl;test t;return t;
}int main()
{test t1;t1 = func();return 0;
}
在上面的代码中,因为t1
对象需要完成实例化,所以会调用构造函数,接着执行t1 = func()
语句,因为赋值运算符有从右往左的结合性,所以先执行func
函数,在func
函数中会再次调用构造函数创建一个对象,(注意中间有一个过程为:调用拷贝构造将返回对象拷贝到临时对象中,再调用析构函数销毁局部对象t
)此时执行赋值语句,此时调用赋值运算符重载函数,将t
对象的内容给t1
对象
- 当返回的是匿名对象,使用该匿名对象初始化对象
#include <iostream>
using namespace std;class test
{
private:int _num;
public:test():_num(){cout << "构造函数" << endl;}void print(){cout << _num << endl;}test(const test& t){_num = t._num;cout << "拷贝构造函数" << endl;}test& operator=(const test& t){cout << "赋值运算符重载函数" << endl;if (this != &t){_num = t._num;}return *this;}~test(){cout << "析构函数" << endl;}
};test func()
{cout << "func" << endl;return test();
}int main()
{test t1 = func();return 0;
}
输出结果:
func
构造函数
析构函数
在上面的代码中,先执行func
函数,常规步骤为:执行test
类中的构造函数创建一个匿名对象,接着调用拷贝构造将匿名对象拷贝到临时对象中返回,接着调用拷贝构造将返回值拷贝给t1
对象,但是此处编译器会优化为直接用返回的匿名对象的内容作为初始值初始化对象t1
总结:
- 为了编译器更好得优化,在传参数时,可以考虑使用引用变量作为参数
- 当使用到返回值时,如果能用引用就使用引用,不能使用引用需要返回值时,可以考虑返回匿名对象