草庐IT

c++ - 将现有的类结构移植到智能指针

coder 2024-02-23 原文

我知道这个问题很长,但我不确定如何用更短的方式解释我的问题。问题本身是关于类层次结构的设计,尤其是如何将基于指针的现有层次结构移植到使用智能指针的层次结构中。如果有人能想出一些方法来简化我的解释,从而使这个问题更通用,请告诉我。这样,它可能对更多 SO 读者有用。

我正在设计一个 C++ 应用程序来处理一个允许我读取一些传感器的系统。该系统由我收集测量值的远程机器组成。此应用程序实际上必须使用两个不同的子系统:

  • 聚合系统:这种类型的系统包含我收集测量值的几个组件。所有通信都通过聚合系统,如果需要,聚合系统会将数据重定向到特定组件(发送到聚合系统本身的全局命令不需要传输到单个组件)。
  • 独立系统:在这种情况下,只有一个系统,所有通信(包括全局命令)都发送到该系统。

  • 接下来你可以看到我想出的类图:



    独立系统继承了 ConnMgrMeasurementDevice .另一方面,聚合系统在 AggrSystem 之间拆分其功能。和 Component .

    基本上,作为用户,我想要的是 MeasurementDevice对象并将数据透明地发送到相应的端点,无论是聚合系统还是独立系统。

    当前实现

    这是我目前的实现。首先,两个基础抽象类:
    class MeasurementDevice {
    public:
        virtual ~MeasurementDevice() {}
        virtual void send_data(const std::vector<char>& data) = 0;
    };
    
    class ConnMgr {
    public:
        ConnMgr(const std::string& addr) : addr_(addr) {}
        virtual ~ConnMgr() {}
        virtual void connect() = 0;
        virtual void disconnect() = 0;
    
    protected:
        std::string addr_;
    };
    

    这些是聚合系统的类:
    class Component : public MeasurementDevice {
    public:
        Component(AggrSystem& as, int slot) : aggr_sys_(as), slot_(slot) {}
        void send_data(const std::vector<char>& data) {
            aggr_sys_.send_data(slot_, data);
        }
    private:
        AggrSystem& aggr_sys_;
        int slot_;
    };
    
    class AggrSystem : public ConnMgr {
    public:
        AggrSystem(const std::string& addr) : ConnMgr(addr) {}
        ~AggrSystem() { for (auto& entry : components_) delete entry.second; }
    
        // overridden virtual functions omitted (not using smart pointers)
    
        MeasurementDevice* get_measurement_device(int slot) {
            if (!is_slot_used(slot)) throw std::runtime_error("Empty slot");
            return components_.find(slot)->second;
        }
    private:
        std::map<int, Component*> components_;
    
        bool is_slot_used(int slot) const {
            return components_.find(slot) != components_.end();
        }
        void add_component(int slot) {
            if (is_slot_used(slot)) throw std::runtime_error("Slot already used");
            components_.insert(std::make_pair(slot, new Component(*this, slot)));
        }
    };
    

    这是独立系统的代码:
    class StandAloneSystem : public ConnMgr, public MeasurementDevice {
    public:
        StandAloneSystem(const std::string& addr) : ConnMgr(addr) {}
    
        // overridden virtual functions omitted (not using smart pointers)
    
        MeasurementDevice* get_measurement_device() {
            return this;
        }
    };
    

    这些是类似工厂的函数,负责创建 ConnMgrMeasurementDevice对象:
    typedef std::map<std::string, boost::any> Config;
    
    ConnMgr* create_conn_mgr(const Config& cfg) {
        const std::string& type =
            boost::any_cast<std::string>(cfg.find("type")->second);
        const std::string& addr =
            boost::any_cast<std::string>(cfg.find("addr")->second);
    
        ConnMgr* ep;
        if (type == "aggregated") ep = new AggrSystem(addr);
        else if (type == "standalone") ep = new StandAloneSystem(addr);
        else throw std::runtime_error("Unknown type");
        return ep;
    }
    
    MeasurementDevice* get_measurement_device(ConnMgr* ep, const Config& cfg) {
        const std::string& type =
            boost::any_cast<std::string>(cfg.find("type")->second);
    
        if (type == "aggregated") {
            int slot = boost::any_cast<int>(cfg.find("slot")->second);
            AggrSystem* aggr_sys = dynamic_cast<AggrSystem*>(ep);
            return aggr_sys->get_measurement_device(slot);
        }
        else if (type == "standalone") return dynamic_cast<StandAloneSystem*>(ep);
        else throw std::runtime_error("Unknown type");
    }
    

    最后是 main() ,展示了一个非常简单的使用案例:
    #define USE_AGGR
    
    int main() {
        Config config = {
            { "addr", boost::any(std::string("192.168.1.10")) },
    #ifdef USE_AGGR
            { "type", boost::any(std::string("aggregated")) },
            { "slot", boost::any(1) },
    #else
            { "type", boost::any(std::string("standalone")) },
    #endif
        };
    
        ConnMgr* ep = create_conn_mgr(config);
        ep->connect();
    
        MeasurementDevice* dev = get_measurement_device(ep, config);
        std::vector<char> data; // in real life data should contain something
        dev->send_data(data);
    
        ep->disconnect();
        delete ep;
        return 0;
    }
    

    提议的变更

    首先,我想知道是否有办法避免dynamic_castget_measurement_device .自 AggrSystem::get_measurement_device(int slot)StandAloneSystem::get_measurement_device()有不同的签名,不可能在基类中创建一个公共(public)的虚拟方法。我正在考虑添加一个接受 map 的通用方法包含选项(例如,插槽)。在那种情况下,我不需要进行动态转换。就更简洁的设计而言,第二种方法是否更可取?

    为了将类层次结构移植到智能指针,我使用了 unique_ptr .首先我改了map AggrSystem 中的组件数量到:
    std::map<int, std::unique_ptr<Component> > components_;
    

    新增 Component现在看起来像:
    void AggrSystem::add_component(int slot) {
        if (is_slot_used(slot)) throw std::runtime_error("Slot already used");
        components_.insert(std::make_pair(slot,
            std::unique_ptr<Component>(new Component(*this, slot))));
    }
    

    返回 Component我决定从 Component 的生命周期开始返回一个原始指针对象由 AggrSystem 的生命周期定义目的:
    MeasurementDevice* AggrSystem::get_measurement_device(int slot) {
        if (!is_slot_used(slot)) throw std::runtime_error("Empty slot");
        return components_.find(slot)->second.get();
    }
    

    返回原始指针是正确的决定吗?如果我使用 shared_ptr ,但是,然后我遇到了独立系统的实现问题:
    MeasurementDevice* StandAloneSystem::get_measurement_device() {
        return this;
    }
    

    在这种情况下,我无法返回 shared_ptr使用 this .我想我可以创建一个额外的间接级别,并有类似 StandAloneConnMgr 的东西。和 StandAloneMeasurementDevice ,第一个类将举行 shared_ptr到第二个实例。

    所以,总的来说,我想问一下在使用智能指针时这是否是一个好方法。最好使用 mapshared_ptr并返回 shared_ptr也是,还是基于使用 unique_ptr 的当前方法更好所有权和访问原始指针?

    附注:create_conn_mgrmain也进行了更改,因此现在我使用 ConnMgr* 而不是使用原始指针( unique_ptr<ConnMgr> ) .我没有添加代码,因为问题已经足够长了。

    最佳答案

    First of all, I wonder whether there is a way to avoid the dynamic_cast in get_measurement_device.



    我会尝试统一 get_measurement_device签名,以便您可以将其设为基类中的虚函数。

    So, overall, I wanted to ask whether this a good approach when using smart pointers.



    我认为你做得很好。您基本上已将“单一所有权”新闻和删除内容转换为 unique_ptr以一种相当机械的方式。这完全是正确的第一步(也可能是最后一步)。

    我也认为您从 get_measurement_device 返回原始指针的决定是正确的。因为在您的原始代码中,此函数的客户端没有获得此指针的所有权。当您不打算共享或转移所有权时处理原始指针是大多数程序员都会认识到的好模式。

    总之,您已经正确地将现有设计转换为使用智能指针,而没有更改设计的语义。

    从这里开始,如果您想研究将您的设计更改为涉及共享所有权的设计的可能性,那么下一步是完全有效的。我自己的偏好是更喜欢独特的所有权设计,直到用例或环境需要共享所有权。

    唯一所有权不仅更有效,而且更容易推理。这种推理的简便性通常会减少意外的循环内存所有权模式(循环内存所有权 == 内存泄漏)。每次看到指针时都只删除 shared_ptr 的编码人员更有可能以内存所有权循环结束。

    也就是说,循环内存所有权也可以仅使用 unique_ptr .如果发生这种情况,您需要 weak_ptr打破循环,和weak_ptr仅适用于 shared_ptr .因此,所有权周期的引入是迁移到 shared_ptr 的另一个很好的理由。 .

    关于c++ - 将现有的类结构移植到智能指针,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11037011/

    有关c++ - 将现有的类结构移植到智能指针的更多相关文章

    1. ruby - 使用 ruby​​ 将 HTML 转换为纯文本并维护结构/格式 - 2

      我想将html转换为纯文本。不过,我不想只删除标签,我想智能地保留尽可能多的格式。为插入换行符标签,检测段落并格式化它们等。输入非常简单,通常是格式良好的html(不是整个文档,只是一堆内容,通常没有anchor或图像)。我可以将几个正则表达式放在一起,让我达到80%,但我认为可能有一些现有的解决方案更智能。 最佳答案 首先,不要尝试为此使用正则表达式。很有可能你会想出一个脆弱/脆弱的解决方案,它会随着HTML的变化而崩溃,或者很难管理和维护。您可以使用Nokogiri快速解析HTML并提取文本:require'nokogiri'h

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

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

    3. ruby - 是否有用于序列化和反序列化各种格式的对象层次结构的模式? - 2

      给定一个复杂的对象层次结构,幸运的是它不包含循环引用,我如何实现支持各种格式的序列化?我不是来讨论实际实现的。相反,我正在寻找可能会派上用场的设计模式提示。更准确地说:我正在使用Ruby,我想解析XML和JSON数据以构建复杂的对象层次结构。此外,应该可以将该层次结构序列化为JSON、XML和可能的HTML。我可以为此使用Builder模式吗?在任何提到的情况下,我都有某种结构化数据-无论是在内存中还是文本中-我想用它来构建其他东西。我认为将序列化逻辑与实际业务逻辑分开会很好,这样我以后就可以轻松支持多种XML格式。 最佳答案 我最

    4. 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.你能做的最好的事情是:

    5. ruby-on-rails - 一般建议和推荐的文件夹结构 - Sinatra - 2

      您将如何构建一个简单的Sinatra应用程序?我正在制作,我希望该应用具有以下功能:“应用程序”更像是一个包含所有信息的管理仪表板。然后另一个应用程序将通过REST访问信息。我还没有创建仪表板,只是从数据库中获取东西session和身份验证(尚未实现)您可以上传图片,其他应用可以显示这些图片我已经使用RSpec创建了一个测试文件通过Prawn生成报告目前的设置是这样的:app.rbtest_app.rb因为我实际上只有应用程序和测试文件。到目前为止,我已经将Datamapper用于ORM,将SQLite用于数据库。这是我的第一个Ruby/Sinatra项目,所以欢迎任何和所有建议-我应

    6. ruby - 如何计算 Liquid 中的变量 +1 - 2

      我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我

    7. arrays - Ruby 数组 += vs 推送 - 2

      我有一个数组数组,想将元素附加到子数组。+=做我想做的,但我想了解为什么push不做。我期望的行为(并与+=一起工作):b=Array.new(3,[])b[0]+=["apple"]b[1]+=["orange"]b[2]+=["frog"]b=>[["苹果"],["橙子"],["Frog"]]通过推送,我将推送的元素附加到每个子数组(为什么?):a=Array.new(3,[])a[0].push("apple")a[1].push("orange")a[2].push("frog")a=>[[“苹果”、“橙子”、“Frog”]、[“苹果”、“橙子”、“Frog”]、[“苹果”、“

    8. ruby - 如何在 ruby​​ 中复制目录结构,不包括某些文件扩展名 - 2

      我想编写一个ruby​​脚本来递归复制目录结构,但排除某些文件类型。因此,给定以下目录结构:folder1folder2file1.txtfile2.txtfile3.csfile4.htmlfolder2folder3file4.dll我想复制这个结构,但不包含.txt和.cs文件。因此,生成的目录结构应如下所示:folder1folder2file4.htmlfolder2folder3file4.dll 最佳答案 您可以使用查找模块。这是一个代码片段:require"find"ignored_extensions=[".cs"

    9. ruby-on-rails - 在现有数据库上进行 Rails 迁移 - 2

      我正在创建一个新的Rails3.1应用程序。我希望这个新应用程序重用现有数据库(由以前的Rails2应用程序创建)。我创建了新的应用程序定义模型,它重用了数据库中的一些现有数据。在开发和测试阶段,一切正常,因为它在干净的表数据库上运行,但是当尝试部署到生产环境时,我收到如下消息:PGError:ERROR:column"email"ofrelation"users"alreadyexists***[err::localhost]:ALTERTABLE"users"ADDCOLUMN"email"charactervarying(255)DEFAULT''NOTNULL但是我在迁移中有这

    10. ruby - 如何将新的 rvm 安装与现有的 ruby​​ 版本相关联? - 2

      我遇到了RVM的问题,所以我卸载并重新安装了它。事实是我实际上尝试过rbenv,但这对我来说没有用,所以我试图让rvm重新启动并运行-而不必安装重复版本的Ruby。我至少安装了1个现有版本的Ruby:ruby--versionruby1.8.7(2011-12-28patchlevel357)[universal-darwin11.0]但是当我执行rvmlist时,我得到一个空白列表:bash-3.2$rvmlistrvmrubies#Defaultrubynotset.Try'rvmaliascreatedefault'.#=>-current#=*-current&&default

    随机推荐