C++11 右值引用

C++11 右值引用,第1张

C++11 右值引用

C++11右值引用
  • 概念
    • 左值引用
    • 右值引用
    • 常量左值引用
  • 通过右值引用减少拷贝次数
    • std::move(左值)

概念

本文主要讲述右值引用相关概念,讲到右值,当然是相对于左值而言。简单理解,能够取地址的 “对象” 为左值,相反则为右值,这里的 “对象” 包含变量、立即数、函数返回值、类型等一切我们想要它进行 *** 作的 target。

左值引用

typename T;
T a; //a 即是一个左值,对它的引用就是左值引用:
T& p = a;

右值引用

右值可以细分为纯右值和将亡值()。
如前所述,右值对象不能取地址,如:
int b = 1; //b是左值,1是纯右值
class A;
A c = A(); //c 是左值,A()是右值。 该语句会报错,后续解释原因。
对右值的引用,即为右值引用:
int&& b = 1; //这里b是对右值 1 的引用
A&& c = A(); //这里c是对一个右值的引用

常量左值引用

一个左值引用只可以绑定左值,如:int& b = 1; 是非法的。
一个右值引用只可以绑定右值:int a = 1; int&& d = a; 是非法的。
常量左值引用:const value_type&, 比较特殊,它可以绑定左值或右值。

引用是变量的别名,右值没有地址,其内容不能被修改,因此左值引用不能指向右值。

但 const 左值引用是可以指向右值的,因为const 限制了该引用所指对象不能被修改,这也是为什么要使用const &作为函数参数的原因之一,如std::vector 的 push_back:

void push_back(const value_type& val);

push_back 的参数既可以是左值,也可以是一个右值。

通过右值引用减少拷贝次数 std::move(左值)

调用 std::move(左值) 返回该左值对象为右值

class A;
A a;
A&& b = std::move(a);  

移动函数 std::move 将对象 a 转化为右值,其所对应的内存块的所有权转移给了一个右值引用 b,这过程中并没有调用拷贝构造函数。

这里,声明一个测试类 test.h:

#include 
class test
{
private:
    static int count;
public:
    test();
    test(const test &c);
    void print();
    ~test();
};

类定义 test.cpp

#include "test.h"
test::test()
{
    ++count;
    std::cout << "construct. " << count <<" address:"<< this << std::endl;
}
test::test(const test &c)
{
    ++count;
    std::cout << "copy constructor." << count <<" address:"<< this << std::endl;
}
test::~test()
{
    --count;
    std::cout << "destruct." << count <<" address:"<< this < 

主函数: main.cpp

#include "test.h"
int main()
{
	test a;
	test b = a;
	test&& c = std::move(a);
}

输出结果:
construct. 1 address:0x7ffcef925bb7
address a = 0x7ffcef925bb7
copy constructor.2 address:0x7ffcef925bb6
address b = 0x7ffcef925bb6
address c = 0x7ffcef925bb7
destruct.1 address:0x7ffcef925bb6
destruct.0 address:0x7ffcef925bb7
可以看出,拷贝构造函数只在给 b赋值时被调用一次,右值引用 c 只是接管了a 的所有权。

进一步,添加函数:

test print()
{
    cout << "static void print()." << endl;
    test T;
    cout << "exit static void print()."<< endl;
    return T;
}

修改main.cpp

#include 
#include "test.cpp"
using namespace std;
static test print();
int main()
{
    cout << "hello,world." << endl;
    test a;
    test b = a;
    b.print();
    test c = print();
    return 0;
}

输出:
hello,world.
construct. 1 address:0x7fff20c8efce
copy constructor.2 address:0x7fff20c8efcd
this is print() in class test.
static void print().
construct. 3 address:0x7fff20c8ef9f
exit static void print().
copy constructor.4 address:0x7fff20c8efcf
destruct.3 address:0x7fff20c8ef9f
copy constructor.4 address:0x7fff20c8efcc
destruct.3 address:0x7fff20c8efcf
destruct.2 address:0x7fff20c8efcc
destruct.1 address:0x7fff20c8efcd
destruct.0 address:0x7fff20c8efce

分析:
如前述所分析test b = a; 调用了拷贝构造函数;
static test print() 函数在返回 对象 T 时一共发生了两次拷贝:
1)通过拷贝构造函数,创建临时对象 tmp,析构T;
2)通过拷贝构造函数,创建对象 c, 析构临时对象 tmp;

修改 static test print()为 static test&& print(), 同时修改main.cpp

test&& print()
{
    cout << "static void print()." << endl;
    test T;
    cout << "exit static void print()."<< endl;
    return move(T);
}

static test&& print();
int main()
{
    cout << "hello,world." << endl;
    test a;
    test b = a;
    b.print();
    test&& c = print();
    return 0;
}

test&& print()
{
    cout << "static void print()." << endl;
    test T;
    cout << "exit static void print()."<< endl;
    return move(T);

输出:
hello,world.
construct. 1 address:0x7fffcf8dbb17
copy constructor.2 address:0x7fffcf8dbb16
this is print() in class test.
static void print().
construct. 3 address:0x7fffcf8dbaef
exit static void print().
destruct.2 address:0x7fffcf8dbaef
destruct.1 address:0x7fffcf8dbb16
destruct.0 address:0x7fffcf8dbb17

由于 std::move()将 T 转化为右值,其所有权被直接赋予右值引用 c,省去了中间的两次拷贝。
注意:
1)此时如果将 main.cpp中的 c 声明为一个左值,将增加一次拷贝,效果等同于:

test c = test();

2)Linux中采用g++编译器编译,为避免编译器自动优化,编译指令需添加参数:
-fno-elide-constructors

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/3971719.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-10-21
下一篇 2022-10-21

发表评论

登录后才能评论

评论列表(0条)

保存