简体   繁体   English

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

[英]Avoiding dynamic_cast in implementation of virtual functions in derived class

Here is some sample code explaining what I am trying to achieve. 这是一些示例代码,解释了我想要实现的目标。

Basically, I have an algorithm that depends on some basic operations available in a class. 基本上,我有一个算法,它依赖于类中可用的一些基本操作。 I have defined those operations in a pure abstract base class. 我已经在纯抽象基类中定义了这些操作。 I want to apply that algorithm to a variety of objects that provide those operations by deriving classes for the specific objects. 我想将该算法应用于各种对象,这些对象通过派生特定对象的类来提供这些操作。

However, the different derived objects are incompatible with one another as far those operations are concerned. 但是,就这些操作而言,不同的派生对象彼此不兼容。 My question is whether I can avoid using RTTI to ensure that for example, bool derived2::identical(const base* other2), asserts(or other exit mechanism) where other2 is not of type derived2. 我的问题是我是否可以避免使用RTTI来确保例如bool derived2 :: identical(const base * other2),断言(或其他退出机制)其中other2不是derived2类型。

One alternative would be to template the function algorithm on the specific derived object, but that would mean that it's implementation would have to live in a header file which I don't want to do since 1) Changing the algorithm code for test purposes can cause recompilation of large portions of the code 2) The algorithm's implementation would be exposed in the header instead of living nicely in a source file hidden from the end-user. 一种替代方法是在特定的派生对象上模拟函数算法,但这意味着它的实现必须存在于我不想做的头文件中1)为了测试目的而更改算法代码会导致重新编译大部分代码2)算法的实现将在头文件中公开,而不是生活在最终用户隐藏的源文件中。

Header file 头文件

#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);

Implementation of algorithm Source file 算法源文件的实现

#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;
}

Main program 主程序

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

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

Well, there is one simple thing: store the real type as a member. 嗯,有一个简单的事情:将真实类型存储为成员。

  • An enum , grouping all the types. enum ,将所有类型分组。 It'll become cumbersome if you have a lot of them. 如果你有很多它会变得很麻烦。
  • A Factory to generate ids (using templates to only generate one id per item) 生成ID的工厂(使用模板仅为每个项生成一个ID)
  • ... ...

I'll illustrate the factory 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

And you can use it like such: 你可以像这样使用它:

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)) {}
};

Then you can use the mId member for testing. 然后,您可以使用mId成员进行测试。 Note that since it's const it can be exposed... otherwise you can create an inline const getter... 请注意,因为它是const所以它可以暴露...否则你可以创建一个内联const getter ...

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

  // ...
}

The cost here is negligible: 这里的成本可以忽略不计:

  • GetId is inlined, thus no function call GetId是内联的,因此没有函数调用
  • GetId is lazily initialized, apart for the initialization it amounts to checking that the static member has been initialized: it's typically implemented as a if statement which condition always evaluate to true (apart from the first time). GetId是懒惰地初始化的,除了初始化它相当于检查static成员是否已经初始化:它通常被实现为if语句,条件总是评估为true(除了第一次)。
  • == is normally fast ;) ==通常很快;)

The only downside is that you actually need to make sure that you correctly initialize the ids. 唯一的缺点是,您确实需要确保正确初始化ID。

There is also a no-storing solution, which involves a virtual function call: 还有一个无存储解决方案,涉及虚函数调用:

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

It's easier to put in practice because not storing a const member means that you don't have to write the assignment yourself. 实践更容易,因为不存储const成员意味着您不必自己编写作业。

You could use a templated function. 您可以使用模板化功能。 With templates it is possible to add more classes later without the need to change the original classes, by just adding another template function in another header file. 使用模板,可以在以后添加更多类,而无需更改原始类,只需在另一个头文件中添加另一个模板函数即可。 If the only problem is the compile speed - you can implement the template function in a source file apart from the header and use explicit template instanciation. 如果唯一的问题是编译速度 - 您可以在除标题之外的源文件中实现模板函数并使用显式模板实例化。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM