在unordered_map中使用抽象类作为值

4

我在使用抽象类作为值时无法正确编译抽象类。理想情况下,我希望像下面这样做:

unordered_map<string, Process_Base> func_map;

Process_Base的外观如下:

//Contained in Process_Base.hpp
class Process_Base{

public:
    virtual ~Process_Base(){};
    virtual void process() const = 0;

};

子类的代码将会类似于:

#include "Process_Base.hpp"

class Process_Message : public Process_Base {

public:
    ~Process_Message(){};
    virtual void process();

};

#include <stdio.h>
#include <string.h>
#include "Process_Base.hpp"

class Process_Message{

public: 
    void process(){
        printf("%s", "Hello");
    }
};

这个想法是我可以将子类添加到地图上,并有一个简单的函数,该函数将查看键值并调用子类的处理函数。

当我在CentOS 5.8上编译时,使用的是:

g++44 -Wall -c -std=c++0x -I/usr/include -g Source.cpp

我收到了以下一系列错误:
In file included from /usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/bits/stl_algobase.h:66,
             from /usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/bits/char_traits.h:41,
             from /usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/string:42,
             from Source.cpp:2:
/usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/bits/stl_pair.h: In instantiation of ‘std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base>’:
/usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/bits/stl_function.h:482:   instantiated from ‘std::_Select1st<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base> >’
/usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/tr1_impl/hashtable_policy.h:790:   instantiated from ‘std::__detail::_Hash_code_base<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base>, std::_Select1st<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base> >, std::equal_to<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, false>’
/usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/tr1_impl/hashtable:137:   instantiated from ‘std::_Hashtable<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base>, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base> >, std::_Select1st<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base> >, std::equal_to<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::hash<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, false, false, true>’
/usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/tr1_impl/unordered_map:48:   instantiated from ‘std::__unordered_map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base, std::hash<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base> >, false>’
/usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/tr1_impl/unordered_map:190:   instantiated from ‘std::unordered_map<std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base, std::hash<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::equal_to<std::basic_string<char, std::char_traits<char>, std::allocator<char> > >, std::allocator<std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base> > >’
Source.cpp:14:   instantiated from here
/usr/lib/gcc/x86_64-redhat-linux6E/4.4.6/../../../../include/c++/4.4.6/bits/stl_pair.h:73: error: cannot declare field ‘std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, Process_Base>::second’ to be of abstract type ‘Process_Base’
Process_Base.hpp:1: note:   because the following virtual functions are pure within ‘Process_Base’:
Process_Base.hpp:5: note:       virtual void Process_Base::process() const
make: *** [Source.o] Error 1

有人可以帮我理解这些编译错误吗?是否尝试制作函数指针的映射比子类更好?如果是,那么如何创建指向像“处理消息”这样的类中函数的指针映射呢?


将其更改为unordered_map<string,Process_Base*>。您只能拥有指向抽象类的指针。 - alecbz
当我这样做时,我会得到一个不同的错误Source.cpp:14: error: expected constructor, destructor, or type conversion before ‘<’ token. - JME
如果你想在帖子中获得关于这个的帮助,你需要在大约第14行附上Source.cpp文件(或者指出如果它已经存在的话)。 - alecbz
您可以忽略我之前的评论,那是我的拼写错误。 - JME
1个回答

7

抽象类不能直接实例化,但是你可以拥有指向它们的指针,其值最终被实例化为非抽象子类。

class Abstract {
 public:
  virtual void Foo() = 0;
};

Abstract x;   // error
Abstract* p;  // fine

class Derived : Abstract {
 public:
  virtual void Foo() {
    printf("Hello!\n");
  }
};

p = new Derived();

// or:
Derived d;
p = &d;

由于这个原因,您也不能拥有抽象类的容器,只能拥有指向抽象类的指针。
unordered_map<string, Process_Base*> func_map;

根据您是如何填充(或尝试填充)原始的func_map而定,使用unique_ptr代替普通指针可能会更有益。

unoredered_map<string, unique_ptr<Process_Base>> func_map;

这样,你就可以做一些类似于 func_map["foo"] = new Derived() 的事情,而不必担心显式调用 delete func_map["foo"]

值得注意的是,这是一个非常适合使用智能指针(如boost::shared_ptr<Abstract>)的优秀案例,这样您就不必担心Abstract对象的生命周期。 - Dale Wilson
抽象类必须当然要有虚析构函数! - Dale Wilson
2
或者是 C++11 中的 std::unique_ptr,它保证了明确的所有权语义。 - dma
@DaleWilson 我不确定智能指针在抽象类的上下文中更/少有用的原因是什么? - alecbz
@alecbenzer 我提到使用智能指针的原因是因为原问题是关于将值放入无序映射中。如果你不是将实际值放入其中,而是将指向堆分配值的指针(Abstract *p = new Derived;)放入其中,那么你应该在某个地方有一个delete p,否则会泄漏内存。将其改为boost::shared_ptr<Abstract> p(new Derived);并将其作为智能指针映射解决了生命周期问题。 - Dale Wilson

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接