【问题标题】:C++ Decorator added to std::vectorC++ 装饰器添加到 std::vector
【发布时间】:2014-04-10 13:31:08
【问题描述】:

我有一个记录的基类,并且想要使用装饰器添加额外的字段和比较函数,并且能够链接装饰器(记录可以有电子邮件,或出生日期,或两者兼有,或没有) .我也会有很多这样的装饰器;每个附加字段一个,以及它的比较功能。完成后,我将使用基类指针将对象添加到向量中。

这是代码的概要:

class BaseRecord
{
public:
    virtual bool Compare();     // defined elsewhere

protected:
    std::string m_strName;
    std::string m_strAddress:
};

class BaseDecorator : public BaseRecord
{
public:
    BaseDecorator(BaseRecord *pBase) : m_pBase(pBase){}

    bool Compare()
    {
        return m_pBase->Compare();
    }

private:
    BaseRecord *m_pBase;
};

class EmailDecorator : public BaseDecorator
{
public:
    EmailDecorator(BaseRecord *pBase) : EmailDecorator(pBase){}

    bool Compare()
    {
        if (!CompareEmail())        // defined elsewhere
        {
            return false;
        }

        BaseDecorator::Compare();
    }

private:
    std::string m_strEmail
};

class DOBDecorator : public BaseDecorator
{
public:
    DOBDecorator(BaseRecord *pBase) : DOBDecorator(pBase){}

    bool Compare()
    {
        if (!CompareDOB())      // defined elsewhere
        {
            return false;
        }

        BaseDecorator::Compare();
    }

private:
    std::string m_strDOB;
};

这些是类。我现在想做的是将它们添加到向量中:

vector<BaseRecord *> m_vecRecords;

BaseRecord pRecord = new BaseRecord();

// wrong - copies pointer only to vector
m_vecRecords.push_back(pRecord);

// OK - default copy constructor for BaseRecord used
m_vecRecords.push_back(new BaseRecord(*pRecord));

// now chain the decorators

// pRecord is a BaseRecord
BaseRecord pRecord = new EmailDecorator(pRecord);

//wrong - copies pointer only to vector
m_vecRecords.push_back(pRecord);

// ??? needs copy constructor
m_vecRecords.push_back(new EmailDecorator(*pRecord));

// pRecord is an EmailDecorator
BaseRecord pRecord = new DOBDecorator(pRecord);

// wrong - copies pointer only to vector
m_vecRecords.push_back(pRecord);

// ??? needs copy constructor
m_vecRecords.push_back(new DOBDecorator(*pRecord));

现在尝试编写复制构造函数:

// should p be an EmailDecorator *, or a BaseDecorator * ?
EmailDecorator::EmailDecorator(const EmailDecorator *p)
{
    // this will leak - no delete in the destructor
    // I have not supplied a destructor
    m_pBase = new BaseDectorator(p);
    m_strEmail = p->m_strEmail;
}

// should p be a DOBDecorator *, or  BaseDecorator * ?
// in the above example, when the copy constructor is needed, it is an EmailDecorator *

DOBDecorator::DOBDecorator(const DOBDecorator *p)
{
    // this will leak - no delete in the destructor
    // I have not supplied a destructor
    m_pBase = new BaseDectorator(p);
    m_strDOB = p->m_strDOB;
}

那么我如何编写复制构造函数来进行深度复制,并能够释放分配的内存?我觉得我错过了一些东西,并且有一种方法可以做到这一点而无需提供复制构造函数?

【问题讨论】:

  • 简而言之 - 要深度复制通过基类的指针/引用保存的多态对象,您不仅需要复制构造函数,还需要一个虚拟的 clone() 类函数。
  • 装饰器模式略有不同,它让基础对象保留一个装饰器列表,需要更多的手动工作,但使用 c++/STL 效果更好
  • Angew - 谢谢,克隆成语是个好主意。
  • sp2danny - 是的,你是对的。我已经重写了我的装饰器,使其具有从基记录继承的基类,然后我所有的具体装饰器都从基装饰器继承。
  • Compare 函数与什么比较?

标签: c++ design-patterns decorator stdvector


【解决方案1】:

就装饰者而言,您并没有太多优势;不幸的是,您在 C++ 方面的表现差强人意。

Decorator 而言,您遇到的主要问题是您的接口 不应具有任何价值。否则,就像这里的情况一样,BaseDecorator 对象有至少两个 name 字段:

  • 一个来自它的基类
  • 一个来自其成员

你忘了从基类初始化那个。

就 C++ 而言,不幸的是这会变得复杂,因为您没有考虑 所有权。在具有垃圾收集器的语言中,您可以偷工减料,但在 C++ 中这不起作用。

那么让我们纠正这个问题吧?


首先,我们需要一个干净的界面:

class IRecord {
public:
    virtual bool lessThan(IRecord const& other) const = 0;
};

我会让你弄清楚如何实际比较两条记录;使用装饰器方法可能并不容易,因为不能保证仅仅因为 thisEmailDecorator,other 在其链中也有 EmailDecorator somewhere

然后,我们可以构建一个装饰器方法,我们将使用强大的所有权声明来实现:

class RecordDecorator: public IRecord {
protected:
    RecordDecorator(std::unique_ptr<IRecord> r): _decorated(std::move(r)) {}

private:
    std::unique_ptr<IRecord> _decorated;
};

我们也可以建造我们的第一块石头:

class BaseRecord final: public IRecord {
public:
    BaseRecord(std::string name, std::string address):
        _name(std::move(name)), _address(std::move(address)) {}

    virtual bool lessThan(IRecord const& record) const override;

private:
    std::string _name;
    std::string _address;
}; // class BaseRecord

然后,我们终于可以尝试从这里创建一个value-class(即可以通过值操作的类):

class Record {
public:
    Record(std::string name, std::string address):
        _data(std::make_unique<BaseRecord>(std::move(name), std::move(address)) {}

    bool lessThan(Record const& other) const {
        return _data->lessThan(other._data);
    }

    template <typename D, typename... Args>
    void decorate(Args&&... args) {
        _data = std::make_unique<D>(std::move(_data), std::forward<Args>(args)...);
    }

private:
    std::unique_ptr<IRecord> _data;
}; // class Record

这个设计不错:

  • 它不会泄漏内存,
  • 它不会保留多个不同的数据成员副本,
  • 并且易于操作(对于客户端)

但是,就目前而言,Record 将没有复制构造函数(也没有复制赋值运算符),因为std::unique_ptr 错过了这些。

如果您想添加它们,您需要添加一个virtual std::unique_ptr&lt;IRecord&gt; clone() const = 0 (*) 到IRecord,它将负责深度复制。这就是我们的RecordDecorator 的亮点:

class RecordDecorator: public IRecord {
protected:
    RecordDecorator(std::unique_ptr<IRecord> r): _decorated(std::move(r)) {}

    RecordDecorator(RecordDecorator const& other):
        _decorated(other._decorated->clone()) {}

    RecordDecorator& operator=(RecordDecorator const& other) {
        if (this == &other) { return *this; }
        _decorated = other._decorated.clone();
        return *this;
    }

    // These two got disabled when we wrote our own copy constructor
    // and copy assignment operator, so let's re-enable them.
    RecordDecorator(RecordDecorator&&) = default;
    RecordDecorator& operator=(RecordDecorator&&) = default;

private:
    std::unique_ptr<IRecord> _decorated;
};

现在,任何从 RecordDecorator 继承的类都会自动获得一个复制构造函数和复制赋值操作符,前提是它不包含自己的不可复制成员。

我们还可以通过请求的复制改进Record

Record::Record(Record const& other):
    _data(other._data.clone())
{}

Record& Record::operator=(Record const& other) {
    if (this == &other) { return *this; }
    _data = other._data.clone();
    return *this;
}

// These two got disabled when we wrote our own copy constructor
// and copy assignment operator, so let's re-enable them.
Record::Record(Record&&) = default;
Record& Record::operator=(Record&&) = default;

锦上添花,如何使用这一切:

class EmailDecorator final: public RecordDecorator {
public:
    EmailDecorator(std::unique_ptr<IRecord> base, std::string email):
        RecordDecorator(std::move(base)), _email(email) {}

    virtual std::unique_ptr<IRecord> clone() const override {
        return std::make_unique<EmailDecorator>(*this);
    }

    virtual bool lessThan(IRecord const&) const override; // up to you ;)

private:
    std::string _email;
}; // class EmailDecorator

int main() {
    Record record{"John, Doe", "12345 Mimosa Road, 3245 Washington DC"};
    record.decorate<EmailDecorator>("john.doe@aol.com");

    std::vector<Record> vec;
    vec.push_back(record); // make a copy
    vec.back().decorate<EmailDecorator>("doe.john@msn.com"); // another e-mail!
}

但是...通过装饰添加字段会使这些字段上的任何逻辑都变得非常尴尬...您很快就会知道痛苦:只要您尝试实际实现lessThan

【讨论】:

    猜你喜欢
    • 2022-01-03
    • 1970-01-01
    • 1970-01-01
    • 2011-06-15
    • 1970-01-01
    • 1970-01-01
    • 2013-04-04
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多