Address of a static variable
我正在尝试进行一个简单的类到唯一ID的转换。我正在考虑添加静态方法:
1 2 3 4 5 6 | class A { static int const *GetId() { static int const id; return &id; } }; |
然后,通过唯一的
我还考虑过指向
1 2 3 4 5 | class A { static std::type_info const *GetId() { return &typeid(A); } }; |
这样好些了吗?
编辑:
我不需要使用ID进行序列化。我只想标识一个小的基类集,我希望某个类的所有子类都具有相同的ID。
是的,这会奏效的。加载模块时,每个
静态变量的地址保证在所有翻译单元中都是唯一和相同的。
这不是一个好主意,因为它要求您向要标识的每个类添加代码。
指向类型信息对象的指针不保证是唯一的,但类型信息对象本身保证在给定类中比较为相等,在不同类中比较为不相等。这意味着您可以使用带有类型信息指针的小型包装器对象,并将比较委托给类型信息对象。C++ 11在标准库中有这样一个包装器,如果你不能访问它,Andrei Alexandrescu的"现代C++设计"中有一个,因此也可能在洛基库中,可能有一个在Boost中,在我的WordPress博客上有一个。
但是,如果要将ID用于序列化,则需要跨生成有效的ID。在这种情况下,您需要字符串或UUID。我会和UUID一起去。
要将类与UUID关联,通常可以使用类型特征类。或者,如果你只做Windows编程,那么你可以使用VisualC++的语言扩展。我认为,但我不能百分之百地肯定这些语言扩展也是由g++实现的(在Windows中)。
干杯!
正如我注意到的,至少MSVC 2008或2010优化了静态变量,因此下面的
1 2 3 4 | static int const *GetId() { static const int i = 0; return &i; } |
因此,不能使用未初始化常量静态变量的地址进行标识。最简单的解决方法是只删除
1 2 3 4 | static int *GetId() { static int i; return &i; } |
生成ID的另一个解决方案是使用全局函数作为计数器:
1 2 3 4 | int Counter() { static int i = 0; return i++; } |
然后在要标识的类中定义以下方法:
1 2 3 4 | static int GetId() { static const int i = Counter(); return i; } |
由于要定义的方法总是相同的,因此可以将其放入基类:
1 2 3 4 5 6 7 | template<typename Derived> struct Identified { static int GetId() { static const int i = Counter(); return i; } }; |
然后使用一个奇怪的循环模式:
1 2 3 | class A: public Identified<A> { // ... }; |
静态
1 2 3 4 5 | static std::type_info const& GetId() { static std::type_info const* id = &typeid(A); return id; } |
与使用
我过去使用的解决方案是:
1 2 3 4 | static char const* GetId() { return"A"; // Or whatever the name of the class is. } |
这在一个代码的执行,以及可以用作外部标识符,在所有编译器中都有保证。我们将其作为宏实现,宏定义了静态函数和返回它的虚拟函数,例如:
1 2 3 | #define DECLARE_IDENTIFIER(name) \ static char const* classId() { return STRINGIZE(name); } \ virtual char const* id() { return classId(); } |
这将导致非常快(但有限)的RTTI,并支持外部用于序列化和持久性的标识符。
显然,指向不同变量的指针必须具有不同的值。如果您选择派生a的子类,就要当心了。您需要决定ID的策略是什么。如果您什么都不做,那么子类将具有相同的ID。
int*方法将是唯一的,因为必须为每个静态变量分配一个不同的静态内存单元,我想理解它要比类型_info思想简单。
一般来说,你真的很想避免像这样的黑客行为。如果我真的必须这样做,我会考虑使用一些UUID系统(为此有一个库在Boost中,但我不太熟悉),或者使用一些单例来维护这些对象的列表,以满足您的任何需要。
静态变量在堆和堆栈内存之前初始化,所以它是唯一的。
虽然古怪。
因为必须将此方法添加到所有需要uid的类中,所以也可以这样做。
1 2 3 4 | unsigned int getUID() { return 12; } |
这样做的好处是,如果您将它用于RTTI以打开类型,编译器将能够使用跳转表,因为跳转表将非常稀疏,因此使用两个指针是不可能的。
(次要)缺点是您需要跟踪哪些标识符已被采用。
第一个方法的一个主要缺点是,不能使用相同的数字来标识对象,因为虚getuid()方法无法获取另一个函数作用域中变量的地址。