【问题标题】:Using preprocessor #ifdef on a class field在类字段上使用预处理器#ifdef
【发布时间】:2013-06-20 18:44:03
【问题描述】:

这个类是在我的项目中定义的:

class B : public A
{
 public:
 A& Get_a(int type);

 ...
 protected:
 #ifdef COMPILE_FLAG
 int line_num;
 const char* file_name;
 #endif
 ...
 private:
 int int_value;
 bool bool-val;
};

函数Get_a是这样实现的(我只放了相关部分):

A& B::Get_a(int type)
    {
     B* returned_a = B->Get_val(type);
     return *(A*)(returned_a) ;
    }

这是我使用这个类的代码:

{
...
B b_val;
A* a_val = &b_val->Get_a(5);
...
}

我的代码被编译成与 A 类和 B 类编译成的 DLL 不同的 DLL。 我的 DLL 不使用标志 COMPILE_FLAG 编译,但 A、B 的 DLL 确实使用此标志编译。 即使我的代码试图从不同的 B 类转换,我也没有收到任何编译错误。 在我的代码执行期间,a_val 有垃圾字段值;实际上改变了值。

我想解释一下为什么 C++ 编译器没有警告我这个错误,以及如何改进这些 #ifdef 定义的编码提示(我被告知这些字段用于日志记录)。

编辑:如果我要编写 2 个具有相同名称的类,则每个类都在不同的 dll 中。然后,如果我要在它们之间创建一个引用,我会得到一个编译错误。预处理器在编译器之前运行,因此编译器可以检查这个类的重新定义。

【问题讨论】:

  • 编译器无法知道用于编译一个 DLL 的标头与用于编译另一个 DLL 的标头不同,或者由加载其中任何一个的客户端代码使用,这就是这里发生的情况。
  • 由于class B类型的定义在两个单独编译的翻译单元中是不同的(程序和DLL是独立的TU,每个TU实际上可能由多个TU组成),您正在调用未定义的行为。编译器、链接器和运行时系统都没有义务提醒您将遇到的问题。您必须确保全局类型 class B 在程序中的所有位置都相同。

标签: c++


【解决方案1】:

#ifdef 是一个预处理器指令。预处理作为编译的第一部分执行。只有在预处理之后,正常的 C++ 语法才必须是有效的并且可以被检查。

您的#ifdef 违反了One Definition Rule:在不同的翻译单元中,B 类有两种不同的定义(大小不同)。编译器无法检测到这一点。作为结论,我会说这是您代码中的错误。作为编码提示,我建议使用相同的编译器设置编译所有文件。在这种特殊情况下,我将删除 #ifdef。

【讨论】:

    【解决方案2】:

    我想解释一下为什么 C++ 编译器没有警告我这个错误

    编译器应该如何警告你?您编译了两个不同的集合,具有不同的选项。由于类定义发生变化,由于这些标志,每个二进制文件中的内存分配不同。但是编译器怎么会知道呢?

    编译不同的版本是一个完全有效的场景,正确使用它们取决于程序员/用户。

    所以你必须在所有 DLL 中重新编译到相同的版本,如果它们要相互定义。

    【讨论】:

      【解决方案3】:

      #ifdef 不会改变你在哪里使用它。如果COMPILE_FLAG 没有像#define COMPILE_FLAG 那样定义,则不会编译该块。所以,你不能使用那些变量(line_num, 文件名),如果你使用它们,你会得到。这两行完全被忽略了。所以,你必须把你使用这些变量的代码放在#ifdeftoo 中。

      【讨论】:

      • 消费者代码没有访问line_num和file_name字段,但是定义class B的DLL中class B的布局与使用class B的DLL中的布局不同,因此class B 中的代码访问了为该类分配的内存的错误部分——因此是虚假值。
      猜你喜欢
      • 2021-10-12
      • 1970-01-01
      • 1970-01-01
      • 2017-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-30
      相关资源
      最近更新 更多