这一节主要讲的是 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 章会提到这个问题,届时就好理解多了。
这里就整理一点我的理解。
以前多少肯定会见过几次这个左值和右值的概念。但是看到 lvalue
和 rvalue
一时无措。
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.
其实就是常见的编程语言中的左边的值和右边的值嘛。