纠正在C ++标头/实现文件中放置#undef指令

时间:2015-11-20 12:22:05

标签: c++ c-preprocessor

我正在编写一个类(在头文件myClass.h和实现文件myClass.cpp中分隔),我希望将它与标准C ++和Qt框架一起使用。由于代码中的差异非常小(我想尝试一次),我决定#define USINGQT 1以便通过

切换代码的​​小部分
#if USINGQT==1
    //Qt code
#else
    //standard code
#endif

现在我得出的结论是,在整个班级中使用QString而不是std::string时,如果"激活" USINGQT开关。但是,上面的方法会使代码非常混乱。我的解决方案(在头文件中):

#if USINGQT==1
    #include <QString>
    #define string QString
#else
    #include <string>
    #define string std::string
#endif

现在回答问题:

将文件视为

---myclass.h-------------------------
#ifndef MYCLASS_H
#define MYCLASS_H

#define USINGQT 1  //1=on, else off
#if USINGQT==1
    #include <QString>
    #define string QString
#else
    #include <string>
    #define string std::string
#endif

namespace mySpace {

class MyClass {
    string qtOrStd;
    string foo();
    //etc...
};

} //namespace
#endif //MYCLASS_H
-------------------------------------

---myclass.cpp-----------------------
#include "myclass.h"
using namespace mySpace;

//implementations
string MyClass::foo()   //string symbol occurs, as does the USINGQT
-------------------------------------
  1. #undefstring符号USINGQT的正确位置在哪里?在头文件的末尾(这将需要重新定义和#34;未定义&#34;在实现文件中)或仅在实现文件的末尾?

  2. 我也应该将string宏大写,不应该......? &GT;&GT;

  3. 如果我将宏定义放在命名空间中,我会收到大约。 800个错误消息,条目如&#34;没有mySpace成员:: std&#34;等等。如果没有进一步的信息,你能说些什么吗?否则它编译得很好。

  4. 编辑:我可能应该告诉你,我希望宏只应用于这个特定的头及其实现文件。尽管我当然会选择typedef s - 在宏观情况下,我猜,我应该将#undef放在实现文件的末尾。因为包含警卫而不会重新定义宏。

3 个答案:

答案 0 :(得分:4)

  1. 首先,您不需要将USINGQT切换为等于1,您只需#define USINGQT,然后使用#ifdef USINGQT作为if语句。
  2. 就您切换使用哪个字符串库的能力而言,我建议在预处理器if语句旁边使用typedef。这可以避免任何名称空间问题。这方面的一个例子如下所示。
  3. // --------------一些配置文件------------- =

    #define USINGQT
    
    // -------------- MyClass.h --------------------=
    // Header guard
    #ifndef MyClass
    #define MyClass 
    
    // Conditional Header types
    #ifdef USINGQT
    // QT OPTION
    typedef QString my_string;
    
    #else
    //  Not QT
    typedef std::string my_string;
    #endif
    
    class MyClass {
    public:
        my_string some_string;
    
        MyClass()
        {
            my_string = "hello world";
        }
    };
    #endif
    

答案 1 :(得分:1)

除非另一个文件试图重新定义它,否则 #undef个宏。在使用它之前,你不能#undef一个宏,因此,如果你在标题中#define一个宏并希望在包含它的文件中使用它标题,然后你不能在该标题中#undef

  

1)#undef字符串和USINGQT符号的正确位置在哪里?在头文件的末尾...

如果您在该标题中使用它...但您显然确实使用了包含标题的文件,因此

  

或仅在实施文件的末尾?

在实现文件末尾取消定义宏是没有意义的,因为在宏应用的文件结尾之后将不再有代码。让它保持定义。

  

2)我也应该把字符串宏大写,我不应该......? &GT;&GT;

您不必将宏大写,但它是一种惯例。也就是说,定义一个与标准类同名的宏只是在寻找麻烦。您应该在此处使用typedef而不是宏,以便在名称冲突时获取有意义的错误消息。并使用其他名称string_t或在命名空间中定义typedef

  

3)如果我将宏定义放在命名空间内,我会收到大约。 800错误消息

错误不是来自定义命名空间内的宏。错误来自使用宏 - 如果它们是命名空间的一部分。例如,如果你说:

namespace mySpace {
#define string std::string
}
mySpace::string s;

然后string将替换为std::string,类型名称将变为mySpace::std::string。由于您尚未在std中定义mySpace命名空间,因此这是错误的。您需要了解的是命名空间对预处理器宏没有任何影响。这使得更难避免名称冲突,这是您通常想要避免使用预处理器宏的一个原因。

如果USINGQT宏适用于代码的所有,使得所有文件必须相同,您可能根本不想在标题中定义它,而是通过它作为编译器的参数。这样,您可以轻松地使用不同的值进行编译而无需更改文件。

关于您的修改:

即使您希望在另一个文件中以不同方式定义宏,然后在实现结束时取消定义它也没有效果,因为包含标头的文件不会包含实现文件。您应该避免需要多个不同定义(或缺少定义)宏的情况,但是如果您处于这种情况,那么是的,您唯一的解决方案是单独定义它每个需要它的文件然后在任何需要它的头的末尾取消定义。但是你不是在这种情况下,因为你可以使用类型别名。

答案 2 :(得分:1)

我认为#undef宏没有任何理由。当然,您希望所有您的代码使用该宏的一个状态进行编译?那么你就不需要#undef了。

但是,我强烈建议您使用typedef作为string定义。无论如何,这将更清楚,你不会考虑大写它,你甚至可以把它放入你的命名空间。如果需要访问全局命名空间,请使用::

#define USINGQT
#ifdef USINGQT
    #include <QString>
#else
    #include <string>
#endif

namespace mySpace {
    #ifdef USINGQT
    typedef ::QString string;
    #else
    typedef ::std::string string;
    #endif
}

另请注意(如上所示)如果您只需要宏的布尔值,则不需要将其设为10 ,只需使用#ifdef / #ifndef

在此之后,在.cpp中,只需使用mySpace::string,就不用担心宏。