如果方法在类中定义,则阻止编译器删除方法

Prevent compiler from removing methods if methods are defined inside class

本文关键字:方法 删除 编译器 定义 如果      更新时间:2023-10-16

免责声明:

我对以下风格是否是"良好做法"的意见不感兴趣。我正在寻找解决问题的答案/方法。

问题:

我正在 cpp 文件中编译一个类。编译过程剥离了我的方法定义,它认为这些定义是"未使用的"。有什么办法可以防止这种情况吗? (请参阅下面的开发环境示例(

开发环境:

我现在正在使用g++ --std=c++98。一个完美的解决方案将在所有 c++98 标准编译器和更新版本上提供。

例:

例1.cpp

#include <stdio.h> 
class Example1{
public: 
void print(){printf("helloWorld");};
};

编译、汇编和链接该文件不会创建print()方法。(g++ --std=c++98 example1.cpp main.cpp,其中main.cpp简单地调用打印方法。

编译这个(没有链接,g++ --std=c++98 -S(将输出:

例1.s

.file   "example1.cpp"
.text
.ident  "GCC: (Ubuntu 9.2.1-9ubuntu2) 9.2.1 20191008"
.section    .note.GNU-stack,"",@progbits
.section    .note.gnu.property,"a"
.align 8
.long    1f - 0f
.long    4f - 1f
.long    5
0:
.string  "GNU"
1:
.align 8
.long    0xc0000002
.long    3f - 2f
2:
.long    0x3
3:
.align 8
4:

这里显然没有生成print()方法。

不好的解决方法

如果我在同一文件中使用它,我可以让编译器生成该方法(请参阅下面的代码(。但是,我正在寻找更好的解决方案。也许编译器选项?

例1.cpp

#include <stdio.h> 
class Example1{
public: 
void print(){printf("helloWorld");};
};
// Create a function outside the class, that uses print().
// That way, print will be generated.
void foo(){
Example1 ex;
ex.print();
}

备注(编辑(:

  • 在类之外定义方法是不可能的。
  • Example1main.cpp中的用法如下:

主.cpp

class Example1{
public:        
void print();
};
int main(){
Example1 example;
example.print();
return 0;
}    

简短的回答是"你不能使用标准C++"。

较长的答案分为几个部分。

首先,您的解决方法没有实现您认为的效果。

#include <stdio.h> 
class Example1{
public: 
void print(){printf("helloWorld");};
};
// Create a function outside the class, that uses print().
// That way, print will be generated.
void foo(){
Example1 ex;
ex.print();
}

这不会阻止编译器Example1::print()"删除"成员函数。 没有什么可以阻止编译器在foo()内联Example1::print()调用,并且(使用您的措辞(以(例如(可以通过链接器解析的形式生成该函数。

二、你的"主.cpp">

如果
class Example1{
public:        
void print();
};
int main(){
Example1 example;
example.print();
return 0;
}    

与"example1.cpp"的任一版本在同一项目中使用它,则会引入未定义的行为,因为类Example1的定义明显不同 - 一个在类定义中定义成员函数print(),另一个则不然。 这打破了单定义规则,因为您的程序有两个不同的类Example1定义。

如果你的第二个"example1.cpp"似乎"有效"(无论你如何评估(,那么你就是很幸运。 未定义行为的本质意味着它似乎可以工作。 问题是,C++标准没有给出任何这样的保证 - 当行为未定义时,编译器被允许做它喜欢的事情 - 你的代码似乎可以"工作",或者它可能做一些完全不同的事情。

通常解决此类问题的方式是将类Example1的定义放在单独的头文件中,例如

// example1.h
#include <stdio.h>
class Example1{
public: 
void print(){printf("helloWorld");};
};

然后将该标头包含在需要它的所有源文件中。 例如;

//   example1.cpp
#include "example1.h"
void foo()
{
Example1 ex;
ex.print();
}

// main.cpp
#include "example1.h"
int main()
{
Example1 example;
example.print();
return 0;
}

然后,上述两个源文件(示例 1.cpp 和 main.cpp(可以在同一程序中安全地使用。

由于函数Example1::print()是内联定义的,因此编译器可以自由地内联它。 编译器仍然不需要内联,但允许这样做。 这意味着,生成的对象文件或可执行文件不一定包含任何可识别的(例如通过检查程序集(作为函数Example1::print()。 在您的示例中,可能只有main()foo()中的代码调用printf(),但没有明显名为Example1::print()的函数。

正如另一个答案(您拒绝了,随后被删除(所说,防止内联的唯一方法是在类定义之外定义函数。 该函数定义需要恰好位于项目中的一个编译单元中。 这允许编译器"生成"函数,但在技术上不需要它(允许智能编译器和链接器"删除"该函数(。

根据彼得的评论Microsoft具体

从这里

您可以创建对无法优化的调试函数的错误引用。为此,请将指向调试函数的指针传递给模块外部的帮助程序函数,该函数不执行任何有趣的操作。由于帮助程序函数不在模块中,因此编译器不知道帮助程序函数不执行任何操作,因此无法优化调试函数。

struct ForceFunctionToBeLinked { 
ForceFunctionToBeLinked(const void *p) { SetLastError(PtrToInt(p)); }
};
ForceFunctionToBeLinked forceTestMe(TestMe);