静态变量的地址

时间:2011-09-23 08:00:35

标签: c++

我正在尝试对一个简单的类进行唯一ID转换。我正在考虑添加一个静态方法:

class A {
  static int const *GetId() {
    static int const id;
    return &id;
  }
};

然后,每个班级将由唯一的int const *标识。这保证有效吗?返回的指针真的是唯一的吗?还有更简单的解决方案吗?

我还想过指向std::type_info的指针:

class A {
  static std::type_info const *GetId() {
    return &typeid(A);
  }
};

这样更好吗?

修改

我不需要使用id进行序列化。我只想识别一小组基类,并且我希望某些类的所有子类具有相同的id

9 个答案:

答案 0 :(得分:9)

是的,这会有效。每个static本地将在加载模块时被赋予不同的内存位置,并且它将持续存在直到模块被卸载。请记住,static本地存储在编译期间分发的静态存储中,它们会一直存在,直到模块被卸载,因此它们将具有不同的内存位置。

答案 1 :(得分:7)

静态变量的地址保证是唯一的,并且在所有翻译单元中都是相同的。

这不是一个好主意,因为它要求您为每个要识别的类添加代码。

指向类型信息对象的指针不保证是唯一的,但类型信息对象本身保证比较给定的类相等,不同的类不相等。这意味着您可以使用带有类型信息指针的小包装器对象,并委托对类型信息对象进行比较。 C ++ 11在标准库中有这样的包装器,如果你没有访问权限,那么Andrei Alexandrescu的“现代C ++设计”就有一个,因此也许在Loki库中,可能有一个在Boost,我的Wordpress博客上有一个 - 你不喜欢从头开始发明一个。

但是,如果要将id用于序列化,则需要在构建版本中有效的id。在这种情况下,您需要字符串或UUID。我会选择UUIDs。

要将类与UUID相关联,通常可以使用类型特征类。或者,如果您只是进行Windows编程,那么您可以使用Visual C ++的语言扩展。我想但我并不是100%确定那些语言扩展也是由g ++实现的(在Windows中)。

干杯&第h

答案 2 :(得分:2)

静态int的地址保证每个地址都是唯一的 函数(对于同一函数的每次调用都是一样的)。因此, 它可以在单次执行代码中作为id工作。 地址可以想象地从一次运行变为另一次运行,并且会 通常会从一个编辑更改为下一个编辑(如果您已更改 代码中的任何内容),因此它不是外部id的好解决方案。 (您没有说明id是否必须在单次执行之外有效 或不。)

typeid的结果地址不保证是。{ 每次调用该函数时都会一样(尽管可能会这样)。 但是,您可以使用它来初始化指针:

static std::type_info const& GetId()
{
    static std::type_info const* id = &typeid(A);
    return id;
}

与使用int*相比,这具有提供额外功能的优势 信息(例如用于调试)。与int*类似,标识符可以是 不同于一次奔跑; A::GetId()->name()会指出 相同的'\0'终止字符串(尽管地址可能是 如果您使用相同的编译器进行编译。 (就我而言 可以说,标准并不保证这一点,但在实践中,我认为 你是安全的。但是,改变编译器,所有的赌注都已关闭。

我过去使用的解决方案是:

static char const* GetId()
{
    return "A";  //  Or whatever the name of the class is.
}

这提供了一个易于比较的唯一标识符 执行代码,以及可以用作的字符串值 外部标识符,并且在所有编译器中都有保证。我们 将它实现为一个宏,它定义了静态函数和 返回它的虚函数,例如:

#define DECLARE_IDENTIFIER(name)                                    \
    static char const* classId() { return STRINGIZE(name); }        \
    virtual char const* id() { return classId(); }

这导致非常快(但有限)的RTTI,并支持外部 序列化和持久性的标识符。

答案 3 :(得分:2)

正如我已经注意到,至少MSVC 2008或2010优化了静态变量,因此即使对于不同的类,以下GetId函数也会返回相同的地址。

static int const *GetId() {
    static const int i = 0;
    return &i;
}

因此,未初始化的常量静态变量的地址不能用于识别。最简单的解决方法是删除const

static int *GetId() {
    static int i;
    return &i;
}

另一种生成ID的解决方案似乎是有效的,它使用全局函数作为计数器:

int Counter() {
    static int i = 0;
    return i++;
}

然后在要识别的类中定义以下方法:

static int GetId() {
    static const int i = Counter();
    return i;
}

由于要定义的方法始终相同,因此可以将其放在基类中:

template<typename Derived>
struct Identified {
    static int GetId() {
        static const int i = Counter();
        return i;
    }
};

然后使用一种奇怪的重复模式:

class A: public Identified<A> {
    // ...
};

答案 4 :(得分:1)

int *方法是唯一的,因为必须为每个静态变量分配一个不同的静态存储单元,我猜想理解type_info的想法更简单。

答案 5 :(得分:1)

显然,指向不同变量的指针必须具有不同的值。请注意,如果您选择派生A的子类,您需要确定您的id政策。如果你什么也没做,那么子类将具有相同的id。

答案 6 :(得分:0)

静态变量在Heap&amp;之前初始化。堆叠内存所以是的,它将是独一无二的。

古怪但是。

答案 7 :(得分:0)

一般来说,你真的很想避免像这样的hacky事情。如果我真的必须这样做,我会考虑使用一些UUID系统(Boost中有一个库,但我不是很熟悉它),或者是为了任何目的维护这些对象列表的一些单例你需要。

答案 8 :(得分:-1)

因为您必须将此方法添加到需要UID的所有类中,您可以这样做。

unsigned int getUID()
{
    return 12;
}

这样做的好处是编译器能够使用跳转表,如果你将它用于RTTI来切换类型,这对于两个指针是不可能的,因为跳转表非常稀疏。

(次要)缺点是您需要跟踪已经采用的标识符。

您提出的第一种方法的主要缺点是相同的数字不能用于标识对象,因为虚拟的getUID()方法将无法在另一个函数的范围内获取变量的地址。

相关问题