我知道基本规则,使用inline
,enum
和const
代替#define
,这不是我对此问题的追求。我想知道的是在C ++中你将使用#define
宏以及如何使用宏的可接受场景。
请不要发布问题或链接到“define vs const”问题或“预处理器与编译器”,我已经浏览过Scott Meyers的Effective C ++,我知道一个优于另一个的优点。
然而,经过几个小时的网上冲浪,我觉得#define在C ++中被视为某种弱者,但我确信必须有一个案例可以接受,甚至是可取的用它。
为了让球滚动起来,我想我可以想到的一个场景是创建一个基于它的DEBUG
宏,可以在整个代码中使用打印和诸如此类的东西进行调试。
答案 0 :(得分:11)
以下是一些使用#define是一个很好的解决方案的场景:
在保留功能签名的同时添加诊断信息:
#ifdef _DEBUG
#define Log(MSG) Log((MSG), __FILE__, __LINE__);
#endif
条件编译和包含警卫也是一个很好的例子(没有给出示例,因为你应该理解这一点:)。)
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 */
用法:
// forward declaration of API function:
LIB_EXPORT void MyFunction(int);
答案 1 :(得分:8)
调试/发布或跨平台代码的简单设置。以下是我的计划示例:
void Painter::render()
{
if (m_needsSorting)
{
_sort();
}
for (GameObject* o : m_objects)
{
o->render();
#ifdef _DEBUG
o->renderDebug();
#endif
}
}
还有一个用于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
另一件事是图书馆:
#ifdef VECTRY_INLINE
#define vinline inline
#else
#define vinline
#endif
以及一些有用的东西:
#define MakeShared(T) \
class T; \
typedef shared_ptr<T> T##Ptr
答案 2 :(得分:6)
C ++中为数不多的有用案例之一包括警卫:
// A.h
#ifndef _A_H_
#define _A_H_
class A
{ /* ... */ };
#endif /* _A_H_ */
答案 3 :(得分:6)
有时,您希望生成代码而无需重复无休止的样板,或者不必使用其他语言来执行此操作。 From time to time,模板不够用,最终您将使用Boost.Preprocessor生成代码。
宏是“必需”的一个例子是Boost.TTI(类型特征内省)。底层机制以某种方式滥用语言来创建一些强大的元函数,但需要大量的样板。例如,宏BOOST_TTI_HAS_MEMBER_FUNCTION
生成一个matefunction,用于检查类是否具有给定的成员函数。这样做需要创建一个新类,如果没有宏,就不能做空(解决问题的非宏解决方案示例here)。
有时您需要使用X-macros来生成代码。在编译时绑定东西非常有用。我不确定它们是否可以在今天完全替换,但无论如何,你可以找到一些非常有趣的应用程序示例here。
总而言之,宏可以成为生成代码的强大工具,但需要谨慎使用。
答案 4 :(得分:5)
我认为当引入C时,C不会使用consts,所以#defines
是提供常量值的唯一方法。但是后来在#define
上没有太多使用,因为consts取而代之(或更好的话我们可以说consts更容易使用)。但我会说include guards仍然是使用它们的一个领域。并且它们被使用,因为您的代码更具可读性。
标题包含警卫是您无法使用争用的区域
#ifndef GRANDFATHER_H
#define GRANDFATHER_H
struct foo {
int member;
};
#endif /* GRANDFATHER_H */
您也可以查看Why would someone use #define to define constants?
还要补充一点,#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);
}