【问题标题】:Doubts about classes and polymorphism对类和多态性的怀疑
【发布时间】:2020-12-21 11:15:10
【问题描述】:

我正在尝试使用链接方法在 C++ 中构建一个哈希表类,一个最小的示例类是:

template <class V>                                                                  
class HashTable {                                                                                                                                                                                                            
private:                                                                                                       
  //some parameters to define the hash_table                                                                                                  
  HashFunc * h;  //family of hash functions                                                                                                                                                                                                                                                          
public:                                                                                                       
  /*other functions*/                                                                       
};

我在使用成员 HashFunc * h 时遇到了一些问题,这是我打算用来在哈希表中存储对 的哈希函数。在我的main 中,我需要构建两个不同的哈希表,一个散列整数,另一个散列字符串。

我的疑惑是:

  1. 对我来说,一个自然的选择是通过以下方式将多态性用于哈希函数(请注意,我对类和多态性的经验有限):
class HashFunc{
public:
  virtual int operator()()=0;
}
    
class IntegerHashFunc: public HashFunc
{
  //parameters to hash integers
 public:
  int operator()(int x){
    //operations for integer hashing
  }
}

class StringHashFunc: public HashFunc
{
  //parameters to hash strings
  IntegerHashFunc h_integer; //string->integer->integer hash
  public:
    int operator()(string name){
      int result=0;
      //operations for string hashing
      return h_integer(result); //I need to hash the integer resulting from string "hashing"
    }
}

但这不起作用,因为StringHashFunc 中的operator()(string name) 具有不同的参数,因此不会覆盖基类中的virtual 方法。如何使这项工作?

  1. 我不确定如何在HashTable 类的构造函数中进行这项工作:如何在两种方法之间进行选择?我是否应该在构造函数中使用一个标志,例如:
template<class V>
HashTable<V>::HashTable(/*args*/, int flag){
  //other parameters
  switch(flag){
    case 1:
      h=new IntegerHashFunc(/*args for integer hashing*/);
    case 2:
      h=new StringHashFunc(/*args for string hashing*/);
  }
}

或者有更好的方法吗?

  1. 有没有更好更自然的方法来做我想做的事?例如,我尝试使用单个类 HashFunc 和两个重载 operator()(int)operator()(string),但如果我只使用整数,我不想“携带”字符串参数和方法。

【问题讨论】:

  • “对我来说,一个自然的选择是使用多态性”。不。在这里停下来再想一想。是否有任何标准库容器使用多态性?为什么或为什么不?
  • 一个HashTable 应该包含一个StringHashFunc 和一个IntHashFunc,还是你总是只有一种类型的哈希函数?
  • 我是第二代词,来自不同的语言,我花了一些时间来欣赏 C++,现在运行时多态是我的最后手段,而不是像其他语言那样的 goto 解决方案
  • 如果V 是那种类型,那么您希望HashTable&lt;V&gt; 持有例如std::vector&lt; HashFunction&lt;V&gt; &gt;HashFunction&lt;V&gt; 有一个operator()(const V&amp;)
  • 您可以从std::unordered_map 获得一些灵感。模板是去这里的方式。你甚至不需要 HashFunc 类或任何东西。标准库处理它的方式你可以传递任何东西(函数、lamba、结构),只要它可以像函数一样被调用。

标签: c++ class hash polymorphism


【解决方案1】:

TL;DR - 所有问题的答案都是“使用模板,而不是多态性”。

对我来说一个自然的选择是通过以下方式将多态性用于哈希函数(请注意,我对类和多态性的经验有限):
但这不起作用,因为 StringHashFunc 中的 operator()(string name) 具有不同的参数,因此不会覆盖基类中的虚拟方法。如何使这项工作?

你不能。为了使派生类非抽象,您需要重写所有纯虚函数。而且即使您添加另一个带参数的重载,它也无济于事,因为您无法使用基类指针调用这些重载。
理论上,您可以使用dynamic_cast 来确定实际类型是什么(在很长很长的if 语句链中),但不鼓励这样做。

我不确定如何在 HashTable 类的构造函数中进行这项工作:如何在两种方法之间进行选择?我是否应该在构造函数中使用一个标志,例如:
还是有更好的方法?

一种选择是使用类型特征来选择正确的分支(假设您真的非常想使用多态性):

if(std::is_same_v<V, int>) 
    h=new IntegerHashFunc(/*args for integer hashing*/);
else if(std::is_same_v<V, std::string>)
    h=new StringHashFunc(/*args for string hashing*/);

有没有更好更自然的方式来做我想做的事?例如,我尝试使用具有两个重载 operator()(int) 和 operator()(string) 的单个类 HashFunc,但如果我只使用整数,我不想“携带”字符串参数和方法.

是的,使用模板,就像标准库哈希表(又名。std::unordered_map)一样:

template<typename T>
class HashFunc{
public:
  int operator()(const T&) const;
};

template<>
class HashFunc<int>{
public:
  int operator()(const int&) const {
    // do calculations for int
  }
};

template<>
class HashFunc<std::string>{
public:
  int operator()(const std::string&) const {
    // do calculations for std::string
  }
};

//etc... for all other specializations

template <class V>                                                                  
class HashTable {                                                                                                                                                                                                            
private:                                                                                                       
  //some parameters to define the hash_table                                                                                                  
  HashFunc<V> h;  //family of hash functions                                                                                                                                                                                                                                                          
public:                                                                                                       
  /*other functions*/                                                                       
};

或者更好的是,使用已经存在的std::hash,它正是这样做的,但已经实现了。

【讨论】:

  • 非常感谢!我知道std::unordered_map,这只是一个关于实现数据结构的练习。
  • 您还可以确保HashTable&lt;float&gt; 不会像这样使用static_assert 进行编译(例如在ctor 内部):public: HashFunc() { static_assert( std::is_same&lt;T,int&gt;::value || std::is_same&lt;T, std::string&gt;::value, "Oops bad hash");}
  • @Yksisarvinen 我有一个疑问:HashFunc&lt;int&gt;HashFunc&lt;string&gt; 的构造函数具有不同数量的参数,我该如何在主类 HashTable 中处理这个问题?
  • @luigi 这取决于你如何确定这些。你从哪里得到这些参数(即HashTable 如何知道将什么传递给那些HashFunc)以及为什么它们不能在HashFunc 专业化中初始化为默认值?
  • @Yksisarvinen 我需要在运行时选择它们并作为输入传递。 HashFunc&lt;int&gt; 需要 4 个参数,而 HashFunc&lt;string&gt; 需要 3 个。
【解决方案2】:

使用基于策略的设计,它会简单得多。

首先创建您的哈希函数对象:

struct IntegerHashFunc
{
  int operator()(int x){ return x; }
};

struct StringHashFunc
{
  IntegerHashFunc h_integer; //string->integer->integer hash
    int operator()(string name){
      int result=0;
      return h_integer(result);
    }
};

您的HashTable 类会将哈希函数作为模板参数。

template <typename Key, typename Val, typename HashFunctionPolicy = IntegerHashFunc>
class HashTable {                                                                                                                                                                                                            
private:                                                                                                       
  //some parameters to define the hash_table                                                                                                                                                                                                                                                         
public:                                                                                                       
  /*other functions*/
  void somefunction(Key key)
  {
      HashFunctionPolicy f;
      auto x = f(key);
  }                                                                   
};

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-08-10
    • 1970-01-01
    • 2018-10-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-14
    • 2023-03-09
    相关资源
    最近更新 更多