繁体   English   中英

使用 PIMPL 惯用法为可复制类编写可复制的 unique_ptr

[英]Writing a copyable unique_ptr for copyable classes with PIMPL idiom

我想编写一个使用 PIMPL 成语的类,但可以实现以下功能:

int main() {
    MyClass myclass {12};
    std::string val = myclass.get_value();
    std::cout << val << "\n";

    MyClass copy = myclass;
    val = copy.get_value();

    return 0;
}

也就是说,在使用 PIMPL 时提供复制功能。 使用std::unique_ptr实现 PIMPL 会导致预期的编译错误:

error C2280: 'MyClass::MyClass(const MyClass &)': attempting to reference a deleted function

这是我的尝试:

类.hpp

#pragma once

#include <string>

#include "copyable_unique_ptr.hpp"

class MyClass {
private:
    struct MyClassPrivate;
    copyable_unique_ptr<MyClassPrivate> _impl;

public:
    MyClass(int value);
    ~MyClass();

    std::string get_value();
};

类.cpp

#include "class.hpp"

#include <string>

struct MyClass::MyClassPrivate {
    int value;
};

MyClass::MyClass(int value) 
    : _impl(std::make_unique<MyClassPrivate>()) {
    _impl->value = value;
}

MyClass::~MyClass() = default;

std::string MyClass::get_value() {
    return std::to_string(_impl->value);
}

copyable_unique_ptr.hpp

#pragma once

#include <memory>

template <typename T>
class copyable_unique_ptr {
private:
    std::unique_ptr<T> _ptr;

public:
    copyable_unique_ptr(std::unique_ptr<T> &&ptr)
        : _ptr(std::move(ptr)) {}

    ~copyable_unique_ptr() = default;

    copyable_unique_ptr(const copyable_unique_ptr<T> &other) {
        _ptr = std::make_unique<T>(*other._ptr);
    }

    copyable_unique_ptr(copyable_unique_ptr<T> &&other) {
         _ptr = std::move(other._ptr);
    }

    copyable_unique_ptr &operator=(const copyable_unique_ptr<T> &other) {
        _ptr = std::make_unique<T>(*other._ptr);
    }

    copyable_unique_ptr &operator=(copyable_unique_ptr<T> &&other) {
         _ptr = std::move(other._ptr);
    }

    T *operator->() {
        return _ptr.get();
    }
};

这会导致以下错误: warning C4150: deletion of pointer to incomplete type 'MyClass::MyClassPrivate'; no destructor called warning C4150: deletion of pointer to incomplete type 'MyClass::MyClassPrivate'; no destructor called

我在第一次尝试使用 unique_ptr 时也遇到了这个错误,解决方案是将析构函数移动到 class.cpp 中: MyClass::~MyClass() = default; .

在使用copyable_unique_ptr方法时,我真的不知道如何解决这个问题。

我在 SO 上看到了这个问题。 他们试图和我做同样的事情,但对答案的最后一条评论表明他们遇到了完全相同的问题。

虽然你可以在这里编写自定义智能指针,但我总是喜欢尽可能多地重用代码。 似乎您想要一个动态创建的对象,它具有复制语义而不是指针语义 出于这个原因,我会毫不犹豫地将您的copyable_unique_ptr称为指针 我确实为此编写了一个类,并决定使用dynamic_object<T>类的东西。

我会使用只包含一个元素的std::vector来代替指针。 有点像这样:

class MyClass {
private:
    struct MyClassPrivate;
    std::vector<MyClassPrivate> _impl;

public:
    MyClass(int value);
    ~MyClass();

    std::string get_value();
};

struct MyClass::MyClassPrivate {
    int value;
};

MyClass::MyClass(int value)
    : _impl(1) {
    _impl.front().value = value;
}

MyClass::~MyClass() = default;

std::string MyClass::get_value() {
    return std::to_string(_impl.front().value);
}

但是,如果您真的想创建您的copyable_unique_ptr我仍然在这里使用std::vector来简化其编码并重用高质量的标准库代码:

也许有点像这样:

template<typename T>
class copyable_unique_ptr
: private std::vector<T>
{
    using base_type = std::vector<T>;

public:
    copyable_unique_ptr(std::unique_ptr<T>&& ptr)
    : base_type(std::move(*ptr), 1) {}

    copyable_unique_ptr(std::unique_ptr<T> const& ptr)
    : base_type(*ptr, 1) {}

    T& operator*()  { return base_type::back(); }
    T* operator->() { return base_type::data(); }

    T const& operator*()  const { return base_type::back(); }
    T const* operator->() const { return base_type::data(); }
};

Sam Varshavchik 在评论中给出了一个提示,将复制构造函数和赋值运算符移到实现中修复了我试图编写的代码,这意味着:

类.hpp

#pragma once

#include <string>

#include "copyable_unique_ptr.hpp"

class MyClass {
private:
    struct MyClassPrivate;
    copyable_unique_ptr<MyClassPrivate> _impl;

public:
    MyClass(int value);

    MyClass(const MyClass &);
    MyClass(MyClass &&);
    MyClass &operator=(const MyClass &);
    MyClass &operator=(MyClass &&);
    ~MyClass();

    std::string get_value();
};

类.cpp

#include "class.hpp"

#include <string>
#include <iostream>

struct MyClass::MyClassPrivate {
    int value;
};

MyClass::MyClass(int value)
    : _impl(std::make_unique<MyClassPrivate>()) {
    _impl->value = value;
}

MyClass::MyClass(const MyClass &) = default;
MyClass::MyClass(MyClass &&) = default;
MyClass &MyClass::operator=(const MyClass &) = default;
MyClass &MyClass::operator=(MyClass &&) = default;
MyClass::~MyClass() = default;


std::string MyClass::get_value() {
    std::cout << &_impl << "\n";
    return std::to_string(_impl->value);
}

它按预期工作。

另一方面,Galik 给出了一个答案,它提供了一个更好、更简单的copyable_unique_ptr替代方案。 我更喜欢用他的方式。

坚持使用普通的unique_ptr并在您的 cpp 文件中实现MyClass复制可能更简单:

class MyClass {
private:
    struct MyClassPrivate;
    std::unique_ptr<MyClassPrivate> _impl;

public:
    MyClass(int value);
    MyClass(MyClass&&);
    MyClass(const MyClass&);
    ~MyClass();

    MyClass& operator = (const MyClass&);
    MyClass& operator = (MyClass&&);

    std::string get_value();
};

MyClass::MyClass(const MyClass& other)
    : _impl(std::make_unique<MyClassPrivate>(*other._impl)) {
}

MyClass& MyClass::operator = (const MyClass& other) {
    _impl = std::make_unique<MyClassPrivate>(*other._impl);
    return *this;
}

MyClass::~MyClass() = default;
MyClass::MyClass(MyClass&&) = default;
MyClass& MyClass::operator = (MyClass&&) = default;

此代码(和您的原始代码)存在一个小问题,因为从对象移动的对象将具有 null _impl您可能希望添加对 null 的检查,或者您可以替换为默认构造的对象而不是空指针,但是在大多数情况下可能是一种浪费?

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM