返回类对象时,什么时候调用拷贝构造函数,什么时候会进行返回值优化(RVO)

该文探讨了C++代码中涉及拷贝构造函数调用的情况,解释了返回值优化(RVO)和命名返回值优化(NRVO)如何避免不必要的拷贝构造函数调用。同时,文章提到了移动语义在优化对象转移过程中的作用,以及不同函数如fun2()和doWork2()中对象处理的差异,分析了为何在某些情况下会发生移动操作而其他情况下则没有。
摘要由CSDN通过智能技术生成
#include<iostream>
using namespace std;

class Person
{
public:
	Person()
	{

	}
	Person(int age)
	{
		m_Age = age;
	}
	Person(const Person& p)
	{
		cout << "拷贝构造函数" << endl;
	}
	Person fun()
	{
		cout << "fun this" << " " << this << endl;
		return *this;
	}
	Person fun2()
	{
		Person p(1);
		cout <<"fun2 p"<<" "<< & p << endl;
		return p;
	}
	Person fun3(Person &p)
	{
		cout << "fun3 p" << " " << &p << endl;
		return p;
	}
	int m_Age;
};
Person oi()
{
	Person p;
	cout << "oi p" << &p << endl;
	return p;
}
Person oI(Person &p)
{
	cout << "oI p" << &p << endl;
	return p;
}
int main()
{
	Person p1(10);
	Person p2(20);
	Person p3(30);
	p2=p1.fun();
	cout << "p2" << " " << &p2 << endl;
	Person p4,p5,p6,p7;
	p5 = p4.fun2();
	cout <<"p5"<<" "<< & p5 << endl;
	/*p7 = p6.fun3(p3);
	cout << "p7" <<" " << &p7 << endl;*/
	Person p8 = oi();
	cout << "p8" << " " << &p8 << endl;
	Person p9 = oI(p1);
	cout << "p9" << " " << &p9 << endl;
	system("pause");//按任意键继续
	return 0;
}

在这段代码中,fun2()函数和oi()函数没有调用拷贝构造函数的原因是因为编译器进行了优化。具体地说,编译器进行了返回值优化(Return Value Optimization,RVO)和命名优化(Named Return Value Optimization,NRVO)。

fun2()函数中,对象p被创建并返回,但由于编译器的优化,实际上并没有执行拷贝构造函数。编译器通过直接在函数内部创建p的位置作为返回值的位置,避免了额外的拷贝操作。因此,fun2()函数的返回值直接被赋值给p5,没有触发拷贝构造函数。

oi()函数中,对象p被创建并返回,同样地,编译器进行了返回值优化,避免了额外的拷贝操作。因此,oi()函数的返回值直接被赋值给p8,也没有触发拷贝构造函数。

需要注意的是,返回值优化是编译器的一种优化技术,不是C++标准的要求。不同的编译器可能有不同的优化策略,因此在其他编译器或编译器设置下,可能会触发拷贝构造函数的调用。

fun()函数和oI()函数调用了拷贝构造函数,是因为它们返回的是对象本身(通过值传递),而不是通过返回值优化(RVO)进行优化。

fun()函数中,对象*this被作为返回值进行返回,而返回值的类型是Person。因为返回值是通过值传递的方式返回的,所以会触发拷贝构造函数来创建返回值的副本。

oI()函数中,函数参数是按引用传递的(Person& p),但在函数返回时,仍然返回的是对象本身(通过值传递)。因此,在返回时会触发拷贝构造函数来创建返回值的副本。

需要注意的是,对于编译器优化的行为并没有明确的规定,不同的编译器和编译器设置可能会有不同的结果。在某些情况下,编译器可能会对这些情况进行优化,避免不必要的拷贝构造函数的调用,这取决于编译器的实现和优化策略。

在C++中,返回值优化(Return Value Optimization,RVO)是一种编译器优化技术,用于避免不必要的对象拷贝。虽然具体的优化行为取决于编译器的实现和优化策略,但可以根据一些常见的情况来判断是否可能发生返回值优化。

以下是一些常见的情况,可能触发返回值优化:

  1. 返回局部对象:当函数返回一个局部对象的副本时,编译器可以进行返回值优化,避免额外的拷贝构造函数调用。例如:
Person fun()
{
    Person p; // 局部对象
    // ...
    return p;
}

 2.返回临时对象:当函数返回一个临时对象时,编译器可以进行返回值优化,避免额外的拷贝构造函数调用。例如:

Person fun()
{
    // ...
    return Person(10); // 返回一个临时对象
}

 3.返回函数参数:当函数返回一个函数参数的副本时,编译器可能进行返回值优化。例如:

Person fun(Person p)
{
    // ...
    return p; // 返回函数参数的副本
}

在函数fun2()中,虽然没有调用拷贝构造函数,但是在返回p时发生了对象的移动操作。这是因为编译器对于临时对象的返回通常会使用移动语义,而不是拷贝语义。

fun2()中,对象p在函数内部被创建,并在函数返回时被移动到p5上。移动语义允许在不进行深拷贝的情况下转移资源的所有权,提高了效率。因此,p5的地址和fun2()函数内部的p的地址不同,因为对象被移动到了新的位置。

需要注意的是,移动语义是C++11引入的特性,用于优化对象的转移操作。当对象可以被有效地移动时,编译器会优先选择移动语义而不是拷贝语义,以提高性能。但是移动语义的使用还受到对象类型的限制,需要对象实现移动构造函数和移动赋值运算符才能实现移动操作。

因此,在fun2()中,对象p的地址和最终赋值给p5的地址不同,是因为发生了对象的移动操作而不是拷贝操作。

 

下面代码也没有调用拷贝构造函数,也没有移动

#include<iostream>
using namespace std;


//拷贝构造函数的调用时机



class Person
{
public:
	Person()
	{
		cout << "Person 构造函数的调用" << endl;
	}
	Person(int age)//有参构造函数
	{
		cout << "Person有参构造函数" << endl;
		m_Age = age;
	}
	Person(const Person& p)//拷贝构造函数
	{
		cout << "Person拷贝构造函数调用" << endl;
		m_Age = p.m_Age;
	}
	~Person()
	{
		cout << "Person 析构函数的调用" << endl;
	}
	int m_Age;
};

Person doWork2()
{
	Person p1;
	cout << (int)&p1 << endl;
	return p1;
}
void test03()
{
	Person p = doWork2();
	cout << (int)&p << endl;
}
int main()
{
	
	test03();
	system("pause");//按任意键继续
	return 0;
}

 

doWork2()函数没有使用拷贝构造函数,但是在test03()函数中,对象p的地址和p1的地址一样的原因是编译器进行了返回值优化(Return Value Optimization,RVO)。 

 同样是没有调用拷贝构造函数,为什么上段代码有移动,这个就没有发生移动操作?

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值