【问题标题】:Should I dynamic cast or not我应该动态投还是不
【发布时间】:2012-01-07 08:29:42
【问题描述】:

我的问题是,我有一个派生类的基类。我在基类中有一个重载的 += 运算符,但我只想能够添加相同类型的派生类。我通过返回 lhs 并在它们不是同一类型时生成警告消息来做到这一点。

class base{
   int variable;
   public:
   base(int v);
   base & operator+=(const base & rhs);

class a: public base{
}

class b: public base{
}

base & operator+=(const base & rhs){
if(type_same(const & rhs){
    variable+=rhs.variable; }
return *this
}

a first(1);
a second(5);
b third(2);

first+=second // first.variable now =6
second+=third // warning produced second.variable still = 5

我以前从未使用过动态转换,所以我的问题是是否可以/建议使用它和 typeid 来检查传递的类型是否相同。我的替代方案是每个派生类的字符串常量名称。

【问题讨论】:

  • huh - 代码是一个很大的语法错误。另外,type_same 是在哪里定义的?最后,在编程错误(如不匹配的类型)上静默发出警告似乎是一个非常糟糕的主意。改为抛出 runtime_error?
  • 这只是试图使我的问题尽可能清晰的伪代码。这比从我的实际代码中复制和粘贴所有不同的小相关位要容易。

标签: c++ polymorphism


【解决方案1】:

为什么首先在基类中有operator+?如果您只需将具有正确类型的operator+ 放入ab 类中,它就可以工作,而无需任何dynamic_cast

dynamic_cast 通常暗示您的类型设计不当,应该避免使用。在所描述的场景中,您肯定可以在没有任何dynamic_cast 的情况下度过难关。如果不是,那么ab 不应派生自base,因为它们的行为不同。从用户的角度考虑:通过从base 派生a,您告诉用户a 可以用作base。如果方法需要实际类型,请将其向下移动,而不是将其放入基接口中。

顺便说一句,正确的用法。将是dynamic_cast rhs 到当前类型(即dynamic_cast<b*>(&rhs)),如果返回非零值,那么rhs 确实是正确的类型(但是,想想如果有一个类会发生什么源自b 被传入!)

【讨论】:

  • 我想把它保存在我的基类中,因为这只是一个例子,我的实际版本做了一些额外的检查和一些事情使它更长,我将有大约 10 个派生类,我想给它们的操作完全相同,最好在基类中。我只想要不能添加不同类型的条件。使用动态演员表版本,我认为我必须遍历一个可能正确的动态演员表列表,直到我达到我的零。
  • 在我看来,“这是基类中的一些东西,但是当你调用它时它有额外的要求”违反了 Liskov 替换原则。当然,LSP 并不是一个永恒的真理,而只是一些事实证明可能会出现问题的代码。因此,设计应该让你停下来。
【解决方案2】:

要解决您在帖子中提到的问题,dynamic_cast 和 RTTI 绝对是正确的工具。但是,在 runtime 发出警告同时为 '+' 操作提供 compile-time 支持可能会指出设计中的问题您的 API 不会完全期望您的 API 提供的行为。

【讨论】:

    【解决方案3】:

    我的看法:你可以使用 CRTP

    按照其他人的建议,以下代码将在编译时发出类型不匹配错误。

    template <typename Derived>
    class base{
       int variable;
     public:
       base(int v) { variable = v; }
       Derived& operator+=(const Derived& rhs);
    };
    
    struct a: base<a> {
        a(int v): base<a>(v) {}
    };
    
    struct b: base<b> {
        b(int v): base<b>(v) {}
    };
    
    template <typename Derived> 
    Derived& base<Derived>::operator+=(const Derived& rhs) 
    {
        variable += rhs.variable; 
        return static_cast<Derived&>(*this);
    }
    
    int main(int argc, const char *argv[])
    {
        a a1(1), a2(2);
        b b1(1), b2(2);
    
        a1 += a2;
        b1 += a2; // compile time error :)
    
        return 0;
    }
    

    即使您真的想将运算符参数键入为base&amp;,您至少会获得使用static_cast&lt;&gt; 的好处,因为转换是精确的并且在运行时定义良好。我可以用几句话来扩展这个想法

    【讨论】:

    • 嗯,模板,这就是缺少的一点。谢谢。
    • CRPT 也意味着所有类现在都派生自不同的基础。我不明白这怎么可能是解决方案,因为首先使用继承是有实际原因的。
    • @UncleBens:那是我没有假设的。此外,解决了使这种多态性问题(给出理由Even if you really wanted to type the operator arguments as base&amp; you'd at least gain the benefit of using static_cast&lt;&gt;
    猜你喜欢
    • 2011-01-13
    • 1970-01-01
    • 1970-01-01
    • 2018-09-27
    • 1970-01-01
    • 1970-01-01
    • 2016-08-03
    • 1970-01-01
    • 2014-05-24
    相关资源
    最近更新 更多