在C++中#define的适当使用场景是什么?

gor*_*rba 18 c++ macros c-preprocessor

我知道基本的规则,使用inline,enumconst不是#define,这不是我追求这个问题.我想知道的是#define在C++中使用宏以及如何使用宏的可接受场景.

请不要发布问题或链接到"define vs const"问题或"预处理器与编译器",我已经通过了Scott Meyers的Effective C++,我知道一个优于另一个的优点.

然而,经过几个小时的网上冲浪,我觉得#define被认为是C++中的某种弱者,但我确信必须有一个案例,使用它可以接受甚至是可取的.

为了推动这个问题,我猜想我可以想到的一个场景是创建一个DEBUG基于它的宏,它可以打印所有代码以及用于调试目的的所有内容.

utn*_*tim 11

以下是使用#define是一个很好的解决方案的一些场景:

在保留功能签名的同时添加诊断信息:

#ifdef _DEBUG
#define Log(MSG)  Log((MSG), __FILE__, __LINE__);
#endif
Run Code Online (Sandbox Code Playgroud)

条件编译和包含警卫也是一个很好的例子(没有给出一个例子,因为你应该理解这个:)).

Boilerplate代码是另一个例子,但这很容易被滥用.使用宏作为样板代码的一个很好的例子是Boost.UnitTest中的BOOST_AUTO_TEST_CASE宏(更糟糕的例子是将Windows API映射到其CHAR或WCHAR宏的WinAPI宏集).

另一个好例子是提供特定于编译器的关键字和设置:

#if (defined _WIN32) && (defined LIB_SHARED)
#   ifdef LIB_EXPORTS
#       define LIB_EXPORT __declspec(dllexport)
#   else
#       define LIB_EXPORT __declspec(dllimport)
#   endif /* LIB_EXPORTS */
#else
#   define LIB_EXPORT extern
#endif /* _WIN32 && LIB_SHARED */
Run Code Online (Sandbox Code Playgroud)

用法:

// forward declaration of API function:
LIB_EXPORT void MyFunction(int);
Run Code Online (Sandbox Code Playgroud)


tei*_*vaz 8

调试/发布或跨平台代码的简单设置.这是我的程序示例:

void Painter::render()
{
    if (m_needsSorting)
    {
        _sort();
    }
    for (GameObject* o : m_objects)
    {
        o->render();
#ifdef _DEBUG
        o->renderDebug();
#endif
    }
}
Run Code Online (Sandbox Code Playgroud)

还有一个对于win/ios:

#ifdef _WIN32

#include "EGL/egl.h"
#include "GLES2/gl2.h"
#include <Windows.h>

#ifdef _ANALYZE
#include <vld.h>
#endif

#else // IOS
#import <Availability.h>
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#import <Foundation/Foundation.h>
#endif
Run Code Online (Sandbox Code Playgroud)

另一件事是图书馆:

#ifdef VECTRY_INLINE
#define vinline inline
#else
#define vinline 
#endif
Run Code Online (Sandbox Code Playgroud)

以及一些有用的东西:

#define MakeShared(T) \
    class T; \
    typedef shared_ptr<T> T##Ptr
Run Code Online (Sandbox Code Playgroud)

  • 最后一部分是一些可以被认为是宏观滥用的代码.提供额外的便利是最小的,但是对代码的理解变得更加困难(跟踪相关的错误,知道C++语法是不够的,必须追捕宏,然后进行宏扩展,甚至开始考虑代码是否正确) .如果它是一个完善的文档库的一部分,可能会好的,但即使这样,学习能够使用该库还有一件事. (9认同)
  • 那么,如果不是宏,我们还会使用什么呢?我觉得多个`inline`函数会有类似的可读性问题和更多的开销,并且在某些情况下,完整的函数都是过度的.我不知道还有什么可以取代宏. (2认同)

TNA*_*TNA 6

C++中为数不多的有用案例之一包括警卫:

// A.h
#ifndef _A_H_
#define _A_H_

class A
{ /* ... */ };

#endif /* _A_H_ */
Run Code Online (Sandbox Code Playgroud)

  • 虽然您不应该使用[保留名称](http://stackoverflow.com/questions/228783)作为警卫. (14认同)

Mor*_*enn 6

有时,您希望生成代码而无需重复无休止的样板,或者不必使用其他语言来执行此操作.有时,模板不够用,最终您将使用Boost.Preprocessor生成代码.

宏是"必需"的一个例子是Boost.TTI(类型特征内省).底层机制以某种方式滥用语言来创建一些强大的元函数,但需要大量的样板.例如,宏BOOST_TTI_HAS_MEMBER_FUNCTION生成一个matefunction,用于检查类是否具有给定的成员函数.这样做需要创建一个新类,如果没有宏,就不能做空(这里有解决问题的非宏解决方案示例).

有时候您需要使用X-macros来生成代码.在编译时绑定东西非常有用.我不确定它们是否可以在今天完全替换,但无论如何,你可以在这里找到一些非常有趣的应用程序示例.

总而言之,宏可以是生成代码的强大工具,但需要谨慎使用.


Rah*_*thi 5

我认为当C被引入时,C并没有使用consts,所以#defines提供常量值的唯一方法也是如此.但后来#define并没有太多使用,因为consts取而代之(或者更好的说法,我们可以说consts更容易使用).但我会说包括警卫仍然是他们使用的一个领域.它们被使用,因为您的代码更具可读性.

头部包含防护是一个你不能使用consts的区域

例如:

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct foo {
    int member;
};

#endif /* GRANDFATHER_H */
Run Code Online (Sandbox Code Playgroud)

您还可以检查为什么有人会使用#define来定义常量?

还有一点要补充,#defines不要考虑范围,所以没有办法创建一个类范围的命名空间,而const变量可以在类中定义.(我知道你知道区别但是想要添加它,因为它很重要.)

还要显示使用#define的一个示例:

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}
Run Code Online (Sandbox Code Playgroud)