Глядя на исходный код C ++, я почти всегда вижу #define
макросы в начале файла, это имеет смысл в большинстве случаев, и я понимаю, почему это лучший метод, но недавно я столкнулся с ситуацией, когда может быть лучше сохранить определения препроцессора в теле функции.
Я пишу кватернион класс, и мой код для рассматриваемой функции выглядит следующим образом:
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
потому что у него нет времени выполнения. Это приемлемая практика? Есть ли лучший способ сделать то, что я здесь делаю, читабельным?
Вместо
#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
кватернионы могут быть умножены.
Макросы не относятся к сфере действия. Эти макросы определены для остальной части файла после их появления. Если в следующей функции у вас есть переменная a1, она испортится. Вам следует #undef
все макросы в конце функции.
Лучше как-нибудь создать некоторые служебные функции. В С ++ 11
auto a1 = [this](){ return this->get_real(); }
...
resultQ.set_real(a1()*a2() ...
Не совсем так, как вам нужно ()
но может быть достаточно хорош для тебя.
Если значения a1 и т. Д. Не меняются во время вычислений, вам следует воспользоваться предложением Альфа.
Другие ответы уже дают альтернативы макросам. Вы должны определенно следовать одной такой альтернативе, потому что это использование макросов в вашем коде является ненужным и плохой.
Но я чувствую, что ваш код все равно нуждается в переработке, после чего вы увидите, что не нужны ни макросы, ни их альтернативы.
Кватернион — это обобщение комплексного числа, и, как таковое, по существу, оно имеет количество элементов данных (четыре, а не два), количество конструкторов и количество операторов.
Вы могли бы взглянуть на дизайн std::complex
чтобы получить идеи. Конструкция кватерниона не должна сильно отличаться. В частности, зачем вам нужны сеттеры / геттеры для доступа к данным из функции-члена? Эти методы именно так что делает выражения длинными! (наряду с ненужным использованием 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
};
}
Нет необходимости в макросах, вспомогательных функциях или промежуточных переменных.