目录
通过C++长时间的学习,我们已经学会了好多通过类定义对象的方法,例如:
class A
{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)构造" << endl;
}
A(const A& a1) {
cout << "A(const A& a1)拷贝构造" << endl;
}
~A()
{
cout << "~A()析构" << endl;
}
void Print() {
cout << _a << endl;
}
private:
int _a;
};
class Solution {
public:
Solution(double s= 3.14)
:_st(s)
{
cout << "Solution(double s)构造" << endl;
}
int Sum_Solution(int n) {
//...
return n;
}
~Solution()
{
cout << "~Solution()析构" << endl;
}
private:
double _st;
};
创建对象的几种方式:
int main() {
//创建对象的方式
A a1,a2; //方式1:
A a3(10); //方式2:
A a4(a1); //方式3:
A a5 = 20; //方式4:
方式1:编译器会调用构造函数去创建对象;
方式2:在调用构造函数的同时给予实参直接赋值;
方式3:编译器会调用拷贝构造去创建对象;
方式4:这与方式3等价,且这会让编译器采用隐式类型转换。
A a6();
要特别注意上面这种创建对象方式,该种方式有歧义,会让编译器不知道该调用构造,还是拷贝构造,所以即使运行成功了,编译器也不会生成a6对象!
今天我再来介绍一种定义对象的方法,这种方法被称为匿名对象。
匿名对象的创建格式:
类名();
例:
调用结果:
解析:通过调试出的结果来看,类A,类Solution的匿名对象刚调用完构造函数就调用析构函数了 ,而之前我们创建的对象都是最后会调用析构函数,因此我们得出一个结论:匿名对象的生命周期是在当前行,执行A();调用构造函数,这行完了之后,跳转到下一行时,编译器会调用析构函数。而一般对象的生命周期是一个局部周期,局部结束才会被销毁。这就是一般对象和匿名对象最大的差别!
那么匿名对象到底有什么用呢?
其实你可以理解它是为懒人专用而研发的,~~哈哈哈
例:
一般对象调用函数时:
A a1;
a1.Print();
Solution s1;
int n=s1.Sum_Solution(25);
而匿名对象调用函数时:
A().Print();
int n=Solution().Sum_Solution(70);
一般对象调用函数需要先创建对象,其次才能再调用函数,需要两行;而匿名对象调用函数时一行就可以解决的,
二.编译器对于拷贝对象做出的优化
#include<iostream>
using namespace std;
class A{
public:
A(int a = 0)
:_a(a)
{
cout << "A(int a)构造" << endl;
}
A(const A& aa)
:_a(aa._a)
{
cout << "A(const A& aa)拷贝构造" << endl;
}
A& operator=(const A& aa){
cout << "A& operator=(const A& aa)重载函数" << endl;
if (this != &aa)
{
_a = aa._a;
}
return *this;
}
~A(){
cout << "~A()析构" << endl;
}
private:
int _a;
};
void f1(A aa){}
A f2(){
A aa(10);
return aa;
}
A f3()
{ //注:F2与F3几乎等价
return A(10); //返回匿名对象
}
场景一:
int main() {
A a1; //构造
a1 = 20; //构造+拷贝构造+赋值运算符重载
}
检测:
第一句调用构造函数创建对象,第二句则是将通过常量20创建临时类A对象(调用构造函数),然后a1拷贝复制(调用拷贝构造)临时类A对象值,总计调用:构造+拷贝构造+赋值重载函数。
优化:
//优化场景1:以下这句与上面等价
A a1 = 20;
检测:
优化后只调用了构造函数。
场景二:
A a2(2022);
f1(a2);
//以上两句共调用:构造+拷贝构造
检测:
解析:A a2(2022)是正常的调用构造函数+实参赋值创建对象,因为f1函数形参是类类型参数,这是调用构造函数的情况之一,所以总计调用:构造+拷贝构造
优化:
//优化2:注,以下这句与上面等价
f1(A(2022));
优化后只调用构造函数。
场景三:
f2(); //构造+拷贝构造
解析:f2函数内部因为创建对象,所以调用构造函数,且返回值为类类型对象,这也是拷贝构造函数调用的情况之一,所以f2函数总计调用:构造+拷贝构造
A ret;
ret=f2(); //总结:构造+拷贝构造+拷贝构造
检测:
总计调用:构造+构造+拷贝构造+赋值重载函数
优化:
//优化3:下面这句与上面两句等价
A ret = f2();
优化后总计调用:构造+拷贝构造
场景四:
A ret = f2(); //构造+拷贝构造
总计调用:构造+拷贝构造
优化:
//优化4:极致优化
A ret2 = f3();
检测:
优化后总计调用一次构造函数。
整体优化总结:
能一句能搞定的事情不要分开去编写,不要做中间商!中间商不仅赚不了差价,而且还会赔本!!!