Инициализируйте несколько постоянных членов класса, используя один вызов функции C ++ - PullRequest
50 голосов
/ 04 апреля 2020

Если у меня есть две разные постоянные переменные-члены, которые нужно инициализировать на основе одного и того же вызова функции, есть ли способ сделать это, не вызывая функцию дважды?

Например, класс дроби, в котором числитель и знаменатель постоянны.

int gcd(int a, int b); // Greatest Common Divisor
class Fraction {
public:
    // Lets say we want to initialize to a reduced fraction
    Fraction(int a, int b) : numerator(a/gcd(a,b)), denominator(b/gcd(a,b))
    {

    }
private:
    const int numerator, denominator;
};

Это приводит к потере времени, так как функция GCD вызывается дважды. Вы также можете определить новый член класса gcd_a_b и сначала назначить вывод gcd в списке инициализаторов, но затем это приведет к потере памяти.

В общем, есть ли способ сделать это без потраченных впустую вызовов функций или памяти? Можете ли вы создать временные переменные в списке инициализаторов? Спасибо.

Ответы [ 3 ]

66 голосов
/ 04 апреля 2020

В общем, есть ли способ сделать это без напрасных вызовов функций или памяти?

Да. Это можно сделать с помощью делегирующего конструктора , представленного в C ++ 11.

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

int gcd(int a, int b); // Greatest Common Divisor
class Fraction {
public:
    // Call gcd ONCE, and forward the result to another constructor.
    Fraction(int a, int b) : Fraction(a,b,gcd(a,b))
    {
    }
private:
    // This constructor is private, as it is an
    // implementation detail and not part of the public interface.
    Fraction(int a, int b, int g_c_d) : numerator(a/g_c_d), denominator(b/g_c_d)
    {
    }
    const int numerator, denominator;
};
10 голосов
/ 04 апреля 2020

Элементы-члены инициализируются в порядке, в котором они объявлены в объявлении класса, поэтому вы можете делать следующее (математически)

#include <iostream>
int gcd(int a, int b){return 2;}; // Greatest Common Divisor of (4, 6) just to test
class Fraction {
public:
    // Lets say we want to initialize to a reduced fraction
    Fraction(int a, int b) : numerator{a/gcd(a,b)}, denominator(b/(a/numerator))
    {

    }
//private:
    const int numerator, denominator;//make sure that they are in this order
};
//Test
int main(){
    Fraction f{4,6};
    std::cout << f.numerator << " / " << f.denominator;
}

Нет необходимости вызывать другие конструкторы или даже создавать их.

0 голосов
/ 05 апреля 2020

@ Дрю Дорманн дал решение, подобное тому, что я имел в виду. Так как OP никогда не упоминает о невозможности изменить ctor, это можно вызвать с помощью Fraction f {a, b, gcd(a, b)}:

Fraction(int a, int b, int tmp): numerator {a/tmp}, denominator {b/tmp}
{
}

Только в этом случае нет второго вызова функции, конструктора или иным образом, так что это не тратится впустую время. И это не потерянная память, так как в любом случае нужно было бы создать временное хранилище, так что вы также можете его использовать. Это также позволяет избежать дополнительного деления.

...