C ++ Decorator添加到std :: vector

时间:2014-04-10 13:31:09

标签: c++ design-patterns decorator stdvector

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

以下是代码的精确度:

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;
}

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

1 个答案:

答案 0 :(得分:2)

就装饰者而言,你并没有太多关闭;不幸的是,你在C ++方面远远不够。

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

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

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

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

那么,让我们纠正这个问题,是吗?


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

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

我会让你弄清楚如何比较两个记录;使用装饰器方法可能并不容易,因为不能保证只是因为thisEmailDecorator,其他人在其链中也有EmailDecorator 某处

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

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

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

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<IRecord> 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的类都会自动获得一个copy-constructor和copy-assignment-operator,前提是它不包含自己的不可复制成员。

我们还可以使用请求的复制来改进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