C++11移动语义

C++ 11 Move Semantics

本文关键字:语义 移动 C++11      更新时间:2023-10-16

我正在努力理解C++11移动语义是如何工作的。我已经实现了一个类,它封装了一个指向String对象的指针,但移动构造函数和移动赋值运算符都没有按预期调用。

我通过Eclipse CDT:使用GCC 4.7.2

你能帮我理解原因吗?

#include <iostream>
#include <string>
#include <utility>
using namespace std;
class StringPointerWrapper {
public:
    // Default constructor with default value
    StringPointerWrapper(const std::string& s = "Empty"): ps(new std::string(s)) {
        std::cout << "Default constructor: " << *ps << std::endl;
    }
    //Copy constructor
    StringPointerWrapper(const StringPointerWrapper& other): ps(new std::string(*other.ps)) {
        std::cout << "Copy constructor: " << *other.ps << std::endl;
    }
    //Copy assignment operator
    StringPointerWrapper& operator=(StringPointerWrapper other) {
        std::cout << "Assignment operator (ref): " << *other.ps << std::endl;
        swap(ps, other.ps);
        return *this;
    }

    //Alternate copy assignment operator
    /*StringPointerWrapper& operator=(StringPointerWrapper& other) {
        std::cout << "Assignment operator (val)" << std::endl;
        //We need to do the copy by ourself
        StringPointerWrapper temp(other);
        swap(ps, temp.ps);
        return *this;
    }*/
    //Move constructor
    StringPointerWrapper(StringPointerWrapper&& other) noexcept : ps(nullptr) {
        std::cout << "Move constructor: " << *other.ps << std::endl;
        ps = other.ps;
        other.ps = nullptr;
    }
    //Move assignment operator
    StringPointerWrapper& operator= (StringPointerWrapper&& other) noexcept {
        std::cout << "Move assignment operator: " << *other.ps << std::endl;
        if(this != &other) {
            delete ps;
            ps = other.ps;
            other.ps = nullptr;
        }
        return *this;
    }

    //Destructor
    ~StringPointerWrapper() {
        std::cout << "Destroying: " << *this << std::endl;
        delete ps;
    }
private:
        friend std::ostream& operator<<(std::ostream& os, StringPointerWrapper& spw) {
            os << *spw.ps;
            return os;
        }
        std::string *ps;
};

int main(int argc, char *argv[]) {
    StringPointerWrapper spw1("This is a string");
    StringPointerWrapper spw2;
    StringPointerWrapper spw3("This is another string");
    StringPointerWrapper spw4 = {"This is a const string"};
    StringPointerWrapper spw5(StringPointerWrapper("String for move constructor"));
    std::cout << "spw2 before: " << spw2 << std::endl;
    spw2 = spw3;
    std::cout << "spw2 after: " << spw2 << std::endl;
    StringPointerWrapper spw6 = StringPointerWrapper("String for move assignment");
    std::cout << spw1 << std::endl;
    std::cout << spw2 << std::endl;
    std::cout << spw3 << std::endl;
    std::cout << spw4 << std::endl;
    std::cout << spw5 << std::endl;
    std::cout << spw6 << std::endl;
}

move构造函数没有被调用,因为编译器将省略构造函数作为优化。如果在编译器调用中传递-fno-elide-constructors,则可以禁用它。

但是,您会遇到问题,因为您的move构造函数只是使用other中的指针,该指针很快就会被删除。将std::string作为指针是没有意义的,您应该直接持有它,并在移动赋值运算符和移动构造函数中调用std::move

StringPointerWrapper spw5(StringPointerWrapper("移动构造函数的字符串"));

这会调用默认构造函数,因为编译器决定通过不创建临时构造函数来优化它(即,它决定执行StringPointerWrapper spw5("String for move constructor"))。相反,通过执行StringPointerWrapper spw5(std::move(StringPointerWrapper("String for move constructor")));来强制移动。

StringPointerWrapper spw6=StringPointerWrapper("用于移动赋值的字符串");

同样,编译器通过优化临时构造函数的创建来调用默认构造函数。

注意:您的operator<<需要防范指针。例如,

friend std::ostream& operator<<(std::ostream& os, StringPointerWrapper& spw) {
    if (spw.ps)
         os << (spw.ps);
        else
            os << "null";
    return os;
 }

您需要告诉编译器自己使用std::move 移动东西

尝试:

 StringPointerWrapper spw5(std::move(StringPointerWrapper("String for move constructor")));