C++的右值引用

1. 语法
	int& left_ref;
	int&& right_ref;
2. 引入目的

右值引用引入了两个语义,移动语义(move sementice)和精确传递(perfect forward).

  1. 右值是即将销毁的对象,在某些场合例如拷贝的时候,我们想直接使用它的资源而重新申请资源。所以我们要区别对待左值和右值,实现函数重载,所以引入
  2. 精确传递是函数参数传递过程中保持左值/右值属性和const/no_const属性
3. 移动语义
class MyString{
public:
    MyString(){
        cout << "default constructor" << endl;
    }
    MyString(const char* p){
        cout << "construct from char*" << endl;
        len_ = strlen(p);
        init_data_(p);
    }

    MyString(const MyString& str){
        cout << "copy constructor" << endl;
        len_ = str.len_;
        init_data_(str.data_);
    }

    MyString& operator=(const MyString& str){
        cout << "copy assignment" << endl;
        if(this != &str){
            len_ = str.len_;
            init_data_(str.data_);
        }
        return *this;
    }

    ~MyString(){
        cout << "destructor" << endl;
    }

private:
    char* data_;
    int len_;
    void init_data_(const char* p){
        data_ = new char[len_ + 1];
        memcpy(data_, p, len_);
        data_[len_] = '\0';
    }
};
  1. 测试代码1
    vector<MyString> vec;
    vec.push_back(MyString("hello"));
    
    cout << "main end" << endl;    

输出

construct from char*
copy constructor
destructor
main end
destructor

观察得知发生了两次构造,申请了两次内存。

  1. 测试代码2
    MyString str;
    str = MyString("hello");

    cout << "main end" << endl;  

输出

default constructor
construct from char*
copy assignment
destructor
main end
destructor

我们观察到语句str = MyString("hello");也执行了两次构造,出现了两次资源的申请。

  1. 添加了移动构造和移动赋值之后。
    上面两个测试的输出变为,我们可以看到对于对于临时对象,会选择移动构造,我们可以在移动构造中直接使用临时对象的资源而不重新申请资源
construct from char*
move copy constructor
destructor
main end
destructor

default constructor
construct from char*
move assignment
destructor
main end
destructor
4. std::move
  1. 只有在是右值的时候,才会选择移动构造和赋值,但是有时候我们知道一个左值我们以后不会再使用它了,可以把它当成右值来使用。move函数提供了这种功能,把左值当成右值使用
  2. 另外使用std::move可以极大提高swap的效率。
    一般来说swap的定义如下,其中会有三次资源的重新申请
template<class T>
void swap(T& a, T& b){
    T tmp(a); //copy constructor
    a = b;     //assignment
    b = tmp;    //assignment
}

而使用move,可以避免这三次资源的重新申请。

template<class T>
void swap(T& a, T& b){
    T tmp(std::move(a)); //move copy constructor
    a = std::move(b);     //move assignment
    b = std::move(tmp);    //move assignment
}
5.perferct forwarding

完美转发:参数通过一个函数原封不动地传入第二个个函数,对第二个函数重载的选择,就像直接传入第二个参数一样

void g(int&& data){
    cout << "g: right_ref" << endl;
}
void g(int& data){
    cout << "g: left_ref" << endl;
}

template<class T>
void template_f(T&& data){
    g(std::forward<T>(data));
}

测试代码

    int i = 0;
    template_f(i);
    template_f(0);

输出

g: left_ref
g: right_ref

note

  1. 这个特性只是在模板函数中才有,在非模板函数中。
	void no_template_f(int&& data){
    	g(data);
	}

	//int i  =0;
    //no_template_f(i); // error: an rvalue reference can't bound to a lvalue
    no_template_f(1);

输出g: left_ref,因为在函数中参数data被当成左值引用。
如果调用g时加上std::forward<int>(data),那么输出是g: right_ref.

参考文章

右值引用与转移语义
Rvalue References and Perfect Forwarding in C++0x

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值