使用#define指令作为代码的“部分”,而不是文件顶部

时间:2014-03-29 22:57:57

标签: c++ c-preprocessor coding-style

在查看c ++源代码时,我几乎总是在文件的头部看到#define宏,这在大多数情况下都是有意义的,我可以看出为什么这是最佳实践,但我最近遇到了可能更好地将预处理程序定义保留在函数体中的情况。

我正在写一个quaternion类,我的相关函数代码如下所示:

Quaternion Quaternion::operator*(const Quaternion& q){
    Quaternion resultQ;

    // These are just macros to make the code easier to read,
    // 1 denotes the quaternion on the LHS of *,
    // 2 denotes the quaternion of the RHS 0f *, e.a. Q1 * Q2.
    // the letter denotes the value of the real number constant part 
    // for each seperate part of the quaterion, e.a. (a+bi+cj+dk)

    #define a1 this->get_real()
    #define a2 q.get_real()
    #define b1 this->get_i()
    #define b2 q.get_i()
    #define c1 this->get_j()
    #define c2 q.get_j()
    #define d1 this->get_k()
    #define d2 q.get_k()

    // This arithemtic is based off the Hamilton product
    // (http://en.wikipedia.org/wiki/Quaternion#Hamilton_product)
    resultQ.set_real(a1*a2 - b1*b2 - c1*c2 - d1*d2);
    resultQ.set_i(a1*b2 + b1*a2 + c1*d2 - d1*c2);
    resultQ.set_j(a1*c2 - b1*d2 + c1*a2 + d1*b2);
    resultQ.set_k(a1*d2 + b1*c2 - c1*b2 + d1*a2);
    return resultQ;
}

我决定添加#define,因为如果我手动替换所有宏,每行都会太长,并且在读取时会被切断或转移到下一行。我可以用变量做同样的事情,但我认为这将是一个不必要的开销,所以我使用#define,因为它没有运行时开销。这是可接受的做法吗?有没有更好的方法让我在这里做的事情可读?

3 个答案:

答案 0 :(得分:3)

而不是

#define a1 this->get_real()

auto const a1 = get_real();

只需为更改数量的每个值使用不同的名称

是的,有些情况下本地#define有意义。不,这不是这种情况。特别是,由于您已经忘记了#undef宏,因此如果它位于标题中(如图所示),它们几乎肯定会在其他代码中导致无意中的文本替换。


顺便说一下,而不是

Quaternion Quaternion::operator*(const Quaternion& q){

我会写

Quaternion Quaternion::operator*(const Quaternion& q) const {

这样也可以将const四元数相乘。

答案 1 :(得分:1)

宏不尊重范围。这些宏在出现后为文件的其余部分定义。如果你在下一个函数中有一个变量a1,它就会搞乱。你应该#undef函数末尾的所有宏。

最好以某种方式创建一些实用功能。在C ++ 11中

auto a1 = [this](){ return this->get_real(); }
...

resultQ.set_real(a1()*a2() ...

与您需要的()不完全相同,但对您来说可能已经足够了。

如果a1等的值在计算过程中没有变化,你应该使用Alf的建议。

答案 2 :(得分:1)

其他答案已经提供了宏的替代方案。你绝对应该遵循一个这样的选择,因为你的代码中使用宏是不必要的,而且不好

但我觉得你的代码无论如何都需要重新设计,之后你会发现它既不需要宏也不需要它们的替代品。

四元数是一个复数的泛化,因此,本质上它有许多数据成员(四个而不是两个),一些构造函数和一些运算符。

您可以查看std::complex的设计以获取想法。四元数的设计不需要太大不同。特别是,为什么需要setter / getters来访问成员函数的数据?这些方法完全使表达式变长的原因! (以及不必要地使用this)。

因此,如果四元数的数据成员是a, b, c, d,并且如果有一个包含这四个值的构造函数是参数,那么您的operator*应该看起来像这样:

Quaternion Quaternion::operator*(const Quaternion& q) const
{
    return Quaternion{
       a*q.a - b*q.b - c*q.c - d*q.d,
       a*q.b + b*q.a + c*q.d - d*q.c,
       a*q.c - b*q.d + c*q.a + d*q.b,
       a*q.d + b*q.c - c*q.b + d*q.a
    };
}

不需要宏,辅助函数或中间变量。