-1

尝试使用复制/移动 ctors/赋值运算符和 dtor(五规则)的实现来解决与抽象基类相关的错误C2248 ,并且出现了一些问题:

1) 为什么在自动处理 unique_ptr 数据成员时适用主要与 dtor 相关的 5 规则?dtor 实现应该留空正确,因为一旦它们的所有者超出范围,unique_ptrs 就会自动销毁?

2) 假设另一个类具有相同类型向量的 std::unique_ptr 类型成员。为了让这个类是可复制的,它必须有一个复制 ctor 和复制赋值运算符来克隆 unique_ptr 数据成员?我已经看到了这个解决方案,但似乎原始发帖人只是切换到 shared_ptr 只是为了消除错误而很少考虑所有权管理。这是正确的策略吗?

3) 考虑与上述问题 2 有关 unique_ptr 向量的相同情况。dtor 是否应该包括对 clear() 向量的调用?

4) Derived1 的赋值运算符不正确。但是基类应该具有复制和移动赋值运算符,因为它具有复制/移动 ctor(4/5 规则)。这些实际上不能在类之外使用,因为它是抽象的,因此不会分配实例。但是我如何利用派生类中的这段代码呢?每个派生类都需要能够移动/复制基本数据成员及其自己的数据成员。我不知道该怎么办。

    #include <algorithm>
#include <memory>
#include <vector>
#include <iostream>

class Base{

public:
    Base() : m_subBases(){};

    /*  copy ctor */
    Base(const Base& other) : m_subBases(){
        *this = other;
    };

    /*  move ctor */
    Base(Base&& other) : m_subBases(){
        *this =std::move( other);
    };

    /*  Move assignment operator*/
    Base& operator=(Base&& other){
        m_subBases = std::move(other.m_subBases);
        return *this;
    };

    /*  Copy assignment operator */
    Base& operator=(const Base& other){
        for(int i = 0; i < other.m_subBases.size(); i++)
            m_subBases.push_back(other.m_subBases[i]->clone());

        return *this;
    };

    /* virtual dtor */
    virtual ~Base(){
        m_subBases.clear();
    };

    /* Used for creating clones of unique_ptrs */
    virtual std::unique_ptr <Base> clone() const= 0;

    /* Do something */
    virtual void execute(float f) = 0;

    //Omitted data member access methods

protected:
    std::vector < std::unique_ptr <Base> > m_subBases;
};

class Derived1 : public Base{

public:
    Derived1() :  Base(){};

    /*  copy ctor */
    Derived1(const Derived1& other) : Base(other){
        *this = other;
    };

    /*  move ctor */
    Derived1(Derived1&& other) : Base(std::move(other)){
        *this = std::move(other);
    };

    /*  Move assignment operator*/
    Derived1& operator=(Derived1&& other){

        //This is redundant when called in the move ctor because
        // of the call to Base(std::move(other))
        m_subBases = std::move(other.m_subBases);

        m_string = other.m_string;
        return *this;
    };

    /*  Copy assignment operator */
    Derived1& operator=( const Derived1& other){

        //This is redundant when called in the copy ctor because
        // of the call to Base(other)
        for(int i = 0; i < other.m_subBases.size(); i++)
            m_subBases.push_back(other.m_subBases[i]->clone());

        m_string = other.m_string;
        return *this;
    };

    /* virtual dtor */
    virtual ~Derived1(){};

    /* Used for creating clones of unique_ptrs */
    virtual std::unique_ptr <Base> clone() const{
        return std::unique_ptr <Base> (new Derived1(*this));
    };

    virtual void execute(float f){
        std::cout << "Derived1 " << f << std::endl; 
    };
protected:

    std::string m_string;
};
4

1 回答 1

2

我想提供一种替代方法。正如@Tony The Lion 已经建议的那样,不是可怕的五规则,而是令人愉快的零规则。我的提议的完整实现已经由几个人编码,@R 中有一个很好的版本。Martinho Fernandes 的图书馆,但我将提供一个简化版本。

首先,让我们回顾一下:

零规则:不要编写复制或移动构造函数、复制或移动赋值运算符或析构函数。相反,组成您的组件类,这些组件处理单一职责并封装相关单个资源的所需行为。

有一个明显的警告:当你设计单一职责类时,你当然必须遵守:

五法则:如果您编写复制或移动构造函数、复制或移动赋值运算符或析构函数中的任何一种,则必须实现所有五种。(但这条规则需要的“五个”函数实际上是:Destructor、Copy-Const、Move-Const、Assignment 和 Swap。)

我们开始做吧。首先,您的消费者:

struct X;

struct Base
{
    std::vector<value_ptr<X>> v;
};

struct Derived : Base
{
};

请注意两者BaseDerived遵守零规则!

我们需要做的就是实施value_ptr。如果指针对象是非多态的,则执行以下操作:

template <typename T>
class value_ptr
{
    T * ptr;
public:
    // Constructors
    constexpr value_ptr()      noexcept : ptr(nullptr) { }
    constexpr value_ptr(T * p) noexcept : ptr(p)       { }

    // Rule of Five begins here:
    ~value_ptr() { ::delete ptr; }
    value_ptr(value_ptr const & rhs) : ptr(rhs.ptr ? ::new T(*rhs.ptr) : nullptr) { }
    value_ptr(value_ptr && rhs) noexcept : ptr(rhs.ptr) { rhs.ptr = nullptr; }
    value_ptr & operator=(value_ptr rhs) { swap(rhs); return *this; }
    void swap(value_ptr & rhs) noexcept { std::swap(rhs.ptr, ptr); }

    // Pointer stuff
    T & operator*() const noexcept { return *ptr; }
    T * operator->() const noexcept { return ptr; }
};

template <typename T, typename ...Args>
value_ptr<T> make_value(Args &&... args)
{
    return value_ptr<T>(::new T(std::forward<Args>(args)...));
}

如果您想要处理多态基类指针的智能指针,我建议您要求您的基类提供一个虚clone()函数,并实现 a clone_ptr<T>,其复制构造函数如下所示:

clone_ptr(clone_ptr const & rhs) : ptr(rhs.ptr ? rhs.ptr->clone() : nullptr) { }
于 2012-09-18T16:33:09.253 回答