拷贝构造,赋值,右值拷贝构造,右值拷贝赋值函数的使用

1.c++编译器返回值优化

​ 由于编译器默认开启了返回值优化(RVO/NRVO, RVO, Return Value Optimization 返回值优化,或者NRVO, Named Return Value Optimization)。

​ 虽然各大厂家的编译器都已经都有了这个优化,但是这并不是c++标准规定的,而且不是所有的返回值都能够被优化,而这篇文章的主要讲的右值引用移动语义可以解决编译器无法解决的问题。

​ 为了更好的观察结果,可以在编译的时候加上-fno-elide-constructors选项(关闭返回值优化)。

g++ -fno-elide-constructors -std=c++11 -O3 -o main main.cpp
2.拷贝构造函数
#include <iostream>

class A { 
public:
    A(){std::cout << "A construct." << std::endl;}  
    ~A(){std::cout << "A desconstruct." << std::endl;}
    A(const A& a){std::cout << "A copy construct" << std::endl;}
    A(A&& a){std::cout << "A right ref construct" << std::endl;}
    A& operator = (const A& a) {std::cout << "A copy and assign." << std::endl;}
    A& operator = (A&& a) {std::cout << "A right ref and assign." << std::endl;}
};

int main(int argc, char** argv) {
    std::cout << "----------a1-----------" << std::endl;
    A a1; 
    std::cout << "----------a2-----------" << std::endl;
    A a2 = a1; 
    std::cout << "----------a3-----------" << std::endl;
    A a3(a1);
}

运行:

----------a1-----------
A construct.
----------a2-----------
A copy construct
----------a3-----------
A copy construct
A desconstruct.
A desconstruct.
A desconstruct.
3. 拷贝赋值
#include <iostream>

class A { 
public:
    A(){std::cout << "A construct." << std::endl;}  
    ~A(){std::cout << "A desconstruct." << std::endl;}
    A(const A& a){std::cout << "A copy construct" << std::endl;}
    A(A&& a){std::cout << "A right ref construct" << std::endl;}
    A& operator = (const A& a) {std::cout << "A copy and assign." << std::endl;}
    A& operator = (A&& a) {std::cout << "A right ref and assign." << std::endl;}
};

/* 拷贝赋值*/
int main(int argc, char** argv) {
    std::cout << "----------a1-----------" << std::endl;
    A a1; 
    std::cout << "----------a2-----------" << std::endl;
    A a2; 
    a2 = a1; 
}

运行:

----------a1-----------
A construct.
----------a2-----------
A construct.
A copy and assign.
A desconstruct.
A desconstruct.
4. 右值拷贝构造
#include <iostream>

class A { 
public:
    A(){std::cout << "A construct." << std::endl;}  
    ~A(){std::cout << "A desconstruct." << std::endl;}
    A(const A& a){std::cout << "A copy construct" << std::endl;}
    A(A&& a){std::cout << "A right ref construct" << std::endl;}
    A& operator = (const A& a) {std::cout << "A copy and assign." << std::endl;}
    A& operator = (A&& a) {std::cout << "A right ref and assign." << std::endl;}
};

A get() { 
    return A();  //等价于 A a; return a;
}

/* 右值拷贝构造*/
int main(int argc, char** argv) {
    std::cout << "----------a1-----------" << std::endl;
    A a1; 
    std::cout << "----------a2-----------" << std::endl;
    A a2 = std::move(a1);
    std::cout << "----------a3-----------" << std::endl;
    A a3 = get();
}

运行:

----------a1-----------
A construct.
----------a2-----------
A right ref construct
----------a3-----------
A construct.
A right ref construct
A desconstruct.
A right ref construct
A desconstruct.
A desconstruct.
A desconstruct.
A desconstruct.

备注:

1. get()函数,首先构造一个A的实例,然后拷贝构造函数返回值临时变量,函数返回值临时变量再构造A对象实例a3; 因为A()和函数返回值临时变量都是临时对象或者将亡值(如:局部变量A a; return a;),所以都是调用右值拷贝构造函数;
2. std::move是将左值无条件转为右值,因此a2也调用拷贝构造函数;

如果没有右值拷贝构造, 运行:

----------a1-----------
A construct.
----------a2-----------
A copy construct
----------a3-----------
A construct.
A copy construct
A desconstruct.
A copy construct
A desconstruct.
A desconstruct.
A desconstruct.
A desconstruct.
5. 右值拷贝赋值
#include <iostream>

class A { 
public:
    A(){std::cout << "A construct." << std::endl;}  
    ~A(){std::cout << "A desconstruct." << std::endl;}
    A(const A& a){std::cout << "A copy construct" << std::endl;}
    A(A&& a){std::cout << "A right ref construct" << std::endl;}
    A& operator = (const A& a) {std::cout << "A copy and assign." << std::endl;}
    A& operator = (A&& a) {std::cout << "A right ref and assign." << std::endl;}
};

A get() {
    return A(); //等价于A a; return a;
}

/* 右值赋值*/
int main(int argc, char** argv) {
    std::cout << "----------a1-----------" << std::endl;
    A a1; 
    std::cout << "----------a2-----------" << std::endl;
    A a2; 
    a2 = std::move(a1);
    std::cout << "----------a3-----------" << std::endl;
    A a3; 
    a3 = get();
}

运行:

----------a1-----------
A construct.
----------a2-----------
A construct.
A right ref and assign.
----------a3-----------
A construct.
A construct.
A right ref construct
A desconstruct.
A right ref and assign.
A desconstruct.
A desconstruct.
A desconstruct.
A desconstruct.

如果没有右值拷贝赋值, 运行:

----------a1-----------
A construct.
----------a2-----------
A construct.
A copy and assign.
----------a3-----------
A construct.
A construct.
A copy construct
A desconstruct.
A copy and assign.
A desconstruct.
A desconstruct.
A desconstruct.
A desconstruct.
6. 总结:
1. 右值拷贝构造和右值拷贝赋值的目的是提高程序的运行效率;
2. 如果一个类没有右值拷贝构造和右值拷贝赋值函数,那么就调用(左值)拷贝构造函数和(左值)拷贝赋值函数;
3. 编译器优化,会默认开启返回值优化, 对于右值拷贝构造(倒数第二个例子),如果使用默认编译器优化(g++ -std=c++11 -o main main.cpp),运行结果是:
----------a1-----------
A construct.
----------a2-----------
A copy construct
----------a3-----------
A construct.
A desconstruct.
A desconstruct.
A desconstruct.

我们会发现,本来应该的两次拷贝构造,实际执行时一次都没有;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值