开源代码编码常用技巧¶
1 do{...}while (0) 的意义和用法¶
- 参考资料
在 C++ 中,
do { ... } while (0)
是一种常见的编程技巧,通常用于宏定义中。它的主要目的是确保宏的行为类似于一个完整的语句块,从而避免一些潜在的错误和问题。以下是一些使用do { ... } while (0)
的常见场景和原因:
1.1 避免悬空 else 问题¶
在宏定义中,如果不使用 do { ... } while (0)
,可能会导致悬空 else
问题。例如:
#define FOO(x) if (x) a = b; else c = d;
如果在使用这个宏时,代码如下:
if (condition)
FOO(true);
else
e = f;
展开后会变成:
if (condition)
if (true) a = b; else c = d;
else
e = f;
这会导致 else
与内部的 if
匹配,而不是外部的 if
,从而导致逻辑错误。使用 do { ... } while (0)
可以避免这个问题:
#define FOO(x) do { if (x) a = b; else c = d; } while (0)
展开后变成:
if (condition)
do { if (true) a = b; else c = d; } while (0);
else
e = f;
这样 else
就会正确地与外部的 if
匹配。
1.2 确保宏的行为类似于语句块¶
do { ... } while (0)
确保宏的行为类似于一个完整的语句块,可以在宏内部定义局部变量、使用 break
和 continue
等控制流语句。例如:
#define SAFE_DELETE(ptr) do { \
if (ptr) { \
delete ptr; \
ptr = nullptr; \
} \
} while (0)
这样在使用宏时,可以确保 ptr
被正确删除并置为 nullptr
:
SAFE_DELETE(myPtr);
1.3 避免多语句宏的副作用¶
如果不使用 do { ... } while (0)
,宏展开后可能会导致意外的副作用。例如:
#define SWAP(a, b) { int temp = a; a = b; b = temp; }
如果在使用这个宏时,代码如下:
if (condition)
SWAP(x, y);
else
z = w;
展开后会变成:
if (condition)
{ int temp = x; x = y; y = temp; };
else
z = w;
这会导致语法错误,因为 ;
后面不能直接跟 else
。使用 do { ... } while (0)
可以避免这个问题:
#define SWAP(a, b) do { int temp = a; a = b; b = temp; } while (0)
展开后变成:
if (condition)
do { int temp = x; x = y; y = temp; } while (0);
else
z = w;
2 (void) val 的意义和用法¶
- 功能:在不使用函数的参数时,使用 void 修饰参数可以使编译器不警告
- 示例
int fun(int a)
{
(void)a;
return 0;
}
3 include 实现文件¶
- 作用:通常用于既可以将实现与声明分开(动态库场景),又可以实现和声明在同一个头文件(便于使用者直接包含头文件,而不需要链接动态库)。
以 asio 中类 endpoint 实现代码来讲解
endpoint.hpp
文件
class endpoint
{
... 省略
ASIO_DECL endpoint(int family,
unsigned short port_num) ASIO_NOEXCEPT;
... 省略
}
#if defined(ASIO_HEADER_ONLY)
# include "asio/ip/detail/impl/endpoint.ipp"
#endif // defined(ASIO_HEADER_ONLY)
endpoint.ipp
endpoint::endpoint(int family, unsigned short port_num) ASIO_NOEXCEPT
: data_()
{
... 省略
}
ASIO_HEADER_ONLY
定义如下
// Default to a header-only implementation. The user must specifically request
// separate compilation by defining either ASIO_SEPARATE_COMPILATION or
// ASIO_DYN_LINK (as a DLL/shared library implies separate compilation).
#if !defined(ASIO_HEADER_ONLY)
# if !defined(ASIO_SEPARATE_COMPILATION)
# if !defined(ASIO_DYN_LINK)
# define ASIO_HEADER_ONLY 1
# endif // !defined(ASIO_DYN_LINK)
# endif // !defined(ASIO_SEPARATE_COMPILATION)
#endif // !defined(ASIO_HEADER_ONLY)