示例1
#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 &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() {
cout << "1.Test t1;" << endl;
Test t1;
cout << "\n2.Test t2(t1)" << endl;
Test t2(t1);//拷贝构造函数
cout << "\n3.Test t3 = t1;" << endl;
Test t3 = t1;//拷贝构造函数
cout << "\n4.Test t4 = Test(20);" << endl;
/*
C++编译器对于对象构造的优化:用临时对象生成新对象的时候,临时对象
就不产生了,直接构造新对象就可以了
*/
Test t4 = Test(20);//和Test t4(20);没有区别的! 仅调用一次默认构造函数
cout << "\n5. t4 = t2;" << endl;
t4 = t2;调用赋值函数,因为t4原本已存在
cout << "\n6 t4 = Test(20);" << endl;
t4 = Test(20);// 显式生成临时对象,临时对象生成后,给t4赋值,出语句后,临时对象析构 (默认构造函数,赋值运算符,析构函数)
cout << "\n7.t4 = (Test)30;" << endl;
/************************************************************************/
/* 把其他类型转成类类型的时候,编译器就看这个类类型
有没有合适的构造函数 把整型转成Test,就看这个类类型有没有
带int类型参数的构造函数 ,有,就可以显式生成临时对象,然后
赋值给t4 出语句后,临时对象析构
*/
/************************************************************************/
t4 = (Test)30;//把30强转成Test类型int->Test(int) 同t4 = Test(20);
cout << "\n8.t4 =30;" << endl;
t4 = 40;//隐式生成临时对象,然后赋值给t4,出语句后,临时对象析构 同7
cout << "\n9 Test *p = &Test(40);" << endl;
Test *p = &Test(40);//指针指向临时对象,这个临时对象肯定是要生成的
//然后p指向这个临时对象的地址
//出语句后,临时对象析构
//此时p指向的是一个已经析构的临时对象,p相当于野指针了
cout << "\n10 const Test &ref = Test(50);" << endl;
const Test &ref = Test(50);//引用一个临时对象,这个临时对象也是要生成的
//出语句后,临时对象不析构,因为引用相当于是别名,临时对象出语句析构是因为没有名字
//用引用变量引用临时对象是安全的,临时对象就是有名字了,临时对象的生存周期就变成引用变量的
//生存周期了。引用变量是这个函数的局部变量,return完,这个临时对象才析构
cout << "\nfinish" << endl;
return 0;
}
示例2
#include <iostream>
using namespace std;
class Test
{
public:
//因为a,b有默认值所以构造有3种方式:
//Test() Test(10) Test(10, 10)
Test(int a = 5, int b = 5)//构造函数
:ma(a), mb(b)
{
cout << "Test(int, int):" << ma << "," << mb << endl;
}
~Test()//析构函数
{
cout << "~Test():" << ma << "," << mb << 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);//在mian函数之前构造,在main函数结束后释放,存储在.data段
int main() {
cout << "\n---------------------------main()" << endl;
cout << "\n 1.Test t2(20,20); " << endl;
Test t2(0,0);//默认构造函数
cout << "\n 2.Test t3 = t2" << endl;
Test t3 = t2;//拷贝构造函数
cout << "\n 3.Test t3 = t2" << endl;
static Test t4 = Test(30, 30);//第一次运行到它才初始化
cout << "\n 4 t2 = (Test)(50, 50);" << endl;
//(50,50)是逗号表达式,(表达式1,表达式2,表达式n)
//(50,50)的最后的结果是最后一个表达式n的结果 50
//(50, 50) = (Test)50;
t2 = (Test)(50, 50);//Test(int,int) operator= 出语句调用~Test()
cout << "\n 5 Test* p1 = new Test(70, 70);" << endl;
Test* p1 = new Test(70, 70);// Test(int,int) 要调用delete才析构对象
cout << "\n 6 Test* p2 = new Test[2];" << endl;
Test* p2 = new Test[2];// Test(int,int) Test(int,int) 要调用delete才析构对象
cout << "\n 7 Test* p3 = &Test(80, 80);" << endl;
Test* p3 = &Test(80, 80);// Test(int,int) 出语句调用~Test()
cout << "\n 8 const Test& p4 = Test(90, 90);" << endl;
const Test& p4 = Test(90, 90);//Test(int,int)
delete p1;//~Test()
delete[]p2;// ~Test() ~Test()
cout << "\n---------------------------finish" << endl;
return 0;
}
Test t5(100, 100);//Test(int, int)
示例3
#include <iostream>
using namespace std;
class Test
{
public:
//有默认值,可以有2种构造方式:Test() Test(20)
Test(int data = 10) :ma(data)
{
cout << "Test(int):" << ma << endl;
}
~Test()
{
cout << "~Test()" << ma << endl;
}
Test(const Test &t) :ma(t.ma)
{
cout << "Test(const Test&)" << endl;
}
void operator=(const Test &t)
{
cout << "operator=" << endl;
ma = t.ma;
}
int getData()const { return ma; }
private:
int ma;
};
Test GetObject(Test t)
{
cout << "\n----------------GetObject1 \n";
int val = t.getData();
Test tmp(val);
cout << "\n----------------GetObject2 \n";
return tmp;
}
int main()
{
Test t1;//1、调用带整型参数的构造函数
Test t2;//2、调用带整型参数的构造函数
/*
(1)实参传递给形参 :调用Test(const Test&) 拿t1拷贝构造形参t
(2) 调用Test(int)的构造,构造tmp对象
(3) return tmp;tmp和t2是两个不同函数栈帧上的对象,是不能直接进行赋值的 GetObject函数完成调用时
tmp对象作为局部对象就析构了 ,为了把返回值带出来, 在return tmp;这里,首先要在main函数栈帧
上构建一个临时对象,目的就是把tmp对象带出来
(4) 调用 Test(const Test&),tmp拷贝构造main函数栈帧上的临时对象
(5) 出 GetObject作用域,tmp析构
(6) 形参t对象析构
(7) operator =,把main函数刚才构建的临时对象赋值给t2,临时对象没名字,出了语句就要析构
(8) 把main函数刚才构建的临时对象析构
(9) main函数结束,t2析构
(10) t1析构
*/
cout << "\n----------------main GetObject1 \n";
t2 = GetObject(t1);//函数调用
cout << "\n----------------main GetObject2 \n";
return 0;
}
总结三条对象优化的规则
- 函数参数传递过程中,对象优先按引用传递,这样可以省去一个形参t的拷贝构造调用,形参没有构建新的对象,出作用域也不用析构了,所以不要按值传!
- 函数返回对象的时候,应该优先返回一个临时对象,而不要返回一个定义过的对象
- 接收返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按赋值的方式接收
优化1:没有t1的拷贝构造,形参t没有新的对象,出作用域也不用析构。
省去了形参t的拷贝构造和形参t的析构
优化2:函数返回对象的时候,应该优先返回一个临时对象,而不要返回一个定义过的对象
3、接收返回值是对象的函数调用的时候,优先按初始化的方式接收,不要按赋值的方式接收
函数返回值临时对象给t2初始化!用这个临时对象拷贝构造同类型的新对象t2。C++编译器会进行优化,这个main函数栈帧上的临时对象都不产生了,直接构造t2对象。也就是return Test(val);直接构造t2对象了
Test t2= GetObject(t1);在汇编上,除了把t1的地址传进去,还把t2的地址也传进去了,也压到函数栈帧上,所以return Test(val);就可以取到t2的地址,就知道在哪块内存上构造一个名为t2的对象。
#include <iostream>
using namespace std;
class Test
{
public:
//有默认值,可以有2种构造方式:Test() Test(20)
Test(int data = 10) :ma(data)
{
cout << "Test(int):" << ma << endl;
}
~Test()
{
cout << "~Test()" << ma << endl;
}
Test(const Test &t) :ma(t.ma)
{
cout << "Test(const Test&)" << endl;
}
void operator=(const Test &t)
{
cout << "operator=" << endl;
ma = t.ma;
}
int getData()const { return ma; }
private:
int ma;
};
Test GetObject(const Test &t)
{
cout << "\n----------------GetObject1 \n";
int val = t.getData();
// Test tmp(val);
cout << "\n----------------GetObject2 \n";
return Test(val);
}
int main()
{
Test t1;//1、调用带整型参数的构造函数
cout << "\n----------------main GetObject1 \n";
Test t2 = GetObject(t1);//函数调用
cout << "\n----------------main GetObject2 \n";
return 0;
}