C ++ вызывает одиночную вспомогательную функцию с * this атрибутами - PullRequest
0 голосов
/ 20 октября 2018

Edit: я новичок в C ++, и я хотел бы больше узнать о том, как оптимизировать мой код.

Я создал объект Fraction в C ++, а также перегруженные операции +, -и т.д. Когда я пришел к унарным операторам, я понял, что не знаю, как уменьшить дробь наиболее эффективным способом.Итак, у меня есть функция gcd, которая находит наибольший делитель:

int gcd (int n, int m) {

int newN = n < 0 ? -n : n;
int newM = m < 0 ? -m : m;
if (newM <= newN && newN % newM == 0) { return newM; }
else if (newN < newM) { return gcd(newM, newN); }
else { return gcd(newM, newN%newM); }
}

, а затем у меня есть перегруженный оператор, например, увеличение:

Fraction& Fraction::operator++() {
num = num + denom;

//reduce fraction
int divisor = gcd(denom,num);
num = num/divisor;
denom = denom/divisor;
if (num < 0 && denom < 0) {num *= (-1);}
if (denom < 0) {denom *= (-1);}

return *this;
}

Для эффективности я бы хотелпоместите часть кода reduce fraction в отдельную вспомогательную функцию single , чтобы конечная функция выглядела следующим образом:

Fraction& Fraction::operator++() {
num = num + denom;

//reduce fraction
reduce(num, denom);

return *this;
}

Таким образом, мне не нужно копировать и вставлятьчто бы ни было в //reduce fraction каждый раз, когда я перегружаю унарный оператор, например.Однако я не уверен, как должна выглядеть функция Reduction (Fraction num, Fraction & denom).Самое большее, я могу реализовать это следующим образом:

void reduce(int& num, int& denom) {
int divisor = gcd(denom,num);
num = num/divisor;
denom = denom/divisor;
if (num < 0 && denom < 0) {num *= (-1);}
if (denom < 0) {denom *= (-1);}
}

Я уверен, что приведенный выше код может столкнуться с проблемами во время компиляции, поэтому мне было интересно, можно ли мне предложить какие-либо указатели для эффективного создания дробной частифункция.Возможно, это немного придирчиво, поскольку мой оригинальный код работает нормально, но, поскольку я новичок в C ++, я хотел бы узнать больше о том, как я могу сделать свой код более эффективным.Большое спасибо!Дайте мне знать, если потребуется дополнительная информация.

Редактировать: вышеуказанный код не работает.Компилируется правильно, но не уменьшает дробь должным образом.Таким образом, 1/2 + 1/4 приводит к 6/8, а не к 3/4.

1 Ответ

0 голосов
/ 20 октября 2018

Что ж, на высоком уровне ваша функция gcd слишком сложна, а последняя часть сокращения немного ошибочна.Если только деноминация отрицательна, вы обращаете ее.Хорошо показывает, почему всегда полезно помещать код в надлежащие функции, потому что они также могут тестироваться отдельно.Поэтому я бы посоветовал написать несколько модульных тестов для ваших функций lower и gcdНачните с простого решения, такого как

static int gcd(int a, int b)
{
    return b == 0 ? a : gcd(b, a % b);
}

и адаптируйте, если необходимо, к отрицательным числам с учетом % семантики .Подумав об этом, функция уже должна быть в порядке, и вам нужно просто вызвать std :: abs (gcd (n, d)) в redu.

В общем, вы должны спросить себя, действительно ли вы хотите заплатитьстоимость перенормировки при каждой отдельной операции или если вы позволяете пользователю решать, когда вызывать, уменьшить.

Для оптимизации более низкого уровня приведены некоторые подсказки:

  • Всегда тестируйте / измеряйте, например,глядя на то, что на самом деле производит компилятор с godbolt.org .
  • Рекурсия в gcd в данном случае не является проблемой с точки зрения производительности, так как она хвостовая рекурсия и компилятор превратит его в цикл для вас.
  • Выходные параметры в lower плохи для оптимизации, потому что компилятор должен доказать, что он не указывает на один и тот же объект.Возврат std :: pair и использование структурированных привязок C ++ 11 std :: tie или C ++ 17 в месте вызова, если это возможно, более элегантно.
...