Qq0
Qq0

Reputation: 946

Initialize multiple constant class members using one function call C++

If I have two different constant members variables, which both need to be initialized based on the same function call, is there a way to do this without calling the function twice?

For example, a fraction class where numerator and denominator are constant.

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;
};

This results in wasted time, as the GCD function is called twice. You could also define a new class member, gcd_a_b, and first assign the output of gcd to that in the initializer list, but then this would lead to wasted memory.

In general, is there a way to do this without wasted function calls or memory? Can you perhaps create temporary variables in an initializer list?

Upvotes: 54

Views: 3649

Answers (3)

Drew Dormann
Drew Dormann

Reputation: 63775

In general, is there a way to do this without wasted function calls or memory?

Yes. This can be done with a delegating constructor, introduced in C++11.

A delegating constructor is a very efficient way to acquire temporary values needed for construction before any member variables are initialized.

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;
};

Upvotes: 73

asmmo
asmmo

Reputation: 7100

The member vars are initialized by the order they are declared in the class declaration, hence you can do the following (mathematically)

#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;
}

No need for calling another constructors or even making them.

Upvotes: 10

a concerned citizen
a concerned citizen

Reputation: 839

@Drew Dormann gave a solution similar to what I had in mind. Since OP never mentions not being able to modify the ctor, this can be called with Fraction f {a, b, gcd(a, b)}:

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

Only this way there is no second call to a function, constructor or otherwise, so it's no wasted time. And it's no wasted memory since a temporary would have to be created anyway, so you may as well make good use of it. It also avoids an extra division.

Upvotes: -3

Related Questions