深度解析-->c++中构造函数,拷贝构造,赋值运算符重载,析构函数的调用情况

前言:

       经过前面对类的认识,构造函数,拷贝构造,运算符重载,析构函数这些知识的学习,相信我们对c++已经有了一个初步的认识;那么本篇博客在前面的基础上,来对这些知识做一个综合性的整理同时也是考察我们对其的掌握程度

(例1):

class  Date
{
public:
	Date()
	{
		cout << "Date()" << endl;
	}
	Date(const  Date&  d)
	{
		cout << "Date(const Date& d)" << endl;
	}
	Date&  operator  =(const  Date&  d)
	{
		cout << "Date& operator=(const Date& d)" << endl;
		return  *this;
	}
	~Date()
	{
		cout << "~Date()" << endl;
	}
};

void f1(Date d)
{
}
int main()
{
	Date d;
	f1(d);
	return 0;
}
这里构造函数、拷贝构造、析构函数分别调用了几次?


简单分析:创建对象d是调用1次构造函数,f1(d)这里由于f1()函数传参时是采用传值的方式,所以会先生成一个临时对象,这里调用了1次拷贝构造;最后两个对象都被析构,所以调用2次析构函数。

如果参数改为“引用”

void f1(Date&d)
{
}
则:


(例2)如果我们把程序再稍加改动:

void f1(Date&d)
{
}
Date f2()
{
	Date d;
	return d;
}
int main()
{
	Date d1;
	f1(d1);
	d1 = f2();
	return 0;
}
那么试问: 构造函数、拷贝构造、赋值运算符重载、析构函数各调用了几次?

我开始的猜想:2次构造,0次拷贝构造,1次重载,2次析构

正确答案:


这里详细解释一下为什么调用了1次拷贝构造,这块也是一个易错点



这也就不难理解为什么调用了3次析构函数了。

(例3):
接下来我们再把f2()函数以传引用的方式进行返回,看一下构造函数、拷贝构造、赋值运算符重载、析构函数各调用了几次?

Date&f2()
{
	Date d;
	return d;
}

详解:



(例4):再来看一种情况:

void f1(Date d)
{
}
Date f2()
{
	Date d;
	return d;
}
int main()
{
	//Date d1;
	//f1(d1);
	f1(Date());
	return 0;

注意这里:Date()构建了一个临时对象,传递给f1的参数(因为这里是传值方式),本来如果是:Date d1;  f1(d1);这两句的话我们不难理解调用了“1次构造,1次拷贝构造”。但是这里是:f1(Date())的方式,“构造临时对象”和“拿临时对象拷贝构造一个对象”这两个东西是在一个语句中完成的,也就是编译器对此作了优化。


(例5):

Date f2()
{
	return Date();
}
int main()
{
	Date d1;
	d1 = f2();
	return 0;
}
试问: 构造函数、拷贝构造、赋值运算符重载、析构函数各调用了几次?
我的猜想: 构造函数-->2次

                   拷贝构造-->1次

                   赋值运算符重载-->1次

                   析构函数-->3次

理由第一次构造毫无疑问是:Date d1时创建对象;第二次是在调用f2()函数时,return  Date()这句构造了一个临时对象,但是它要马上还给操作系统(因为函数作用域的关系),而f2()函数时以值进行返回,所以还要以这个“临时对象”拷贝构造一个与其一模一样的对象作为返回值,这里就调用了1次拷贝构造。

事实上正确答案是:(这里由于编译器的优化,return Date()构造的临时对象马上又要拷贝构造,作为返回值,所以编译器直接一步到位用“构造的临时对象”作为返回,没有调用拷贝构造)         

                  构造函数-->2次

                  拷贝构造-->0次

                  赋值运算符重载-->1次

                  析构函数-->2次


详解如下图所示:



问题:为什么下面这种情况编译器就不会做优化呢?




(例6):

Date f2()
{
	return Date();
}
int main()
{
	Date d1=f2();
	return 0;
}
试问: 构造函数、拷贝构造、赋值运算符重载、析构函数各调用了几次?

我的猜想:构造函数-->1次

                  拷贝构造-->2次

                  赋值运算符重载-->0次

                  析构函数-->3次

理由:由例(5)的讲解得知在f2()函数中编译器的优化,所以f2()函数中进行了:1次构造,0次拷贝构造。再来看main()函数中Date d1=f2(),拿“f2()函数中的临时对象”拷贝构造d1,所以调用1次拷贝构造。

正确答案:

                 构造函数-->1次

                 拷贝构造-->0次

                 赋值运算符重载-->0次

                 析构函数-->1次

详解如下图所示:



(例7):

Date f2()
{
	return Date();
}
int main()
{
	Date d1=f2();
	return 0;
}
试问:构造函数、拷贝构造、赋值运算符重载、析构函数各调用了几次?

这里有了上面(编辑器优化的讲解),我们不难得出:

               构造函数-->1次

               拷贝构造-->0次

               赋值运算符重载-->0次

               析构函数-->1次


(例8):在例7的代码上稍作改动:把f2()函数改为传引用返回

Date& f2()
{
	return Date();
}
int main()
{
	Date d1=f2();
	return 0;
}
试问:构造函数、拷贝构造、赋值运算符重载、析构函数各调用了几次?

正确答案:

                 构造函数-->1次

                 拷贝构造-->1次

                 赋值运算符重载-->0次

                 析构函数-->2次

这里为什么返回“引用”就和例(7)结果不一样了呢?原因是它会影响编译器的优化


---------------------------------------------------------------------------------------------------------------------------------------------

下面我们看这样一个题:


分别讲解一下Test2:



Test3:



猜你喜欢

转载自blog.csdn.net/melody_1016/article/details/54915131