我可以在这个设计中使用dynamic_cast以外的东西吗?

Can I use something other than dynamic_cast in this design?

本文关键字:cast dynamic 我可以      更新时间:2023-10-16

在我们的系统中,有

  • 多个deviceTypes
  • 每个deviceType可以有不同的配置类型
  • 每个deviceType都是一个自己的库

我在一个情况下,我被迫使用dynamic_cast。我想知道是否有更好的方法来设计这个

我有的是:

// in common code
class Config {public: virtual ~Config(){} }; 
    class Device {
     protected:
        Config* devConfig;
    protected:
        virtual void createDevConfig() = 0;
    public:
        virtual void display() = 0;
    };
    // Device specific Code
    class A0Device : public Device {
    protected:
        virtual void createDevConfig() { this->devConfig = new A0Config(); }
    public:
        A0Device() { this->createDevConfig(); }
        virtual void display(){
        A0Config* config = dynamic_cast<A0Config*>(this->devConfig);
        if(!config) std::cout << "Null objectn";
        }
    };
    class A0Config : public Config {};
    int main() {
        Device* dev = new A0Device();
        dev->display();
        return 0;
    }

本质上,A0Device有自己的配置类型:A0Config,由其他成员组成。A0Device在基类中将devConfig定义为Config*。在A0Device::display() -我需要访问devConfig对象(作为A0Config类型)。virtual createDevConfig()确保配置对象在A0Device =>中始终为A0Config类型,在这里使用dynamic_cast是否安全?有没有更好的设计方法呢?

如果您可以支持额外的(在通用系统中最小,在实时/嵌入式系统中巨大)运行时开销,则可以安全地使用dynamic_cast<>

然而,你必须意识到这个"小"细节。给定表达式dynamic_cast<T*>(expr),当*expr的动态类型既不是 T,也不是T的子类时,表达式求值为空指针nullptr 。当然,解引用nullptr会调用未定义的行为,并且会在大多数平台上导致崩溃

然而,检查nullptr 可能是不值得的,如果,只有当你知道你的代码会在这种情况下崩溃

因为c++是一个碗,里面有汤和冰淇淋,土豆和咖啡,当然,每个想法都来自Stroustrup/c++委员会的耳朵/头脑,有几个替代方案:

  • A 向下转换 static_cast<T*>(expr),尽管这与将某些内容转换为其他内容具有相同的问题。
  • 对于那些觉得"必要"的C语言的人来说,reinterpret_cast<T*>(expr)在这里是不必要的,但它在那里。
  • 如果你都认为某件事在工程上是完美的,并且你知道所有对象在运行时可能在一个地方拥有的类型,你可以使用enum for the typeunion for holding the dataplacement newexplicit destructor calls supercombo。
  • 如果数据的东西涉及的问题,你可以有像virtual Config &getConfig() = 0的基础,和ABC123Config config; Config &getConfig { return this->config; }的东西在派生。
  • 对于像我这样的疯子, :使用c风格/构造器风格的强制转换。

希望这对你有帮助!

您可以在基类中拥有一个返回Config指针或引用的纯虚函数(除非您需要指针),然后在派生类中拥有存储。这个问题/答案涵盖了指针和引用的区别:何时使用引用和指针

这种设计的优点是基类中任何需要Config的都可以使用getConfig,而任何需要使用派生类的都可以不需要强制转换。另外,您不需要调用newdelete

class Device {
   protected:
      virtual Config& getConfig() = 0;
   ...
};
class A0Device {
public:
   ...
   Config& getConfig() {return config;}
   ...
private:
   A0Config config;
};

[我不确定,如果我在这里错过了什么,但它或多或少听起来像一个设计味]

为什么A0Config不能聚合Config ?

通常,碰到dynamic_cast是在建立 is - a 关系方面出现问题的线索。在这种情况下,A0Config提供了一些不适合其基类Config的功能(getA()),可能它违反了sps - liskov替代原则,从而违反了强is - a

尽管如此,如果一个类关系可以用不止一种方式来表达,那么就使用最弱的关系——Herb Sutter

…肖恩·帕伦特就是这样。继承是evil的基类