C++对传参合传返回值时构造的优化处理

讨论构造拷贝构造中的N种调用情况:

用一个简单的日期类来说明问题:

class Date
{
public:
	Date(int year = 2017,int month = 10,int day = 21)
		:_year(year)
		, _month(month)
		, _day(day)
	{
		cout << "构造函数" << endl;
	}

	Date(const Date& d)
	{
		this->_year = d._year;
		this->_month = d._month;
		this->_day = d._day;

		cout << "拷贝构造" << endl;
	}

	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			this->_year = d._year;
			this->_month = d._month;
			this->_day = d._day;
		}

		cout << "operator=" << endl;

		return *this;
	}

	~Date()
	{
		cout << "析构函数" << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

一:

void func1(Date d)
{

}

void test1()
{
	Date d1;
	func1(d1);
}

test1中,创建d1调用了构造函数,然后调用func1函数传参,Date d是d1的临时拷贝,调用了拷贝构造函数然后func1函数结束后d自动销毁,调用析构函数,然后test1结束后调用析构函数清理d1。

二:

Date func2()
{
	Date tmp;
	return tmp;
}

void test2()
{
	func2();
}


扫描二维码关注公众号,回复: 1236991 查看本文章

test2里调用func2,创建tmp调用构造函数,返回时创建临时对象调用拷贝构造函数,然后func2结束自动销毁tmp调用析构,然后test2函数结束后调用析构来清理tmp的拷贝。


三:编译器优化处理:

 
 
void func1(Date d)
{

}

void test1()
{
	//Date d1;
	//func1(d1);
	func1(Date());//传匿名对象
}



test1里直接调用func1,传参的同时创建匿名对象,,那么编译器会优化处理,把test1里的传参和func1里的Date d,合二为一,那么就只调用一次构造函数,也就只析构一次。

四:编译器优化处理:

Date func2()
{
	//Date tmp;
	//return tmp;
	return Date();
}

void test2()
{
	func2();
}


func里从先创建对象再返回,变成了创建对象的同时返回,那么编译器会优化,本来先创建对象调用构造函数,然后创建临时对象返回调用拷贝构造函数,现在两步合二为一,直接创建,调用一次构造函数来返回,那么就只调用一次析构了。


五:了解了一些编译器的优化,我举三个例子,它们的功能一样,但效率却大大不同!

1.效率最低

Date func1()
{
	Date tmp;
	return tmp;
}

void test1()
{
	Date d1;
	d1 = func1();
}

 
  
 

test1里创建d1调用构造函数,然后调用func1,func1里创建tmp调用构造函数,然后返回tmp的临时拷贝调用拷贝构造函数,func1结束调用析构函数清理tmp,然后把返回的临时拷贝赋给d1调用operator=,然后析构临时拷贝和d1调用两次析构函数。

2.效率提高:

Date func2()
{
	Date tmp;
	return tmp;
}

void test2()
{
	Date d1 = func2();
}


上一个例子是先创建对象再给对象赋值,这个是创建的同时初始化,所以test2中的"="是拷贝构造。

调用func2,创建tmp调用构造函数,然后调用拷贝构造来创建tmp的临时拷贝返回,出了func2会调用析构函数来清理tmp,这里编译器会将test2里的拷贝构造和func2里的拷贝构造合二为一,所以只有一次拷贝构造,之后出了test2再调用析构函数来清理拷贝的对象。

3.效率最高:

Date func3()
{
	return Date();
}

void test3()
{
	Date d1 = func3();
}


test3调用func3,本来先创建对象调用构造函数,然后创建临时对象返回调用拷贝构造函数,现在两步合二为一,直接创建,调用一次构造函数来返回,那么就只调用一次析构函数来清理fun3里的对象,然后tets3里是创建对象同时初始化,那编译器会将d1的构造和fun3的构造合二为一,那么一共只调用了一次构造函数,也就只用析构一次。

最后分享一道很容易出错的题目:

首先拿一个更简单的类作说明:

class AA
{
public:
	AA(int data = 1)
		:_data(data)
	{
		cout << "构造函数" << endl;
	}
	AA(const AA& a)
	{
		this->_data = a._data;
		cout << "拷贝构造" << endl;
	}
	AA& operator=(const AA& a)
	{
		if (this != &a)
		{
			this->_data = a._data;
		}
		cout << "operator=" << endl;
		return *this;
	}
	~AA()
	{
		cout << "析构函数" << endl;
	}
private:
	int _data;
};

AA f(AA a)
{
	return a;
}

void Test1()
{
	AA a1;
	a1 = f(a1);
}

void Test2()
{
	AA a1;
	AA a2 = f(a1);
}

void Test3()
{
	AA a1;
	AA a3 = f(f(a1));
}

问题是,每个Test函数分别调用了几次拷贝构造函数,几次赋值运算符的重载?

Test1:


AA f(AA a)
{
	return a;
}

void Test1()
{
	AA a1;
	a1 = f(a1);
}
Tets1里,调用f,传参调用拷贝构造函数创建对象a,然后f调用拷贝构造函数来创建a的临时拷贝返回,然后返回值赋给a1调用operator=。

Test2:



AA f(AA a)
{
	return a;
}

void Test2()
{
	AA a1;
	AA a2 = f(a1);
}

Test2里调用f,传参调用拷贝构造函数创建a,然后f调用拷贝构造来创建a的临时拷贝返回,返回时,a2是创建时初始化所以是拷贝构造,编译器将两者合二为一,只调用一次拷贝构造将返回值给a2。

Test3:



AA f(AA a)
{
	return a;
}


void Test3()
{
	AA a1;
	AA a3 = f(f(a1));
}

这个稍微有一定难度:



猜你喜欢

转载自blog.csdn.net/han8040laixin/article/details/78306770