移动语义的简单示例

Posted jobgeo

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了移动语义的简单示例相关的知识,希望对你有一定的参考价值。

下面是一个使用移动语义的简单示例:

#include <iostream>
#include <string>
 
class MyString 
public:
    MyString() : m_data(nullptr), m_size(0) 
 
    MyString(const char* str) : MyString() 
        m_size = strlen(str);
        m_data = new char[m_size + 1];
        strcpy_s(m_data, m_size + 1, str);
    
 
    // 拷贝构造函数
    MyString(const MyString& other) : MyString() 
        if (other.m_data != nullptr) 
            m_size = other.m_size;
            m_data = new char[m_size + 1];
            strcpy_s(m_data, m_size + 1, other.m_data);
        
    
 
    // 移动构造函数
    MyString(MyString&& other) noexcept : m_data(other.m_data), m_size(other.m_size) 
        other.m_data = nullptr;
        other.m_size = 0;
    
 
    ~MyString()  delete[] m_data; 
 
    // 赋值运算符重载(拷贝赋值)
    MyString& operator=(const MyString& other) 
        if (this != &other) 
            delete[] m_data;
            m_size = other.m_size;
            m_data = new char[m_size + 1];
            strcpy_s(m_data, m_size + 1, other.m_data);
        
        return *this;
    
 
    // 赋值运算符重载(移动赋值)
    MyString& operator=(MyString&& other) noexcept 
        if (this != &other) 
            delete[] m_data;
            m_data = other.m_data;
            m_size = other.m_size;
            other.m_data = nullptr;
            other.m_size = 0;
        
        return *this;
    
 
    char* getData() const  return m_data; 
    size_t getSize() const  return m_size; 
 
private:
    char* m_data;
    size_t m_size;
;
 
int main() 
    MyString s1("Hello");
    MyString s2(std::move(s1)); // 调用移动构造函数
    std::cout << "s2: " << s2.getData() << std::endl; // 输出 "s2: Hello"
    std::cout << "s1: " << (s1.getData() == nullptr ? "null" : s1.getData()) << std::endl; // 输出 "s1: null"
 
    MyString s3("World");
    s2 = std::move(s3); // 调用移动赋值运算符重载
    std::cout << "s2: " << s2.getData() << std::endl; // 输出 "s2: World"
    std::cout << "s3: " << (s3.getData() == nullptr ? "null" : s3.getData()) << std::endl; // 输出 "s3: null"
 
    return 0;

在这个示例中,我们定义了一个MyString类来管理字符串数据。该类有一个成员变量m_data指向字符串数据的内存空间,以及一个成员变量m_size表示字符串的长度。

为了支持移动语义,在类中定义了移动构造函数和移动赋值运算符重载函数。在移动构造函数中,我们将原有对象的资源所有权移交给新的对象,并将原有对象的m_data和m_size设为nullptr和0,以确保它们不会被析构函数释放。在移动赋值运算符重载函数中,我们先删除当前对象的指针成员变量,然后将右值对象的指针数据复制到当前对象中,再将右值对象的指针成员变量设为nullptr,以确保当前对象和右值对象独立管理各自的资源。

在main函数中,我们首先创建了一个MyString对象s1,然后通过调用std::move将其转移给另一个MyString对象s2。由于调用了移动构造函数,s2获得了s1的资源所有权,并将s1的指针成员变量设为nullptr。接着,我们通过调用std::move将另一个MyString对象s3的资源所有权转移到s2中,此时s2包含的字符串数据为"World"。最后,我们输出s1和s3的指针成员变量,可以看到它们都已经被设为nullptr。

通过这个示例,我们可以看到移动语义的使用方式,以及通过移动构造函数和移动赋值运算符重载实现资源所有权转移的方法。

尝试使用移动语义创建线程保护

【中文标题】尝试使用移动语义创建线程保护【英文标题】:Attempting to create a threadguard with move semantics 【发布时间】:2020-08-06 20:49:34 【问题描述】:

标题很容易解释。我正在尝试获得一个线程保护的最小工作示例,它还可以支持 std::threads 具有的移动语义。

#include <iostream>
#include <thread>
#include <vector>
#include <functional>

class ThreadGuard 
public:
    explicit ThreadGuard(std::thread input): t(std::move(input))
    
    ~ThreadGuard()
        if(t.joinable())
            t.join();
        
    
    ThreadGuard(ThreadGuard const& t) = delete;
    ThreadGuard& operator=(ThreadGuard const&) = delete;

    ThreadGuard& operator=(ThreadGuard&& out)
        this->t = out.transfer();
        return *this;
    
    std::thread transfer()
        return std::move(t);
    
private:
    std::thread t;
;

void doWork(std::string input)
    std::cout << input << std::endl;


static const auto numThreads = 4;
int main()

    std::vector<ThreadGuard> tp;
    tp.reserve(numThreads);
    for(auto i = 0 ; i < numThreads; ++i)
        tp[i] = ThreadGuard(std::thread(doWork, i));
    
    return 0;

目前遇到了障碍。 std::invoke,没有找到匹配的重载函数,我看不到这里缺少什么。

【问题讨论】:

【参考方案1】:

您需要将int 转换为std::string

tp[i] = ThreadGuard(std::thread(doWork, std::to_string(i)));

您也不需要编写自己的移动构造函数和移动赋值运算符。使用default

class ThreadGuard 
public:
    explicit ThreadGuard(std::thread&& input): t(std::move(input))
    
    ThreadGuard(ThreadGuard const& t) = delete;
    ThreadGuard(ThreadGuard&&) noexcept = default;
    ThreadGuard& operator=(ThreadGuard const&) = delete;
    ThreadGuard& operator=(ThreadGuard&&) noexcept = default;  
    ~ThreadGuard()
        if(t.joinable())
            t.join();
        
    

private:
    std::thread t;
;

您还可以让转换构造函数接受线程构造函数参数并直接转发它们:

    template<typename...Args>
    explicit ThreadGuard(Args&&... args): t(std::forward<Args>(args)...)
    

这将允许它像这样创建:

tp[i] = ThreadGuard(doWork, std::to_string(i));

另外值得注意的是:在 C++20 中添加了 std::jthread,而 join()s 在销毁时自动添加。

【讨论】:

ThreadGuard&amp; operator=(ThreadGuard&amp;&amp;) noexcept = default; 我觉得不对,你自己写吧。您需要检查this-&gt;t 是否可加入(并加入),然后再覆盖它。 @MikeVine 在这种情况下,默认行为(OP 也有)将调用std::terminate。它可能是也可能不是 OP 想要的,但我只是在答案中保持了相同的逻辑。 是的 OP 也有这个错误 (?)。这将是一个 非常 奇怪的类,在这种情况下不会自行清理。它很容易修复 - 或者如果你真的想在那种情况下终止,那么明确似乎是正确的。 @MikeVine 我想说的是品味问题。这个瘦包装器的行为与std::thread 一样,只是它在销毁时自动加入。另一方面,std::jthread 有一个更智能的移动赋值运算符,它调用request_stop(),然后调用join()。对于类似于使用std::thread 的东西,需要添加一个需要检查的原子。只是join()ing 可能会导致各种麻烦。 1+ 以获得完整答案。

以上是关于移动语义的简单示例的主要内容,如果未能解决你的问题,请参考以下文章

c++ 移动语义及使用(续)

cpp►C++11右值引用移动语义移动构造函数移动赋值运算符

cpp►C++11右值引用移动语义移动构造函数移动赋值运算符

当基类提供时,派生类是不是需要实现移动语义?

c++11的右值引用移动语义

使用移动语义在构造函数中初始化类成员