我有一个记录的基类,并希望使用装饰器添加其他字段和比较函数,并且能够链接装饰器(记录可以有电子邮件,或者出生日期,或两者都有,或者没有) 。我也会有很多这样的装饰者;每个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;
}
那么如何编写复制构造函数来执行深层复制,并且能够释放分配的内存?我觉得我错过了一些东西,而且无需提供复制构造函数就可以做到这一点吗?
答案 0 :(得分:2)
就装饰者而言,你并没有太多关闭;不幸的是,你在C ++方面远远不够。
就Decorator
而言,您遇到的主要问题是接口不应包含任何值。否则,就像这里的情况一样,BaseDecorator
对象的至少两个name
字段:
你忘了从基类初始化那个。
就C ++而言,遗憾的是,这很复杂,因为您没有考虑所有权。在具有垃圾收集器的语言中,您可以偷工减料,但在C ++中这不起作用。
那么,让我们纠正这个问题,是吗?
首先,我们需要一个干净的界面:
class IRecord {
public:
virtual bool lessThan(IRecord const& other) const = 0;
};
我会让你弄清楚如何比较两个记录;使用装饰器方法可能并不容易,因为不能保证只是因为this
是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
错过了这些。
如果您想添加它们,则需要将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
。