C++ Primer Plus(第6版)学习笔记-右值&移动&赋值

文章介绍了C++中的右值引用和移动语义,这两种特性允许程序员更高效地处理临时对象和内存管理。通过右值引用,可以捕获那些不能被左值引用的对象,而移动语义则允许更有效地转移资源所有权,例如在对象构造和赋值时避免不必要的拷贝。示例代码展示了如何在类中实现移动构造函数和移动赋值运算符,以及它们在实际操作中的应用。
摘要由CSDN通过智能技术生成

右值

传统 C++ 只能引用左值,例如:

int n;
int * pt = new int;
const int b = 101;     // can't assign to b, but &b is valid
int & rn = n;       // n identifies datum at address &n
int & rt = *pt;     // *pt identifies datum at address pt
const int & rb = b; // b identifies const datum at address &b

前面的这些变量,我们很容易理解到,能够取他们的地址。但是还有一些变量,我们在左值这个概念下无法获取到地址。

int & r1 = 13;
int & r2 = x + y;
double & r3 = std::sqrt (2.0) ;

上面的这种写法一定会报错,我们无法通过左值引用获取它的地址。这个时候引入了右值引用的概念,右值引用则可以获取到它们的地址。右值引用的方式如下:

int && r1 = 13;
int && r2 = x + y;
double && r3 = std::sqrt (2.0) ;

右值引用主要的能力是能够使用到临时内存区域。这个时候对高性能编程就有非常大的帮助了。对于高性能编程除了理解右值引用外,还需要理解移动。

移动

移动的概念在我看来就是把临时内存区域加以利用。与其说是移动,不如说是复制右值。还是以例子来看移动语义吧。下面的例子有点长,内容也有些分散。下面主要讲内存的分配情况。

// stdmove.cpp -- using std::move()
#include <iostream>
#include <utility>
// use the following for g++4.5
// #define nullptr 0
// interface
class Useless {
   private:
    int n;          // number of elements
    char *pc;       // pointer to data
    static int ct;  // number of objects
    void ShowObject() const;

   public:
    Useless();
    explicit Useless(int k);
    Useless(int k, char ch);
    Useless(const Useless &f);  // regular copy constructor
    Useless(Useless &&f);       // move constructor
    ~Useless();
    Useless operator+(const Useless &f) const;
    Useless &operator=(const Useless &f);  // copy assignment
    Useless &operator=(Useless &&f);       // move assignment
    void ShowData() const;
};

// implementation
int Useless::ct = 0;

Useless::Useless() {
    ++ct;
    n = 0;
    pc = nullptr;
}

Useless::Useless(int k) : n(k) {
    ++ct;
    pc = new char[n];
}

Useless::Useless(int k, char ch) : n(k) {
    ++ct;
    pc = new char[n];
    for (int i = 0; i < n; i++) pc[i] = ch;
}

Useless::Useless(const Useless &f) : n(f.n) {
    ++ct;
    pc = new char[n];
    for (int i = 0; i < n; i++) pc[i] = f.pc[i];
}

Useless::Useless(Useless &&f) : n(f.n) {
    ++ct;
    pc = f.pc;       // steal address
    f.pc = nullptr;  // give old object nothing in return
    f.n = 0;
}

Useless::~Useless() {
    delete[] pc;
}

Useless &Useless::operator=(const Useless &f)  // copy assignment
{
    std::cout << "copy assignment operator called:\n";
    if (this == &f) return *this;
    delete[] pc;
    n = f.n;
    pc = new char[n];
    for (int i = 0; i < n; i++) pc[i] = f.pc[i];
    return *this;
}

Useless &Useless::operator=(Useless &&f)  // move assignment
{
    std::cout << "move assignment operator called:\n";
    if (this == &f) return *this;
    delete[] pc;
    n = f.n;
    pc = f.pc;
    f.n = 0;
    f.pc = nullptr;
    return *this;
}

Useless Useless::operator+(const Useless &f) const {
    Useless temp = Useless(n + f.n);
    for (int i = 0; i < n; i++) temp.pc[i] = pc[i];
    for (int i = n; i < temp.n; i++) temp.pc[i] = f.pc[i - n];
    return temp;
}

void Useless::ShowObject() const {
    std::cout << "Number of elements: " << n;
    std::cout << " Data address: " << (void *)pc << std::endl;
}

void Useless::ShowData() const {
    if (n == 0)
        std::cout << "(object empty)";
    else
        for (int i = 0; i < n; i++) std::cout << pc[i];
    std::cout << std::endl;
}

// application
int main() {
    using std::cout;
    {
        Useless one(10, 'x');
        Useless two = one + one;  // calls move constructor
        cout << "object one: ";
        one.ShowData();
        cout << "object two: ";
        two.ShowData();
        Useless three, four;
        cout << "three = one\n";
        three = one;  // automatic copy assignment
        cout << "now object three = ";
        three.ShowData();
        cout << "and object one = ";
        one.ShowData();
        cout << "four = one + two\n";
        four = one + two;  // automatic move assignment
        cout << "now object four = ";
        four.ShowData();
        cout << "four = move(one)\n";
        four = std::move(one);  // forced move assignment
        cout << "now object four = ";
        four.ShowData();
        cout << "and object one = ";
        one.ShowData();
    }
    std::cin.get();
}

上面的例子是 C++ Primer Plus 里面的,从这个例子里,我们能够学习到很多知识。

在上述代码中的 112 行

Useless two = one + one;  // calls move constructor

通过增加日志我们会发现如下几种情况(MAC 上执行):

  1. 能够看到 two = one + one 执行了 Useless Useless::operator+(const Useless &f) 函数

  1. 这里的移动语义并没有真的执行到 Useless::Useless(Useless &&f) 而是将 Useless Useless::operator+(const Useless &f) 返回值 temp 的地址直接赋值给 two。

其中第 1 点很容易理解,需要注意的是,左边的 one 调用了 Useless Useless::operator+(const Useless &f) 语句,右边的 one 相当于 const Useless &f 传入进来。

第 2 点也可以认为是移动语义,但是这里并没有真的调用移动复制构造函数,而是直接将 temp 的地址赋值给 two。

  1. 我们看 four = one + two,这时调用了 Useless::Useless(Useless &&f)

  1. 然后 four = std::move(one),也是调用了移动语义 Useless::Useless(Useless &&f)

其他都相对容易理解,在这里就不再多做解释了。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

攻城狮-小文

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值