Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Right way to conditionally initialize a C++ member variable?

I'm sure this is a really simple question. The following code shows what I'm trying to do:

class MemberClass {
public:
    MemberClass(int abc){ }
};

class MyClass {
public:
    MemberClass m_class;
    MyClass(int xyz) {
        if(xyz == 42)
            m_class = MemberClass(12);
        else
            m_class = MemberClass(32);
    }
};

This doesn't compile, because m_class is being created with an empty constructor (which doesn't exist). What is the right way of doing this? My guess is using pointers and instantiating m_class using new, but I'm hoping there is an easier way.

Edit: I should have said earlier, but my actual problem has an additional complication: I need to call a method before initializing m_class, in order to set up the environment. So:

class MyClass {
public:
    MemberClass m_class;
    MyClass(int xyz) {
        do_something(); // this must happen before m_class is created
        if(xyz == 42)
            m_class = MemberClass(12);
        else
            m_class = MemberClass(32);
    }
};

Is it possible to achieve this with fancy initialization list tricks?

like image 603
Pedro d'Aquino Avatar asked Jun 18 '09 18:06

Pedro d'Aquino


2 Answers

Use the conditional operator. If the expression is larger, use a function

class MyClass {
public:
    MemberClass m_class;
    MyClass(int xyz) : m_class(xyz == 42 ? 12 : 32) {

    }
};

class MyClass {
    static int classInit(int n) { ... }
public:
    MemberClass m_class;
    MyClass(int xyz) : m_class(classInit(xyz)) {

    }
};

To call a function before initializing m_class, you can place a struct before that member and leverage RAII

class MyClass {
    static int classInit(int n) { ... }
    struct EnvironmentInitializer {
        EnvironmentInitializer() {
            do_something();
        }
    } env_initializer;
public:
    MemberClass m_class;
    MyClass(int xyz) : m_class(classInit(xyz)) {

    }
};

This will call do_something() before initializing m_class. Note that you are not allowed to call non-static member functions of MyClass before the constructor initializer list has completed. The function would have to be a member of its base class and the base class' ctor must already be completed for that to work.

Also note that the function, of course, is always called, for each separate object created - not only for the first object created. If you want to do that, you could create a static variable within the initializer's constructor:

class MyClass {
    static int classInit(int n) { ... }
    struct EnvironmentInitializer {
        EnvironmentInitializer() {
            static int only_once = (do_something(), 0);
        }
    } env_initializer;
public:
    MemberClass m_class;
    MyClass(int xyz) : m_class(classInit(xyz)) {

    }
};

It's using the comma operator. Note that you can catch any exception thrown by do_something by using a function-try block

class MyClass {
    static int classInit(int n) { ... }
    struct EnvironmentInitializer {
        EnvironmentInitializer() {
            static int only_once = (do_something(), 0);
        }
    } env_initializer;
public:
    MemberClass m_class;
    MyClass(int xyz) try : m_class(classInit(xyz)) {

    } catch(...) { /* handle exception */ }
};

The do_something function will be called again next time, if it threw that exception that caused the MyClass object fail to be created. Hope this helps :)

like image 190
Johannes Schaub - litb Avatar answered Oct 18 '22 00:10

Johannes Schaub - litb


Use the initializer list syntax:

class MyClass {
public:
    MemberClass m_class;
    MyClass(int xyz) : m_class(xyz == 42 ? MemberClass(12) : MemberClass(32)
                               /* see the comments, cleaner as xyz == 42 ? 12 : 32*/)
    { }
};

Probably cleaner with a factory:

MemberClass create_member(int x){
   if(xyz == 42)
     return MemberClass(12);
    // ...
}

//...
 MyClass(int xyz) : m_class(create_member(xyz))
like image 26
Todd Gardner Avatar answered Oct 17 '22 23:10

Todd Gardner