Bootstrap

C++对象的构造顺序解析

#include <iostream>
using namespace std;
class Test {
public:
	Test(int data = 10) :ma(data)
	{
		cout << "Test(int)" << endl;
	}
	~Test()
	{
		cout << "~Test(int)" << endl;
	}
	Test(const Test &obj)
	{
		cout << "Test(const Test &obj)" << endl;
		this->ma = obj.ma;
	}
	void operator=(const Test &obj)
	{
		cout << "=operator(const Test &obj)" << endl;
		if (this == &obj)
			return;
		this->ma = obj.ma;
	}
private:
	int ma;
};
int main()
{
	cout << "+++++++++++++++" << endl;
	Test t1 = Test(50);
	cout << "+++++++++++++++" << endl;
	Test t2;
	cout << "+++++++++++++++" << endl;
	t2 = Test(50);
	cout << "+++++++++++++++" << endl;
	t2 = 50;
	cout << "+++++++++++++++" << endl;
        return 0;
}

以上代码的运行结果是:

当我们使用Test t1 = Test(50);拷贝构造了同类型的对象的时候临时对象不产生,故只调用了一次t1的构造函数。

当我们使用t2 = Test(50)的时候因为此时t2对象已经存在,这里并没有拷贝构造的操作,故需要临时变量的产生来给t2做等号赋值运算。然后语句结束后临时对象就被析构。

t2 = 50 同理。

然后程序的最后析构为t2的析构和t1的析构。

那么我们可以得出结论:如果临时对象"拷贝构造"同类型的对象那么临时对象不产生。

临时对象的函数应用总结

引例子:

我们在上面代码的基础上增加俩个函数

类内函数:

int getData()
	{
		return ma;
	}

类外函数获取Test对象

Test GetTestObject1(Test t)
{
	int val = t.getData();
	Test tmp(val);
	return tmp;
}

此时我们调用GetTestObject1函数来看有多少个构造和析构

主函数:

int main()
{
	Test t1;
	Test t2 = GetTestObject1(t1);
	return 0;

}

结果:

一共有八行

第一行:t1的构造函数。

第二行:使用t1给参数t进行拷贝构造。

第三行:函数中的tmp的默认的构造函数。

第四行:函数中的tmp给main栈区的t2的拷贝构造(因为tmp是局部变量,函数调用后它的声明周期就结束了,如果要在main函数中取得函数中的对象,只能在tmp被析构前把它的内容拷贝构造给t2,)这里还因为临时对象给相同类型的对象拷贝构造,并不真正的产生临时变量,故这里相当于tmp直接给t2拷贝构造。

第五行:由于函数的执行完毕需要执行t和tmp的析构,这一行是析构tmp局部对象。

第六行:析构t

第七行:析构t2

第八行:析构t1

优化1:将参数的类型设置为引用

修改函数:

Test GetTestObject2(Test &t)
{
    int val = t.getData();
    Test tmp(val);
    return tmp;
}

运行结果:

可以看到经过这么修改,8行变为6行,少了参数t的构造和析构,避免了不必要的开销,优化了对象方法调用的过程。

优化2:根据(临时对象拷贝构造相同类型的对象的时候,并不产生新的对象)的原理我们可以将函数的返回值设置为临时对象(返回临时对象)。

代码:

Test GetTestObject3(Test &t)
{
	int val = t.getData();
	return Test(val);
}

这样之后结果:

这样之后从上次优化后的六行变为四行。又优化了tmp的构造和析构,又进一步减少了对象方法调用的开销。

上诉中所有的测试用例代码总结:

#include <iostream>
using namespace std;
class Test {
public:
	Test(int data = 10) :ma(data)
	{
		cout << "Test(int)" << endl;
		
	}
	~Test()
	{
		cout << "~Test(int)" << endl;
	}
	Test(const Test &obj)
	{
		cout << "Test(const Test &obj)" << endl;
		this->ma = obj.ma;
	}
	void operator=(const Test &obj)
	{
		cout << "=operator(const Test &obj)" << endl;
		if (this == &obj)
			return;
		this->ma = obj.ma;
	}
	int getData()
	{
		return ma;
	}
private:
	int ma;
};
Test GetTestObject1(Test t)
{
	int val = t.getData();
	Test tmp(val);
	return tmp;
}
Test GetTestObject2(Test &t)
{
	int val = t.getData();
	Test tmp(val);
	return tmp;
}
Test GetTestObject3(Test &t)
{
	int val = t.getData();
	return Test(val);
}
int main()
{
	//1
	/*cout << "+++++++++++++++" << endl;
	Test t1 = Test(50);
	cout << "+++++++++++++++" << endl;
	Test t2;
	cout << "+++++++++++++++" << endl;
	t2 = Test(50);
	cout << "+++++++++++++++" << endl;
	t2 = 50;
	cout << "+++++++++++++++" << endl;*/
	//2
	Test t1;
	Test t2 = GetTestObject3(t1);
	return 0;

}

总结:

1,函数调用传参,如果是对象传递引用。(少一次构造和一次析构的对象方法开销)。

2,函数需要返回对象值时候,返回临时对象(少一次局部对象的构造和析构)。

 

 

;