【问题标题】:How do I make an unreferenced object load in C++?如何在 C++ 中加载未引用的对象?
【发布时间】:2010-12-28 17:38:13
【问题描述】:

我有一个.cpp 文件(我们称之为statinit.cpp)使用gcc 编译并链接到我的可执行文件中。 我的main() 函数在statinit.cpp不是

statinit.cpp 有一些我需要运行的静态初始化。 但是,我从来没有在我的main() 中明确引用来自statinit.cpp 的任何内容,或者它所引用的任何内容。 会发生什么(我想)是从statinit.cpp 创建的链接对象永远不会在运行时加载,所以我的静态初始化永远不会运行,导致代码中的其他地方出现问题(这很难调试,但我最终跟踪它)。

是否有标准库函数、链接器选项、编译器选项或可以让我强制该对象在运行时加载而不引用其元素之一的东西?

我想做的是在 statinit.cpp 中定义一个虚拟函数,在 main() 看到的头文件中声明它,然后从 main() 调用该虚拟函数。但是,这是一个非常丑陋的解决方案,我非常希望避免对 statinit.cpp 本身进行更改。

谢谢, 丹尼尔

【问题讨论】:

  • 当您说“程序集”时,这是否意味着这是一个 .NET 问题?如果是这样,请将其标记为这样。如果不是,请编辑问题以使用不同的术语。
  • 它在问题中说 gcc,所以它不是 .NET
  • 显然“组装”不是正确的术语。改为“对象”(我希望是对的)。
  • 你用statinit.cpp建静态库吗?然后链接到静态库?这可能是个问题。
  • “静态初始化”是什么意思?这不是 C++ 概念。您是否使用全局变量初始化?如果它们在命名空间中,这可能是个问题。

标签: c++ loader


【解决方案1】:

目前还不清楚问题是什么:

C++ 没有静态初始化器的概念。
所以有人假设你在“文件范围”中有一个对象。

  • 如果此对象在全局命名空间中,那么它将在 main() 被调用之前构造并在 main() 退出后销毁(假设它在应用程序中)。
  • 如果此对象位于命名空间中,则实现可以选择延迟初始化变量。这只是意味着它将在首次使用之前完全初始化。因此,如果您依赖于构造的副作用,请将对象放在全局命名空间中。

现在你可能看不到这个对象的构造函数执行的一个原因是它没有链接到应用程序中。这是链接器问题,而不是语言问题。当对象被编译成静态库并且您的应用程序随后链接到静态库时,就会发生这种情况。链接器只会加载从应用程序显式引用的应用程序函数/对象(即解析符号表中未定义事物的事物)。

要解决这个问题,您有几个选择。

  • 不要使用静态库。
    • 编译成动态库(现在的常态)。
    • 将所有源代码直接编译到应用程序中。
  • 在 main 中明确引用对象。

【讨论】:

  • 您是说使用 .a 静态库文件还是使用 .so 共享对象(动态库)文件会有所不同?有趣的。这可能确实是我问题的答案。明天我会尝试更改它并报告。谢谢。
  • 嗯,我的问题和那个人的问题是一样的,但是我在发帖之前没有找到。我应该改写一下,然后我会立即得到答案:)
【解决方案2】:

我遇到了同样的问题。

写一个文件,DoNotOptimizeAway.cpp:

void NoDeadcodeElimination()
{
    // Here use at least once each of the variables that you'll need.
}

然后从main 调用NoDeadcodeElimination()

编辑:或者,您可以编辑链接器选项并告诉它始终链接所有内容,即使它没有被使用。不过我不喜欢这种方法,因为可执行文件会变得更大。

【讨论】:

  • 感谢您的回答。使用我未引用文件中的变量的问题在于,在这个特定文件中,只有具有复杂构造函数的类需要预构建参数,并且没有静态成员。我发现在文件中添加我自己的小变量或函数并引用它可以解决问题,但我想避免在文件中进行更改。
  • 您所说的链接器选项是什么?我认为这可能是这种情况下的解决方案。我的项目中可能没有很多未使用的代码(如果有的话),但我可能是错的。
【解决方案3】:

这些问题以及这些潜在解决方案的问题都围绕着这样一个事实,即您无法保证静态初始化的很多内容。所以既然不靠谱,就不要依赖了!

使用静态“InitializeLibrary”类型的静态函数显式初始化数据。现在您可以保证它会发生,并且您可以根据您的调用时间来保证与其他代码相关的它何时发生。

【讨论】:

  • 感谢您的建议,但不幸的是,这不是我的选择。在我开始工作之前,这个项目中已经使用了静态初始化,我应该在不改变它的情况下使用该文件(除非有其他方法)。消除项目中的静态初始化并用更好的解决方案替换它们是可能的,但我正在寻找更直接的解决方案。
  • 静态初始化定义好(除了顺序,好像不是问题)。
【解决方案4】:

一种 C++ 风格的方法是使用单例。

本质上,编写一个函数来返回对对象的引用。要强制它初始化,请将其设为函数内的静态对象。

制作一个类静态函数,大致如下:

class MyClass {
   static MyClass& getObject()
   {
        static MyObject obj;
        return obj;
    }
};

【讨论】:

  • 我尝试过这样做,但我无法包含 .cpp 文件,并且没有任何头文件声明其中的任何内容。
  • @Martin:如果您将 getObject 设为静态函数并将构造函数设为私有。否则你会怎么做?
【解决方案5】:

由于您使用的是 C++,因此您始终可以声明一个全局对象(即引用 statinit.cpp 中的类的全局变量。与往常一样,构造函数将在初始化时调用,并且由于对象是全局的,这将是在 main() 运行之前调用。

不过,有一个非常重要的警告。无法保证何时调用构造函数,也无法明确地确定调用每个构造函数的顺序。这也可能会挫败任何检查内存泄漏的尝试,因为您不能再保证在运行 main 时分配的所有内存都已被释放。

【讨论】:

  • 如果它在库中,则无法保证全局对象的构造函数会被调用。事实上,可能不会。
【解决方案6】:

是静态项从未初始化的问题,还是需要使用静态项时未初始化的问题?

所有静态初始化都应该在你的 main() 运行之前完成。但是,如果使用另一个静态对象对静态对象进行初始化,则可能会遇到问题。 (注意:这不适用于使用 int 等原语)

例如,如果您在文件 x.cpp 中有:

static myClass x(someVals);

在 y.cpp 中:

static myClass y = x * 2; 

系统可能会在创建 x 之前尝试实例化 y。在这种情况下,“y”变量可能为 0,因为 x 在初始化之前可能为 0。

一般来说,最好的解决方案是在第一次使用对象时实例化它(如果可能的话)。但是,我在上面注意到您不允许修改该文件。该文件中的值是否正在其他地方使用,也许您可​​以更改访问这些值的方式?

【讨论】:

  • 我从不直接使用该文件中的任何内容,但静态初始化具有将创建的对象附加到全局数据结构的副作用。问题是静态初始化永远不会运行,因此该结构不包含我所期望的。
【解决方案7】:

阅读 ld 命令的手册页并查看 -u 选项。如果 statinit.cpp 定义了任何看起来像函数的东西,那么尝试在 -u 中命名它。否则选择一个在 statinit.cpp 中定义的数据对象并在 -u 中命名,并希望它能正常工作。我认为最好编写命令行,这样 -u 选项就在你的库的 -l 选项之前,其中包含 statinit 的目标代码。

【讨论】:

  • 谢谢,我认为这可能是使用动态库的下一个最佳解决方案。
【解决方案8】:

当然动态库解决方案是最好的,但我也被告知可以使用链接器选项链接整个静态库:

-Wl,-whole-archive

在库的 -l 选项之前,并且

-Wl,-no-whole-archive

在它之后(避免将其他库也作为一个整体包含在内)。

【讨论】:

    猜你喜欢
    • 2015-05-24
    • 2010-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多