C++构造函数、拷贝构造函数、赋值运算符漫谈(三)——NRV

//

首先看下面一段程序:

class X
{
public:
	X()
	{
		cout<<"X()"<<endl;
	};
	X(int v):val(v)
	{
		cout<<"X(int)"<<endl;
	}
	X(const X& x)
	{
		cout<<"X(const X& x)"<<endl;
	}
	X& operator=(const X&)
	{
		cout<<"="<<endl;
		return *this;
	}
	~X()
	{cout<<"destructor"<<endl;}
	void memfun()
	{
	  cout<<"memfun"<<endl;
	}
private:
	int val;
};

int _tmain(int argc, _TCHAR* argv[])
{
   X x0(1024);
	X x1=X(1024);
	X x2=(X)1024;
}

以上两个运行结果截图分别来自VS和g++。

    看到这个输出,相信不少人和我一样有疑惑:为什么没有调用拷贝构造函数?在回答这个疑问之前,我们先解决另外一个疑问——使用“=”创建的对象就一定要调用拷贝构造函数吗?

     从运行结果来看是否定的,在创建x1,x2的时候,我们的预想是编译器先创建一个临时对象,在使用临时对象作为拷贝构造函数的参数创建新对象。其实这里编译器采用了一种优化,叫做Named Return Value(NRV)。

Named Return value 优化:

nrv优化的本质是优化掉拷贝构造函数,去掉它不是生成它。当然了,因为为了优化掉它,前提就是它存在,也就是欲先去之,必先有之,这个也就是nrv优化需要有拷贝构造函数存在的原因。 nrv优化会带来副作用,目前也不是正式标准,倒是那个对象模型上举的应用例子看看比较好。极端情况下,不用它的确造成很大的性能损失,知道这个情况就可以了。  

为什么必须定义了拷贝构造函数才能进行nrv优化?首先它是lippman在inside c++ object mode里说的。那个预先取之,必先有之的说法只是我的思考。查阅资料,实际上这个可能仅仅只是cfont开启NRV优化的一个开关。 

The C++ standard allows the elision of the copy constructor (even if this results in different program behavior), which has a side effect of enabling the compiler to treat both objects as one。也就是我说的副作用,c++标准允许这个副作用的出现,也就是它允许进行NRV优化,但不是必须。 

时间: 2024-10-28 10:29:18

C++构造函数、拷贝构造函数、赋值运算符漫谈(三)——NRV的相关文章

【C/C++学院】(6)构造函数/析构函数/拷贝构造函数/深copy浅copy

1.构造函数     类的初始化即为构造函数.也为:隐式的初始化. 构造函数在对象初始化的时候,自动被调用.隐式的调用. 构造函数分为三种:有参构造函数.无参构造函数.拷贝构造函数. 有参构造函数调用有三种:括号法.等号法.手工法. #include <iostream> using namespace std; class Test { private: int m_a; public: Test()//无参构造函数 { } Test(const Test &obj)//拷贝构造函数

C++中拷贝构造函数的应用详解_C 语言

一.C++中拷贝构造函数的定义: 有一个参数的类型是其类类型的构造函数是为拷贝构造函数. 如下所示: X::X( const X& x); Y::Y( const Y& y, int =0 ); //可以是多参数形式,但其第二个即后继参数都有一个默认值 二.拷贝构造函数的应用: 当一个类对象以另一个同类实体作为初值时,大部分情况下会调用拷贝构造函数. 一般是这三种具体情况: 1.显式地以一个类对象作为另一个类对象的初值,形如X xx=x; 2.当类对象被作为参数交给函数时. 3.当函数返回

C++构造函数、拷贝构造函数、赋值运算符漫谈(二)——函数返回值

 首先我们先看一下C程序的返回值处理情况,我们知道当C函数返回int等小型数据时直接将返回值放入eax寄存器.那当返回大的数据结构又是如何处理呢?看如下一段代码: #include <stdio.h> typedef struct big_thing { char buf[128]; }big_thing; big_thing return_test() { big_thing b; b.buf[0]=0; retutn b; } int main(int argc, char *argv[]

C++构造函数、拷贝构造函数、赋值运算符漫谈(一)——函数参数传递

在讨论C++函数参数之前,我们先来看一下C程序是如何调用函数的.   如图,为C语言的函数调用记录,C++也类似.当有如下函数: void foo(X x0): 如果有如下调用方式: X xx; foo(xx);     编译器(对于C)会将实参xx以"位逐次拷贝"方式复制给形参x0(注:X0即在上图函数活动记录中的参数位置).在C++中,如果一个Class也展现了"位逐次拷贝语义"[1],且用户没有定义拷贝构造函数,那么编译器按照这种方式进行拷贝没有问题.但是当一

拷贝构造函数和拷贝赋值运算符的调用问题

问题描述 拷贝构造函数和拷贝赋值运算符的调用问题 假设有如下类:class HasPtr{public: HasPtr(const string &s): ps(new string(s))i(0){} HasPtr(const HasPtr &hp){ ps=new string(*hp.ps); i=hp.i; } HasPtr(const char *s):ps(new string(s))i(0){} HasPtr & operator=(const HasPtr &

php中拷贝构造函数、赋值运算符重载_php技巧

对象的赋值与复制: 赋值:通过" = "运算符重载User a(10),b;b = a;复制:调用复制构造函数User b;User a(b);或者User a = b;//相当于User a(b);与赋值的区别,赋值是对一个已经存在的对象进行赋值(已经实现定义了被赋值的对象),而复制是从无到有建立一个新的对象,并使它与已有的对象相同.浅复制与深复制: 若对象中有指针成员,在复制时,只会将该指针成员的地址复制给新建立的对象,因此,两个对象中的指针成员都指向了同一块内存区域,在释放时会出

详解C++ 拷贝构造函数和赋值运算符_C 语言

本文主要介绍了拷贝构造函数和赋值运算符的区别,以及在什么时候调用拷贝构造函数.什么情况下调用赋值运算符.最后,简单的分析了下深拷贝和浅拷贝的问题. 拷贝构造函数和赋值运算符 在默认情况下(用户没有定义,但是也没有显式的删除),编译器会自动的隐式生成一个拷贝构造函数和赋值运算符.但用户可以使用delete来指定不生成拷贝构造函数和赋值运算符,这样的对象就不能通过值传递,也不能进行赋值运算. class Person { public: Person(const Person& p) = dele

关于拷贝构造函数和赋值运算符

重点:包含动态分配成员的类 应提供拷贝构造函数,并重载"="赋值操作符. 以下讨论中将用到的例子: class CExample { public: CExample(){pBuffer=NULL; nSize=0;} ~CExample(){delete pBuffer;} void Init(int n){ pBuffer=new char[n]; nSize=n;} private: char *pBuffer; //类的对象中包含指针,指向动态分配的内存资源 int nSize

c++中拷贝构造函数的参数类型必须是引用

如果拷贝构造函数中的参数不是一个引用,即形如CClass(const CClass c_class),那么就相当于采用了传值的方式(pass-by-value),而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构 造函数.因此拷贝构造函数的参数必须是一个引用   在C++中, 构造函数,拷贝构造函数,析构函数和赋值函数(赋值运算符重载)是最基本不过的需要掌握的知识. 但是如果我问你"拷贝构造函数的参数为什么必须使用引用类型?"这个问题, 你会怎么回答? 或许你会回答为了