对象使用过程中背后调用了哪些方法
实例代码1:(注意t4的构造)
#include<iostream>
using namespace std;
class Test
{
public:
Test(int a = 10) :ma(a) { cout << "Test()" << 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)显示生产临时对象 生存周期:所在的语句
Test t4 = Test(20);//普通构造 等价于 Test t4(20)
return 0;
}
t4属于是c++编译器对于对象构造的优化,用临时对象生成新对象的时候,临时对象就不产生了,直接构造新对象就可以了
但如果是
t4 = Test(30);
那么首先会调用构造函数,构造一个临时对象,然后再通过赋值运算符重载
其次,临时对象在出了语句之后就会立马析构
对于t4 = (Test)30;// 把整数强转成Test类型,显示
编译器会看,Test有没有带int类型的构造函数
还有隐式构造
t4 = 30;
示例代码2:
//p指向的是临时对象
Test* p = &Test(40);
const Test& ref = Test(50);//引用不能变,所以加const
cout << "---------- " << endl;
上面一句是错误的,因为指向的是一个临时对象,出了语句以后就会析构,就没了
而下面可以,因为const起别名了,相当于给临时对象安了名字,生命周期就变了
示例代码3:
#include<iostream>
using namespace std;
class Test
{
public:
Test(int a = 10,int b = 20) :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)
{
cout << "operator=" << endl;
ma = src.ma;
mb = src.mb;
}
private:
int ma;
int mb;
};
Test t1(10, 10);//1 -- Test(int,int)
int a()
{
Test t2(20, 20);//3 -- Test(int, int)
Test t3 = t2;//4 -- Test(const Test&)
static Test t4 = Test(30, 30);//5 -- Test(int,int)
t2 = Test(40, 40);//6 -- Test(int,int) + operator= + ~Test()
t2 = (Test)(50, 50);//7 -- (Test)50 ->Test(50,20) + operator= + ~Test()
t2 = 60;//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 -- 不能用指针指向临时对象
const Test& p4 = Test(90, 90);//12 -- Test(int,int)
delete p1;//13 -- ~Test()释放p1
delete []p2;//14 -- ~Test(),~Test()两次
return 0;
//15 -- p4析构
//16 -- p3析构
//17 -- t3析构
//18 -- t2析构
//19 -- t4析构 --- static是在数据区
//20 -- t5,t1析构 --- 全局区
}
Test t5(100, 100);//2 -- Test()
程序运行以后,首先是全局变量先构造
所以是t1先构造,其次是t5,之后就是按顺序
在static Test t4 = Test(30, 30);
中,存在编译器优化,不会生成临时对象。
在t2 = (Test)(40, 40);
中是将(50,50)强转成Test类型,此时看似有两个40,其实只识别一个,判断是int类型,就去找Test有没有int类型的构造函数。
本质上和下一行改成t2 = 50;
是一样的,是Test(50),所以调用构造函数,其中ma赋值为50,mb默认为20.
在 Test* p1 = new Test(70, 70);
中,使用new的话,需要delete才能删除,不是临时变量,使用不会出语句后就析构
函数调用过程中对象背后调用的方法太多
实例代码:
#include<iostream>
using namespace std;
class Test
{
public:
Test(int a = 10) :ma(a) { cout << "Test(int )" << endl; }
~Test() { cout << "~Test()" << endl; }
Test(const Test& src) :ma(src.ma) { cout << "Test(const Test&)" << endl; }
void operator=(const Test& src)
{
cout << "operator=" << endl;
ma = src.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(t2);
}
(注:不能返回局部的或临时对象的指针或引用)
结果:
第三句中
函数调用,实参=》形参 是初始化?还是赋值呢?
是初始化
在函数return tmp;
时,直接给t2赋值?
错!
先在栈中创建一个临时对象(使用拷贝构造),然后先tmp析构
总结三条对象优化的规则.mp4
原则一: 函数参数传递过程中,对象优先按引用传递,不要按值传递。
可以省一个形参的构造和析构
Test getObject(Test &t)
原则二:函数返回对象的时候,应该优先返回一个临时对象,而不要返回定义过的对象
在用临时对象拷贝构造一个新对象时,如:
Test t = Test(30);
直接构造,不产生临时对象
Test getObject(Test &t)
{
int val = t.getData();
return Test(val);// 3 -- Test(int)
}
其次,我们虽然省去了在函数方法中创建临时对象,
即从:
函数方法中创建的对象=》main栈帧中创建临时对象用于传递=》传递给t2
变成了:
直接main栈帧中创建临时对象=》传递给t2
但是其实还可以优化
直接用构造方法构造t2
代码:
Test getObject(Test &t)
{
int val = t.getData();
return Test(val);// 3 -- Test(int)
}
int main()
{
Test t1;// 1 -- Test(int)
Test t2 = getObject(t2);
}
可以看出减少了很多构造和析构
**原则三:**接受返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按赋值的方式接收
添加带右值引用参数的拷贝构造和赋值函数
左值引用:有内存,有名字
int a = 10;
int &b = a;
右值引用:没名字(临时变量)或者没内存
int &c = 20;//没名字
int &&c = a; //无法将左值绑定到右值引用
int &&d = 20;//可以把一个右值绑定到右值引用上
int &&f = d;//右值引用,本身是这个左值