首页 > 解决方案 > 静态变量不适用于 .so 文件

问题描述

Class UserClass{
    public:
     int rollNo;
}

在我的应用程序中,我只需要一个UserClass对象。这是我无法修改的第 3 方非单例类。

File-name: userinclude.h
-------------------------
class Base {
public:
    static UserClass* getUserInstance(){
        static UserClass mUser;
        return &mUser;
    }
};

1)我有一个使用userinclude.h的文件lib.cpp。在这个文件中有一个print()函数,它打印rollNo。 例如。cout<< Base::getUserInstance()->rollNo;

从 lib.cpp 创建 lib.so。

2)我有另一个文件main.cpp也使用userinclude.h。在这里面有一个函数setUserRollNo(int r)设置rollNo
例如。Base::getUserInstance()->rollNo = 5;

main.cpp文件lib.so使用类加载器动态加载。从 main.cpp创建main.exe

问题:- 运行应用程序后,我观察到即使我使用 setUserRollNo() 更改 rollNo,print() 总是打印 0;调试后我注意到 getUserInstance() 在 main.cpp 和 lib.cpp 中返回不同的地址。来自同一文件的多次调用提供相同的地址,但它们在不同文件之间不匹配。因此创建了 2 个实例。

我试图将 mUser 作为类的静态成员,但遇到了同样的问题。

我的应用程序是单进程应用程序,因此整个应用程序的数据段(存储静态的地方)应该相同。因此,本地静态 mUser 应该只创建一次,并且 getUserInstance() 应该在整个应用程序中始终返回相同的地址。但我很惊讶看到这种行为。

C++:14
操作系统:Linux

请帮助我确定问题。

标签: c++static

解决方案


static UserClass* getUserInstance(){
    static UserClass mUser;
    return &mUser;
}

像这样在头文件中的内联函数声明意味着这个函数实际上被编译到每个包含这个头文件的 C++ 源文件中。每个调用的 C++ 源代码getUserInstance()都会将此函数编译为它的一部分。根据定义,内联函数就是这样工作的。

C++ 中的“一个定义规则”要求每个函数、方法或对象实际上只出现一次。那么,您如何调和这一点呢?

这是链接器的工作。技术细节并不重要,但最重要的是,当链接到最终程序时,重复的副本会从最终程序中删除。

如果链接器生成一个共享库,一个副本将getUserInstance()成为共享库的一部分。之后,如果一个程序被编译并包含此头文件,然后与此共享库链接,则链接器在与共享库链接时getUserInstance()删除该程序的副本。链接器看到这个内联函数已经存在于共享库中,并删除所有其他副本,以符合单一定义规则。

但是,如果您的程序与共享库链接,则不会发生这种情况。链接器绝对没有任何其他信息可以指定此内联函数已经在另一个共享库中。随后,如果程序动态加载共享库,则存在内联函数的第二个副本,这违反了一个定义规则。

在动态加载共享库时,无法删除重复的内联代码。只有链接器会这样做。

唯一能做的就是把内联定义从头文件中去掉,编译到头文件所属的共享库中。


推荐阅读