从不同编译单元访问的静态变量可能存在问题?

时间:2009-09-09 21:40:29

标签: c++

当我作为软件开发人员的第一份工作开始时,我被指派创建一个系统,允许从PDF文档中写入和读取C ++值(或对象)。这需要一个用于将类型映射到id的系统,反之亦然。代码库非常庞大,并且有几个“层”代码(基本框架层,工具层,视图层等)。建议的解决方案是向每个图层添加一个头文件,其枚举包含特定图层中定义的类型的ID。为避免冲突,每个枚举的第一个值都以偏移值(1000,2000,3000,...)开头。

由于这个系统对我来说似乎有些笨拙,所以我决定尝试别的东西,并提出了代表独特ID的课程:

// UniqueId.h
class UniqueId
{
public:
    UniqueId();

    operator int() const;

private:
    int mId;
    static int sCounter;
};

// UniqueId.cpp
int UniqueId::sCounter = 0;

UniqueId::UniqueId()
{
    mId = ++sCounter;
}

UniqueId::operator int() const
{
    return mId;
}

现在只需创建一个UniqueId对象列表而不用担心冲突的ID,而不是枚举。

审核我的代码的人(公司的软件架构师)首先同意这一点。但是,一天后他改变主意,告诉我这个系统不起作用。我依稀记得他提到多个编译单元会出现问题。

今天,大约四年后,我仍然不明白问题本来是什么。据我所知,静态变量只能在一个编译单元(UniqeId.cpp)中定义。

所以..由于StackOverflow是如此丰富的资深开发人员,我想借此机会要求澄清一下。这确实是一个问题吗?或者我的想法好吗?

3 个答案:

答案 0 :(得分:4)

他可能指的是Static Initialization Order Fiasco

我发现你发布的内容没有问题。您需要依赖其他翻译单元中的静态变量来解决惨败问题。

答案 1 :(得分:4)

在程序的不同运行之间为“类型”分配相同的ID是否重要?如果没有,一切都应该没问题。如果是的话......

不同的“类型”如何抓取他们的ID?一种方法可能是:

class FooType
{
private:
    static UniqueId myId;
...
};

这会调用Fred Larson链接到的static initialization order fiasco。由于静态的初始化顺序未定义,因此从构建到构建可能会获得分配给myId的不同值,或者如果您真的不幸,则从运行到运行。

您可以仅在需要时创建UniqueId的实例,例如在对象的构造函数中或作为具有静态变量的方法。例如:

class BarType
{
private:
    const UniqueId &getMyId()
    {
        static UniqueId myId;
        return myId;
    }
    ....
};

现在您可能不得不担心线程安全(如果您正在开发多线程程序)。除此之外,UniqueId值在很大程度上取决于程序的流程,并且很可能在运行之间发生变化。在程序的一次运行中,您可能永远不需要实例化BarType,因此它不会声明ID。在另一场比赛中,您可能需要提前BarType;在另一个你可能需要BarType之后。

更加险恶的是,一切都可能适用于许多版本,直到你忘记了所有这些设置。有人添加了一个新的“类型”,一些现有的类型,或者很可能做出一个完全不相关的变化。然后突然之间所有因为上面列出的原因之一而中断。

答案 2 :(得分:1)

您的代码对我来说似乎很好,并且明确地比系统的所有“层”中具有保留值的枚举更好。即使以这种方式使用静力学存在问题,也会有解决方案,例如GUID。你的想法确实很好。请记住,软件架构师只是一个提高工资的开发人员的名字 - 这并不意味着他比你更熟练;)