整体问题

这一节主要讲的是 Moving Containers。其实就是在复制的时候,不是复制,而是移动。这样,就省却了复制的时间和空间操作。

他这里我看是看明白了,但是,我需要一个具体的例子来加深我的理解。

对,我需要一个可以跑起来的例子,不然总觉得不踏实,不会用。

下面就具体地把书里代码补充完整并进行相应的测试。

#include "../headers/bookheaders.h"

using namespace std;

class Vector {
   private:
    double* elem;  // elem points to an array of sz doubles
    int sz;

   public:
    Vector(int s);  // constructor: establish invariant, acquire resources
    ~Vector();      // destructor: release resources

    Vector(const Vector& a);             // copy constructor
    Vector& operator=(const Vector& a);  // copy assignment

    Vector(Vector&& a);             // move constructor
    Vector& operator=(Vector&& a);  // move assignment

    double& operator[](int i);
    const double& operator[](int i) const;

    int size() const;
};

Vector::Vector(int s) : elem(new double[s]), sz(s) {
    // constructor: establish invariant, acquire resources
    // Here, we dynamically allocate an array of doubles of size s using new.
    // The pointer to the allocated memory is stored in elem.
    // We also store the size s in the sz member variable.
}

Vector::~Vector() {
    // destructor: release resources
    // We need to deallocate the memory allocated by new[] in the constructor
    // using delete[] to avoid memory leaks.
    delete[] elem;
}

Vector::Vector(const Vector& a) : elem(new double[a.sz]), sz(a.sz) {
    // copy constructor
    // It creates a new Vector object that is a copy of an existing Vector object 'a'.
    // We allocate new memory for the elements of the new object using new[],
    // and then copy the elements from 'a' to the new object.
    for (int i = 0; i < sz; i++) {
        elem[i] = a.elem[i];
    }
}

Vector& Vector::operator=(const Vector& a) {
    // copy assignment
    // It assigns the values of one Vector object 'a' to another Vector object.
    // First, we check for self-assignment to avoid issues with deallocation
    // of existing memory. Then, we allocate new memory for the elements,
    // copy the elements from 'a', and deallocate the old memory.
    if (this == &a) {
        return *this;  // self-assignment, no work needed
    }

    delete[] elem;  // deallocate old memory

    elem = new double[a.sz];  // allocate new memory
    sz = a.sz;                // update size

    for (int i = 0; i < sz; i++) {
        elem[i] = a.elem[i];  // copy elements
    }

    return *this;
}

Vector::Vector(Vector&& a) : elem(a.elem), sz(a.sz) {
    // move constructor
    // It creates a new Vector object by "stealing" the resources (memory) from
    // an existing Vector object 'a'. We simply transfer the ownership of the
    // memory pointed by 'a.elem' to the new object, and update the size.
    // 'a' will be left in a valid but unspecified state (its destructor can still
    // be safely called to release any remaining resources).
    a.elem = nullptr;  // set the source object's pointer to nullptr
    a.sz = 0;          // update the source object's size
}

Vector& Vector::operator=(Vector&& a) {
    // move assignment
    // It assigns the values of one Vector object 'a' to another Vector object by
    // "stealing" the resources (memory) from 'a'. We first check for self-assignment,
    // deallocate the old memory, transfer ownership of the memory from 'a' to the
    // new object, and update the size. 'a' will be left in a valid but unspecified state.
    if (this == &a) {
        return *this;  // self-assignment, no work needed
    }

    delete[] elem;  // deallocate old memory

    elem = a.elem;  // transfer ownership of memory
    sz = a.sz;      // update size

    a.elem = nullptr;  // set the source object's pointer to nullptr
    a.sz = 0;          // update the source object's size

    return *this;
}

double& Vector::operator[](int i) {
    // element access: subscript operator for non-const objects
    // It returns a reference to the element at index i, allowing it to be
    // modified if the Vector object is not const.
    return elem[i];
}

const double& Vector::operator[](int i) const {
    // element access: subscript operator for const objects
    // It returns a const reference to the element at index i, ensuring that
    // the Vector object is not modified if it is const.
    return elem[i];
}

int Vector::size() const {
    // returns the size of the Vector object
    return sz;
}

int main(int argc, char const* argv[]) {
    // Create a Vector object of size 3
    Vector v(3);

    // Set values using subscript operator
    v[0] = 1.0;
    v[1] = 2.0;
    v[2] = 3.0;

    // Print the values using subscript operator and size()
    std::cout << "Vector v: ";
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << " ";
    }
    std::cout << std::endl;

    // Create a new Vector object by copying v
    Vector v2 = v;

    // Modify v2
    v2[1] = 5.0;

    // Print v and v2
    std::cout << "Vector v: ";
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << " ";
    }
    std::cout << std::endl;

    std::cout << "Vector v2: ";
    for (int i = 0; i < v2.size(); i++) {
        std::cout << v2[i] << " ";
    }
    std::cout << std::endl;

    // Create a new Vector object by moving v
    Vector v3 = std::move(v);

    // Print v and v3
    // 经过 move 之后,v 本身就被消除了
    std::cout << "Vector v: ";
    for (int i = 0; i < v.size(); i++) {
        std::cout << v[i] << " ";
    }
    std::cout << std::endl;

    std::cout << "Vector v3: ";
    for (int i = 0; i < v3.size(); i++) {
        std::cout << v3[i] << " ";
    }
    std::cout << std::endl;

    return 0;
}

运行结果,

Vector v: 1 2 3
Vector v: 1 2 3
Vector v2: 1 5 3
Vector v:
Vector v3: 1 2 3

按:这里就讲一点我的想法。关于技术书,我觉得最好还是要给出完整的可运行的代码。这样可能会增加篇幅,不过,我们也可以在书本的示例代码之外再给出一个整体的代码仓库嘛。

在这里,经过实验,就会理解移动语义(move semantic)的含义了,后面第 5 章会提到这个问题,届时就好理解多了。

左值和右值的问题

这里就整理一点我的理解。

以前多少肯定会见过几次这个左值和右值的概念。但是看到 lvaluervalue 一时无措。

The word “rvalue” is intended to complement “lvalue,” which roughly means “something that can appear on the left-hand side of an assignment.” So an rvalue is – to a first approximation – a value that you can’t assign to, such as an integer returned by a function call, and an rvalue reference is a reference to something that nobody else can assign to.

其实就是常见的编程语言中的左边的值和右边的值嘛。