0

我有一个 Central 类的私有静态成员变量,带有类型映射。我想用“ Base* ”指针填充这个映射,这些指针指向派生自“ Base ”的类的实例。派生类的这些实例必须存储在动态内存中。还涉及到一些模板的乐趣。我确实尝试了一种填充地图的方法,但它给了我一个编译错误(稍后会说明)。

为了清楚起见,这是一个代码片段:

#include <all_necesary_std_headers>

class Base
{
    /* guts */
};

template<class CType>
class Derived: public Base
{
    /* innards */
};

class Central
{
    /* partial entrails */
private:
    // the static map I was referring to
    static std::map<string, Base*> base_map;
};

// Initializing the static map here
std::map<string, Base*> Central::base_map;

class Test1
{
    /* viscera */
};

// Compilation error here, on next line of code.
Central::base_map["Test1"] = dynamic_cast<Base*>( new Derived<Test1>);

class Test2
{
    /* bowels */
};

// Compilation error here, on next line of code.
Central::base_map["Test2"] = dynamic_cast<Base*>( new Derived<Test2>);

这是我得到的编译错误: error: expected constructor, destructor, or type conversion before '=' token;

我已经有一个析构函数来释放映射分配的内存,所以不需要提醒我。我想在 main() 中使用“Central”类。这个类结构将用于从存储在文件中的类名动态创建类的新实例。

希望很清楚,如果有什么不明白的,请说。

4

2 回答 2

2

赋值语句只能进入函数,不能进入命名空间范围。

在 C++11 中,您可以在其声明中初始化映射:

std::map<string, Base*> Central::base_map = {
    {"Test1", new Derived<Test1>},
    {"Test2", new Derived<Test2>}
};

如果你被困在过去,那么你需要使用一个函数来填充地图;也许是这样的:

std::map<string, Base*> make_base_map() {
    std::map<string, Base*> map;
    map["Test1"] = new Derived<Test1>;
    map["Test2"] = new Derived<Test2>;
    return map;
}

std::map<string, Base*> Central::base_map = make_base_map();

dynamic_cast另外,请注意,除非这些类型是多态的(即Base至少具有一个虚函数),否则您不能使用。正如我在示例中所做的那样,您可以在没有演员表的情况下转换Derived<T>*为;Base*但是如果你需要转换回来,dynamic_cast那么你需要确保它们是多态的。

更新:如果您希望能够通过仅将代码添加到该类的实现文件来单独注册每个类,那么您需要声明一个静态对象,该对象在其构造函数中注册该类。现在我们遇到了初始化顺序的失败:如果静态对象在不同的​​翻译单元中初始化,那么初始化顺序是未指定的,所以你不能从另一个的构造函数中安全地访问一个。

我们可以通过使映射成为局部静态变量来解决这个问题,在第一次访问时初始化:

static std::map<string, Base*> & base_map() {
    static std::map<string, Base*> map;
    return map;
}

现在我们可以定义类来注册类:

template <typename T>
struct BaseMapEntry {
    explicit BaseMapEntry(string name) {
        base_map()[name] = new Derived<T>;
    }
};

并将其用于每个类:

// header file
class Test1
{
    static BaseMapEntry<Test1> entry;
    /* gubbins */
};

// source file
BaseMapEntry<Test1> Test1::entry("Test1");

(或者,您可以将其设置为源文件中的静态命名空间范围对象,而不是静态成员;选择主要是为了美观。不幸的是,由于单一定义规则,您所做的任何事情都必须在源文件中定义.)

于 2013-01-23T12:59:14.393 回答
0

使用此行向映射添加新值(来自成员函数或 ctor):

Central::base_map["Test1"] = new Derived<Test1>();

如果你想在初始化时用一些值填充它,使用boost::assign或 C++11:

std::map<string, Base*> Central::base_map = { 
  {"Test1", new Derived<Test1>()}, 
  {"Test2", new Derived<Test2>()} 
};
于 2013-01-23T12:53:29.220 回答