Victor L
Victor L

Reputation: 10230

Simplest way to initialize multiple related const properties in a constructor?

When multiple const properties of a C++ class depend on some intermediate calculation, what is the simplest way to initialize them?

For example, how do I correct the constructor for the class below?

class MyClass {
public:
    const int a;
    const int b;

    MyClass() {
        int relatedVariable = rand() % 250;
        a = relatedVariable % 100;
        b = abs(relatedVariable - 150);
    }
};

Upvotes: 14

Views: 7268

Answers (8)

ulidtko
ulidtko

Reputation: 15560

This will kinda work for those of us who happen to prefer being less advanced in their coding:

class MyClass {
public:

    int iamStupid;      /* K.I.S.S. */

    const int a;
    const int b;

    MyClass()
      : iamStupid(rand() % 250)
      , a(iamStupid % 150)
      , b(abs(iamStupid - 150))
    {}
};

The additional member presents an unnecessary overhead — which may or may not be significant for the task at hand. OTOH, the code is simple.

Remember to declare iamStupid before a and b! (see comments)

Upvotes: 3

ulidtko
ulidtko

Reputation: 15560

In case if you are stuck with an ancient compiler which doesn't support delegating constructors, here's the same approach adapted for the older language version:

class MyClassBase {
public:
    const int a;
    const int b;
    MyClassBase(int a, int b) : a(a), b(b) {}
};

class MyClass : public MyClassBase {
    static MyClassBase Maker() {
        int x = rand() % 250;
        return MyClassBase(x % 100, abs(x - 150));
    }
public:
    using MyClassBase::a;
    using MyClassBase::b;

    MyClass() : MyClassBase(Maker()) { }
};

Upvotes: 2

Daniel Frey
Daniel Frey

Reputation: 56863

With C++11, you can simply use a delegating constructor:

class MyClass
{
public:
    const int a;
    const int b;

private:
    MyClass( int relatedVariable )
      : a( relatedVariable % 100 ),
        b( abs( relatedVariable - 150 ) ) {}

public:
    MyClass() : MyClass( rand() % 250 ) {}
};

Upvotes: 12

Wug
Wug

Reputation: 13196

You could make a and b private, and provide getters to access their values from outside the class.

class MyClass
{
private:
    int a, b; // private
public:
    int getA() { return a; }
    int getB() { return b; }

    MyClass()
    {
        int relatedVariable = rand() % 250;
        a = relatedVariable % 100;
        b = abs(relatedVariable - 150);
    }
};

Or, you could just use subobject initializers and cache the random number somehow. Turning the optimization on might even remove the temporary variable in the generated program text.

class MyClass
{
private:
    int temp; // this is a workaround
public:
    const int a;
    const int b;

    MyClass() : temp(rand() % 250), a(temp % 100), b(abs(temp - 150)) {}
};

Remember that subobject construction happens in the order that members are declared in the class, and that the order of subobjects in the initialization list is ignored.

Or, you can be lazy, and only store the initial random number, and generate a, b on demand.

Upvotes: -1

jbruni
jbruni

Reputation: 1247

Const is a contract between a class's user and implementor. It indicates that the class user should not modify the member variables, thus providing an immutable object design. It is fine for a constructor to otherwise initialize that state. That said, it might be better to hide these behind a private access qualifier and to provide accessors that allow read-only. The correct way to temporarily remove const-ness is using the const_cast<>.

class MyClass {
public:
   const int a;
   const int b;

MyClass() : a(0), b(0) {
    int relatedVariable = rand() % 250;
    const_cast<int&>(a) = relatedVariable % 100;
    const_cast<int&>(b) = abs(relatedVariable - 150);
}

};

Upvotes: 1

Tomer Arazy
Tomer Arazy

Reputation: 1823

You could do something like this - not pretty but should do the trick:

class MyClass {
public:
    const int a;
    const int b;
    static int relatedVariable;
    MyClass() :
        a(setRand()),
        b(relatedVariable)  {}
    static const int setRand()
    {
        relatedVariable = rand() % 250;
        return relatedVariable;
    }
};
int MyClass::relatedVariable = 0;

Upvotes: 2

Kerrek SB
Kerrek SB

Reputation: 476990

Here's a roundabout solution using delegating constructors:

class MyClass
{
    MyClass(int aa, int bb) : a(aa), b(bb) { }

    static MyClass Maker() { int x = /* ... */; return MyClass(x * 2, x * 3); }

    int const a;
    int const b;

public:
    MyClass(MyClass const &) = default;
    MyClass() : MyClass(Maker()) { }
};

Upvotes: 6

tpdi
tpdi

Reputation: 35141

Introduce an intermediate class that does the calculation:

class ConstCalc {

   public:
    ConstCalc(int related) : rv(related){}

    int a() const { return rv % 100; } 
    int b() const { return abs( rv - 150 ) ; } 

   private:
    const int rv;
};

class MyClass {
public:
    const int a;
    const int b;

    MyClass( const ConstCalc c ) : a( c.a() ), b( c.b() ) {
    }
};

Upvotes: 1

Related Questions