【问题标题】:Functional vs Safety / Static vs Dynamic instantiations功能与安全/静态与动态实例化
【发布时间】:2014-06-20 10:27:24
【问题描述】:

我的情况是,我认为两种实现都是正确的,但我不知道该选择哪一种。

我有一个模拟读卡器的应用程序。它有一个 GUI,您可以在其中选择要使用的串行端口和速度,以及一个播放和停止按钮。

我正在寻找阅读器构建的最佳实现方式。

我有一个 SimulatorCore 班级,与我的申请一样长 SimulatorCore 实例化Reader 类。并且可以在多个串口上模拟多个阅读器。

两种可能:

  • 我的Reader是一个指针(动态实例化),我在播放按钮时实例化它,当停止按钮时删除它。

  • 我的Reader 是一个对象(静态实例化),我在SimulatorCore 构造函数中对其进行实例化,然后在我的Reader 类中创建并调用Reader.init()Reader.cleanup(),并在播放和停止时调用它们打

我个人是看功能方面的,明明是想用指针,不模拟reader就不用reader实例化。

有人说我应该使用静态实例化(原因:为了安全,因为“当你可以选择不使用指针时,使用指针很糟糕”)

我不熟悉它们,但我想我也可以使用智能指针。

代码示例:第一种解决方案:

class SimulatorCore
{  
    play(){reader = new Reader();};
    stop(){delete reader; reader = nullptr;};

private:
    Reader *reader;
}

代码示例:第二种解决方案:

class SimulatorCore
{  
    play(){reader.init();};
    stop(){reader.cleanup();};

private:
    Reader reader;
}

代码未经测试,我只是为了说明而写下来。

什么是最好的解决方案?为什么?

【问题讨论】:

    标签: c++ instantiation simulator


    【解决方案1】:

    您可以轻松使用 shared_ptr/unique_ptr:

    class SimulatorCore
    {  
        play(){_reader = make_shared<Reader>();};
        stop(){_reader = nullptr};
    
    private:
        shared_ptr<Reader> _reader;
    }
    

    我猜这会正确地解决你的问题。

    动态分配会带来一些问题,例如抛出异常(例如,如果在 play() 和 stop() 之间抛出异常,可能会丢失内存,并且永远不会调用 stop())。或者您可以在 SimulatorCore 销毁之前忘记调用 stop(),如果程序很重,可能会发生这种情况。

    如果您从未尝试过智能指针,那么这是开始尝试的好机会。

    【讨论】:

    • 谢谢,我终于使用了 unique_ptr 并且效果很好。由于解释,我会接受另一个答案,但你解决了我的问题。
    【解决方案2】:

    您通常应该避免自己使用new 执行动态分配,因此如果您打算使用第一种解决方案,则应该使用智能指针。

    但是,这里的主要问题是逻辑问题。真正的读卡器在被使用之前一直处于空闲状态。在第二个解决方案中,initcleanup 做什么?他们是简单地将读卡器设置为空闲状态,还是开始模拟实际读取卡?如果是第一种情况,我建议这种行为应该在Reader的构造函数和析构函数中,然后创建一个Reader对象表示带读卡器存在。如果是第二种情况,那么我会说第二种解决方案非常正确,只是函数命名错误。

    在我看来最合乎逻辑的是这样的:

    class SimulatorCore
    {  
        play(){reader.start();};
        stop(){reader.stop();};
    
    private:
        Reader reader;
    }
    

    是的,我所做的只是更改Reader 的函数名称。但是,这些函数现在不负责初始化或清理读取器 - 责任在 Reader 的构造函数和析构函数手中。相反,startstop 开始和结束Reader 的模拟。然后,单个Reader 实例可以在其生命周期内多次进入和退出此模拟模式。

    如果您以后想将此想法扩展到多个Readers,您只需将成员更改为:

    std::vector<Reader> readers;
    

    但是,我无法确定这是您想要的,因为我不知道您的程序的逻辑。希望这会给您一些想法。

    同样,无论你决定做什么,你都应该避免使用new 来分配你的Readers,然后也避免使用原始指针来引用那些Readers。使用智能指针及其对应的make_... 函数来动态分配这些对象。

    【讨论】:

    • 很好的解释,谢谢。我将使用智能指针,我认为这是最好的解决方案,我需要练习它们!停止按钮实际上是停止模拟,没有模拟=没有阅读器,所以我认为删除阅读器很好。
    • @MoKaT 好的,如果这是你的逻辑,那听起来不错!
    【解决方案3】:

    这显然取决于您的整个程序的组织方式,但总的来说,出于责任考虑,我认为我更喜欢静态方法:

    假设您有一个单独的类来处理串行通信。该类将发送和接收消息并将它们分派给阅读器类。消息可能随时到达。动态和静态方法的区别在于:

    • 使用动态方法,串行类必须在发送消息之前测试阅读器是否真的存在。或者阅读器必须在串行类中注册和注销自己。
    • 使用静态方法,阅读器类可以自行决定当前是否能够处理消息。

    所以我认为静态方法更简单直接。

    但是,如果将来您有可能必须实现其他不同的阅读器类,动态方法将使此扩展更容易,因为适当的类可以很容易地在运行时。

    因此动态方法提供了更大的灵活性。

    【讨论】:

    • 谢谢,我的通讯是我阅读器的一部分(因为我可能需要多个阅读器,在不同的串行端口 - USB 上)。而且我有多个阅读器类,但在另一个层次上(我有一个抽象基类,它是我用于实例化不同阅读器的接口)。智能指针似乎非常适合这种情况。
    • 我已经简化了我的问题,但是我们在这里所说的Reader 是一个实例化真正阅读器的状态机。但这更像是一个功能问题,而不是技术问题。
    • 好的,在这个答案的第一部分,我试图展示一个示例,其中静态对象的“始终存在且有效”属性是一个优势。根据您在此处添加的内容,我想我也会使用智能指针。
    猜你喜欢
    • 1970-01-01
    • 2012-11-12
    • 1970-01-01
    • 2013-12-14
    • 2018-09-10
    • 2011-03-02
    • 1970-01-01
    • 2011-05-22
    • 1970-01-01
    相关资源
    最近更新 更多