在派生类中实现虚函数时避免使用dynamic_cast

时间:2010-07-08 05:52:19

标签: c++ inheritance rtti dynamic-cast

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

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

但是,就这些操作而言,不同的派生对象彼此不兼容。我的问题是我是否可以避免使用RTTI来确保例如bool derived2 ::相同(const base * other2),断言(或其他退出机制)其中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
}

3 个答案:

答案 0 :(得分:2)

您可以使用双重发送(http://en.wikipedia.org/wiki/Double_dispatch

答案 1 :(得分:1)

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

  • 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语句,该条件总是评估为true(除了第一次)。
  • ==通常很快;)

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

还有一个无存储解决方案,涉及虚拟函数调用:

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

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

答案 2 :(得分:0)

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