C Decorator添加到std :: vector

我有一个记录的基类,并希望使用装饰器添加其他字段和比较函数,并能够链接装饰器(记录可以有一个电子邮件,或出生日期,或两者,或没有).我也会有很多这样的装饰者;每个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;
}

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

最佳答案 就装饰者而言,你并没有太多关闭;不幸的是,你在C方面远远不够.

就Decorator而言,你遇到的主要问题是你的界面不应该有任何价值.否则,就像这里的情况一样,BaseDecorator对象至少有两个名称字段:

>一个来自基类
>一个来自其成员

而你忘了从基类初始化那个.

就C来说,不幸的是,这很复杂,因为你没有考虑所有权.在具有垃圾收集器的语言中,您可以偷工减料,但在C中这不起作用.

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

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

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

我会让你弄清楚如何比较两个记录;使用装饰器方法可能并不容易,因为不能保证只是因为这是一个EmailDecorator,其他人在其链中的某处也有一个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会错过这些.

如果你想添加它们,你需要添加一个虚拟的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 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!
}

但是……通过装饰添加字段会使这些字段上的任何逻辑变得非常笨拙……而且你很快就会知道这种痛苦:一旦你试图实现更少的话.

点赞