【问题标题】:Avoiding dynamic_cast in implementation of virtual functions in derived class在派生类中实现虚函数时避免 dynamic_cast
【发布时间】:2010-07-09 16:41:09
【问题描述】:

这是一些示例代码,解释了我想要实现的目标。

基本上,我有一个算法,它依赖于类中可用的一些基本操作。我在纯抽象基类中定义了这些操作。我想将该算法应用于通过为特定对象派生类来提供这些操作的各种对象。

但是,就这些操作而言,不同的派生对象彼此不兼容。我的问题是我是否可以避免使用 RTTI 来确保例如 bool derived2::identical(const base* other2)、asserts(或其他退出机制) where other2 不是 derived2 类型。

另一种方法是在特定派生对象上对函数算法进行模板化,但这意味着它的实现必须存在于我不想这样做的头文件中,因为 1) 更改用于测试的算法代码目的可能会导致重新编译大部分代码 2) 算法的实现将在标头中公开,而不是很好地保存在对最终用户隐藏的源文件中。

头文件

#include <list>

class base
{
public:
 virtual float difference(const base*) const = 0;
 virtual bool identical(const base*) const = 0; 
};


class derived1 : public base
{
 public:
 float difference(const base* other1) const
 {
  // other1 has to be of type derived1
            if(typeid(other1) == typeid(this))
            {
                    // process ...
            }
            else
            {
                    assert(0);
            }
  return 1;
 }

 bool identical(const base* other1) const
 {
  // other1 has to be of type derived1
            if(typeid(other1) == typeid(this))
            {
                    // compare...
            }
            else
            {
                    assert(0);
            }
  return true;
 }
};


class derived2 : public base
{
 public:
        float difference(const base* other2) const
        { 
             // process ...
  // other2 has to be of type derived2
            return 2;
        }

 bool identical(const base* other2) const
        {
                // do comparison
  // derived1 and derived2 cannot be compared
                return true;
        }
};

// Declaration
int algorithm(std::list<base*>& members);

算法实现源码文件

#include "header_file_containing_base"
int algorithm(std::list<base*>& members)
{
 // This function only relies on the interface defined in base
 // process members;

 return 1;
}

主程序

int main()
{
  // Create lists of derived1 and derived2
  // Run algorithm on these lists
}

【问题讨论】:

    标签: c++ inheritance rtti dynamic-cast


    【解决方案1】:

    您可以使用双重调度 (http://en.wikipedia.org/wiki/Double_dispatch)

    【讨论】:

    • 以后可以通过继承derived1 和derived2 并覆盖函数来扩展双重调度。但是您的问题并没有说明您希望以后能够添加更多类型而无需更改原始类。
    【解决方案2】:

    好吧,有一件简单的事情:将真实类型存储为成员。

    • enum,对所有类型进行分组。如果你有很多,它会变得很麻烦。
    • 生成 id 的工厂(使用模板为每个项目仅生成一个 id)
    • ...

    我将说明工厂 id:

    class IdFactory
    {
    public:
      template <class T>
      static size_t GetId(T const&) // argument deduction
      {
        static size_t const Id = GetIdImpl();
        return Id;
      }
    
    private:
      static size_t GetIdImpl()
      {
        static size_t Id = 0;
        return ++Id;
      }
    }; // class IdFactory
    

    你可以像这样使用它:

    class Base
    {
    public:
      explicit Base(size_t id): mId(id) {}
      size_t const mId; // meaningless to change it afterward...
    
    private:
    };
    
    class Derived: public Base
    {
    public:
      explicit Derived(): Base(IdFactory::GetId(*this)) {}
    };
    

    然后您可以使用mId 成员进行测试。请注意,因为它是const,所以它可以被暴露...否则您可以创建一个内联const getter...

    float Derived::difference(const Base& rhs)
    {
      assert( IdFactory::GetId(*this) == rhs.mId );
    
      // ...
    }
    

    这里的成本可以忽略不计:

    • GetId 是内联的,因此没有函数调用
    • GetId 被延迟初始化,除了初始化之外,它相当于检查 static 成员是否已初始化:它通常实现为 if 语句,其条件始终评估为真(第一次除外)。李>
    • == 通常很快;)

    唯一的缺点是您实际上需要确保正确初始化 id。

    还有一种无存储的方案,涉及到虚函数调用:

    class Other: public Base
    {
    public:
      virtual size_t id() const { return IdFactory::GetId(*this); }
    };
    

    实践起来更容易,因为不存储const 成员意味着您不必自己编写作业。

    【讨论】:

    • 有什么理由让Ids 成为一个向量,而不是一个整数计数器?
    • @Mike: no :p 对不起,我开始在向量中注册 typeid(T).name() 以便能够从 id 返回到类型。在这里你是对的,一个简单的整数就可以了,我在它上面。
    • 我经常在想,为什么这比使用dynamic_cast 更好?
    • 我从来没有说过它更好:) OP 明确要求一种避免它的方法,所以我展示了如何使用它。 dynamic_cast 速度较慢,但​​也更强大且更安全。
    【解决方案3】:

    您可以使用模板函数。使用模板可以在以后添加更多类而无需更改原始类,只需在另一个头文件中添加另一个模板函数即可。如果唯一的问题是编译速度 - 您可以在源文件中实现模板函数,而不是头文件并使用显式模板实例化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-14
      • 1970-01-01
      • 1970-01-01
      • 2017-07-09
      • 2012-02-14
      相关资源
      最近更新 更多