草庐IT

C++ 装饰器添加到 std::vector

coder 2024-02-24 原文

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

代码如下:

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 中:

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方面,您遇到的主要问题是您的接口(interface) 不应该有任何值(value)。否则,就像这里的情况一样,BaseDecorator对象有 至少 两个 name字段:

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

并且您忘记了从基类初始化一个。

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

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


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

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

我会让您了解如何实际比较两条记录;使用装饰器方法可能并不容易,因为不能保证仅仅因为 this是一个 EmailDecorator , other 也有一个 EmailDecorator 某处 在它的链中。

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

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

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

我们也可以 build 我们的第一 block 石头:

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 的类自动获取复制构造函数和复制赋值运算符,前提是它不包含自己的不可复制成员。

而且我们还可以改进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实际上。

关于C++ 装饰器添加到 std::vector,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22989530/

有关C++ 装饰器添加到 std::vector的更多相关文章

  1. ruby - 我需要将 Bundler 本身添加到 Gemfile 中吗? - 2

    当我使用Bundler时,是否需要在我的Gemfile中将其列为依赖项?毕竟,我的代码中有些地方需要它。例如,当我进行Bundler设置时:require"bundler/setup" 最佳答案 没有。您可以尝试,但首先您必须用鞋带将自己抬离地面。 关于ruby-我需要将Bundler本身添加到Gemfile中吗?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/4758609/

  2. ruby - 将 Bootstrap Less 添加到 Sinatra - 2

    我有一个ModularSinatra应用程序,我正在尝试将Bootstrap添加到应用程序中。get'/bootstrap/application.css'doless:"bootstrap/bootstrap"end我在views/bootstrap中有所有less文件,包括bootstrap.less。我收到这个错误:Less::ParseErrorat/bootstrap/application.css'reset.less'wasn'tfound.Bootstrap.less的第一行是://CSSReset@import"reset.less";我尝试了所有不同的路径格式,但它

  3. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  4. ruby - 续集在添加关联时访问many_to_many连接表 - 2

    我正在使用Sequel构建一个愿望list系统。我有一个wishlists和itemstable和一个items_wishlists连接表(该名称是续集选择的名称)。items_wishlists表还有一个用于facebookid的额外列(因此我可以存储opengraph操作),这是一个NOTNULL列。我还有Wishlist和Item具有续集many_to_many关联的模型已建立。Wishlist类也有:selectmany_to_many关联的选项设置为select:[:items.*,:items_wishlists__facebook_action_id].有没有一种方法可以

  5. ruby - 可以通过多少种方法将方法添加到 ruby​​ 对象? - 2

    当谈到运行时自省(introspection)和动态代码生成时,我认为ruby​​没有任何竞争对手,可能除了一些lisp方言。前几天,我正在做一些代码练习来探索ruby​​的动态功能,我开始想知道如何向现有对象添加方法。以下是我能想到的3种方法:obj=Object.new#addamethoddirectlydefobj.new_method...end#addamethodindirectlywiththesingletonclassclass这只是冰山一角,因为我还没有探索instance_eval、module_eval和define_method的各种组合。是否有在线/离线资

  6. ruby - 如何在 Ruby 中向现有方法定义添加语句 - 2

    我注意到类定义,如果我打开classMyClass,并在不覆盖的情况下添加一些东西我仍然得到了之前定义的原始方法。添加的新语句扩充了现有语句。但是对于方法定义,我仍然想要与类定义相同的行为,但是当我打开defmy_method时似乎,def中的现有语句和end被覆盖了,我需要重写一遍。那么有什么方法可以使方法定义的行为与定义相同,类似于super,但不一定是子类? 最佳答案 我想您正在寻找alias_method:classAalias_method:old_func,:funcdeffuncold_func#similartoca

  7. ruby-on-rails - 添加回形针新样式不影响旧上传的图像 - 2

    我有带有Logo图像的公司模型has_attached_file:logo我用他们的Logo创建了许多公司。现在,我需要添加新样式has_attached_file:logo,:styles=>{:small=>"30x15>",:medium=>"155x85>"}我是否应该重新上传所有旧数据以重新生成新样式?我不这么认为……或者有什么rake任务可以重新生成样式吗? 最佳答案 参见Thumbnail-Generation.如果rake任务不适合你,你应该能够在控制台中使用一个片段来调用重新处理!关于相关公司

  8. ruby - 我如何添加二进制数据来遏制 POST - 2

    我正在尝试使用Curbgem执行以下POST以解析云curl-XPOST\-H"X-Parse-Application-Id:PARSE_APP_ID"\-H"X-Parse-REST-API-Key:PARSE_API_KEY"\-H"Content-Type:image/jpeg"\--data-binary'@myPicture.jpg'\https://api.parse.com/1/files/pic.jpg用这个:curl=Curl::Easy.new("https://api.parse.com/1/files/lion.jpg")curl.multipart_form_

  9. ruby - 使用 `+=` 和 `send` 方法 - 2

    如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:

  10. ruby-on-rails - 在 Ruby on Rails 中添加 boolean 列值 - 2

    我正在开发一个创建网络博客的RubyonRails项目。我希望将一个名为featured的boolean数据库字段添加到Post模型中。该字段应该可以通过我添加的事件管理界面进行编辑。我使用了以下代码,但我什至没有在网站上显示另一列。$railsgeneratemigrationaddFeaturedfeatured:boolean$rakedb:migrate我是RubyonRails的新手,非常感谢任何帮助。我的index.html.erb文件中的相关代码(views):FeaturedPost架构.rb:ActiveRecord::Schema.define(:version=>

随机推荐