右值引用和移动构造函数
右值引用
C++11 中的一个新特性,用于实现移动语义和完美转发。右值引用用 `&&` 表示,可以绑定到临时对象或将要被销毁的对象,是一种可修改的引用类型。
右值引用的主要作用有两个:
1. 实现移动语义:右值引用可以将资源所有权从一个对象转移到另一个对象,避免了不必要的内存分配和复制,提高了程序的性能和效率。
2. 实现完美转发:右值引用可以将参数按照原始类型精确地转发到下一层函数,避免了类型退化和多余的拷贝构造函数调用,提高了程序的灵活性和可维护性。
以下是一个简单的右值引用的例子,用于实现移动语义:
```cpp
#include <iostream>
#include <vector>
class MyVector {
public:
MyVector() {
std::cout << "Default constructor" << std::endl;
}
MyVector(const MyVector& other) {
std::cout << "Copy constructor" << std::endl;
}
MyVector(MyVector&& other) {
std::cout << "Move constructor" << std::endl;
}
~MyVector() {
std::cout << "Destructor" << std::endl;
}
};
MyVector createVector() {
MyVector v;
return v; // 返回一个临时对象
}
int main() {
std::vector<MyVector> vec;
vec.push_back(MyVector()); // 调用默认构造函数和移动构造函数
MyVector v = createVector(); // 调用默认构造函数和移动构造函数
return 0;
}
```
在上面的代码中,我们定义了一个 `MyVector` 类,用于演示移动语义。在类中,我们定义了默认构造函数、拷贝构造函数和移动构造函数,并在每个构造函数中输出一条提示信息。在 `createVector` 函数中,我们创建了一个临时对象并返回。在 `main` 函数中,我们创建了一个 `std::vector` 容器,并向其中添加一个临时对象和一个从 `createVector` 函数中返回的对象。在程序运行时,我们可以看到输出的提示信息,其中有两次移动构造函数的调用,分别对应于临时对象和从 `createVector` 函数中返回的对象的转移。
通过使用右值引用,我们可以实现移动语义,避免了不必要的拷贝构造函数调用,提高了程序的性能和效率。
移动构造函数
移动构造函数是 C++11 中的一个新特性,用于实现移动语义。移动构造函数的作用是将一个对象的资源(如内存、文件句柄、网络连接等)所有权从一个对象转移到另一个对象,避免了不必要的内存分配和复制,提高了程序的性能和效率。
移动构造函数的特点有:
1. 接受一个右值引用作为参数,表示将要被移动的对象。
2. 将被移动对象的资源所有权转移到新对象中,使用移动语义。
3. 将被移动对象的指针置为 `nullptr`,避免重复释放。
以下是一个简单的移动构造函数的例子,用于演示移动语义:
```cpp
#include <iostream>
class MyString {
public:
MyString() {
std::cout << "Default constructor" << std::endl;
data_ = nullptr;
size_ = 0;
}
MyString(const char* str) {
std::cout << "Constructor with const char*" << std::endl;
size_ = std::strlen(str);
data_ = new char[size_ + 1];
std::strcpy(data_, str);
}
MyString(const MyString& other) {
std::cout << "Copy constructor" << std::endl;
size_ = other.size_;
data_ = new char[size_ + 1];
std::strcpy(data_, other.data_);
}
MyString(MyString&& other) {
std::cout << "Move constructor" << std::endl;
size_ = other.size_;
data_ = other.data_;
other.size_ = 0;
other.data_ = nullptr;
}
~MyString() {
std::cout << "Destructor" << std::endl;
delete[] data_;
}
private:
char* data_;
size_t size_;
};
MyString createString() {
MyString s("Hello, world!");
return s; // 返回一个临时对象
}
int main() {
MyString s1("Hello, world!"); // 调用构造函数
MyString s2(s1); // 调用拷贝构造函数
MyString s3(std::move(s1)); // 调用移动构造函数
MyString s4 = createString(); // 调用移动构造函数
return 0;
}
```
在上面的代码中,我们定义了一个 `MyString` 类,用于演示移动构造函数。在类中,我们定义了默认构造函数、构造函数(接受 `const char*` 类型的参数)、拷贝构造函数和移动构造函数,并在每个构造函数和析构函数中输出一条提示信息。在 `createString` 函数中,我们创建了一个临时对象并返回。在 `main` 函数中,我们创建了四个 `MyString` 对象,分别调用了默认构造函数、构造函数、拷贝构造函数和移动构造函数,以及从 `createString` 函数中返回的对象的移动构造函数。在程序运行时,我们可以看到输出的提示信息,其中有三次构造函数的调用和两次移动构造函数的调用,分别对应于对象的创建和转移。