您的位置:

快速实现对象的资源转移——C++移动构造函数

一、什么是移动构造函数

在C++11中,增加了移动语义这一概念,移动语义可以将资源所有权从一个对象转移到另一个对象,从而避免资源的不必要拷贝和销毁。这就是移动构造函数的作用,它是一种特殊的构造函数,它接收的参数类型为右值引用,可以将右值引用所绑定的对象的资源所有权转移到新构造的对象上,同时将右值引用所绑定的对象变为无效状态。


class MyString {
public:
    MyString() : data(nullptr), len(0) {}
    MyString(char* str) { // 普通构造函数
        len = strlen(str);
        data = new char[len + 1];
        strcpy(data, str);
    }
    MyString(MyString&& other) { // 移动构造函数
        len = other.len;
        data = other.data;
        other.len = 0;
        other.data = nullptr;
    }
private:
    char* data;
    size_t len;
};

在上面的代码中,我们定义了一个MyString类,其中包含了一个普通构造函数和一个移动构造函数。移动构造函数使用了右值引用类型MyString&& other作为参数,当对象被移动构造时,将传入的MyString对象的数据和长度成员变量直接复制给新对象,如果构造成功,传入的对象变为无效状态。

二、为什么需要移动构造函数

在C++中,对象的构造和销毁时需要进行内存的分配和释放,这是比较耗费时间和资源的操作,因此在对象需要被复制或赋值时,或者作为函数返回值返回时,都会调用到对象的构造函数和拷贝构造函数。这就导致了一个问题,如果对象内存较大,频繁的进行构造和拷贝会导致内存和时间的浪费。

移动构造函数的出现就是为了解决这个问题,通过移动构造函数,可以避免不必要的复制和销毁操作,从而提高程序的效率和性能。

三、移动构造函数的应用

1. 函数返回值

移动构造函数可以用于返回值优化(Return Value Optimization,RVO),RVO是一种编译器优化策略,可以避免通过拷贝构造函数返回一个临时对象,直接在调用函数栈上创建对象,在函数内部直接使用。


MyString func() {
    char* temp = new char[10];
    strcpy(temp, "hello");
    return MyString(temp);
}

在上述代码中,函数func返回一个MyString对象,由于该对象是通过传入临时字符串指针构造的,因此可以使用移动构造函数避免拷贝操作。

2. 容器元素类型

移动构造函数可以用于STL容器元素类型的构造和赋值,例如std::vector、std::list等容器的insert、emplace_back等操作。


std::vector
    vec;
MyString str1("hello");
vec.push_back(str1); // 这里会调用拷贝构造函数,将str1复制到vector尾部
MyString str2("world");
vec.push_back(std::move(str2)); // 这里调用移动构造函数,将str2的资源转移至vector尾部

   

在上述代码中,第一个push_back操作会调用拷贝构造函数将str1对象复制到vector尾部,第二个push_back操作调用移动构造函数将str2对象的资源转移至vector尾部,这样就可以避免不必要的内存和时间浪费。

3. RAII资源管理类

移动构造函数可以用于RAII(Resource Acquisition Is Initialization)资源管理类的实现中,RAII是一种C++中常用的资源管理技术,可以保证当对象销毁时资源被正确释放。


class File {
public:
    File(const char* filepath) : m_fileptr(fopen(filepath, "r")) {}
    File(File&& other) : m_fileptr(other.m_fileptr) {
        other.m_fileptr = nullptr;
    }
    ~File() {
        if (m_fileptr) {
            fclose(m_fileptr);
        }
    }
private:
    FILE* m_fileptr;
};

在上述代码中,我们定义了一个RAII资源管理类File,它包含了文件指针m_fileptr和构造函数、移动构造函数、析构函数。在对象的构造函数中,我们打开了文件,并将文件指针保存在m_fileptr中,在对象销毁时,析构函数中将调用fclose函数释放文件资源,在移动构造函数中,我们将传入的右值引用对象的文件指针移交给当前对象,并将传入的对象变为无效状态。

四、总结

移动构造函数是C++11中的一个有用的特性,可以避免不必要的资源拷贝和销毁,从而提高程序的效率和性能。移动构造函数可以用于函数返回值、容器元素类型和RAII资源管理类等场景中,是C++开发中不可或缺的一部分。