3

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

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

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

3 回答 3

2

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

于 2010-07-08T05:56:24.043 回答
1

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

  • 一个enum,对所有类型进行分组。如果你有很多,它会变得很麻烦。
  • 生成 ids 的工厂(使用模板只为每个项目生成一个 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吸气剂......

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成员意味着您不必自己编写作业。

于 2010-07-08T07:05:40.803 回答
0

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

于 2010-07-09T08:33:38.877 回答