Best practice for deferred initialization of private class members

Is there a best practice for deferred initialization of a private member of a class of a Mclass C? For example:

class C {
public:
    C();

    // This works properly without m, and maybe called at any time,
    // even before startWork was called.
    someSimpleStuff();

    // Called single time, once param is known and work can be started.
    startWork(int param); 

    // Uses m. Called multiple times.
    // Guaranteed to only be called after startWork was called 
    doProcessing(); 

private:
    M m;
};

class M {
    M(int param);
};

Class objects Ccannot be built because they Mdo not have a default initializer.

If you can change the implementation M, you can add a method initto Mand make its constructor take no arguments, which would allow you to build class objects C.

If not, you can wrap the element C Min std::unique_ptrand build it when possible.

, . , , M ?

: C , , C public .

+4
5

- .

C C(int param) : m(param){}. , .

, undefined, , concurrency, .

+5
#define ENABLE_THREAD_SAFETY

class C {
public:
    C();

    // This works properly without m, and maybe called at any time,
    // even before startWork was called.
    someSimpleStuff();

    // Called single time, once param is known and work can be started.
    startWork(int param); 

    // Uses m. Called multiple times.
    // Guaranteed to only be called after startWork was called 
    doProcessing(); 

    M* mptr()
    {
#ifdef ENABLE_THREAD_SAFETY
       std::call_once(create_m_once_flag, [&] {
          m = std::make_unique<M>(mparam);
       });
#else
        if (m == nullptr)
          m = std::make_unique<M>(mparam);
#endif
       return m.get();
    }
private:
    int mparam;
    std::unique_ptr<M> m;
#ifdef ENABLE_THREAD_SAFETY
    std::once_flag create_m_once_flag;
#endif
};

class M {
    M(int param);
};

, , m mptr(). M , .

+2

unique_ptr... ? M :

if(m)
    m->foo();

, , , , . , - , m , , - public/protected - . , , .

+1

, , ?

, M class D. D . M, doProcessing(), C, D param, .

, . , , startWork() , C

. , :)

class M
{
public:
    M(int param) {}
};

class D
{
public:
    D() {}

    // This works properly without m, and maybe called at any time,
    // even before startWork was called.
    void someSimpleStuff() {}
};


class C
{
public:
    C(D& d, int param) : d(d), m(param) { startWork(param); }

    // Uses m. Called multiple times.
    // Guaranteed to only be called after startWork was called
    void doProcessing() {}

private:
    // Called single time, once param is known and work can be started.
    void startWork(int param) {}

    D& d;
    M m;
};

int main()
{
    D d;
    d.someSimpleStuff();

    C c(d, 1337);
    c.doProcessing();
    c.doProcessing();
}
+1

: " , m , C?"

Answer No , you must use a type system to ensure that the M object will not be used until initialization, which implies a separation of the C interface. At compile time, compilers know only the type of objects and the meaning of constant expressions. C cannot be a literal type. Therefore, you must use a type system: you must split the C interface so that at compile time you can verify that M is used only after initialization.

0
source

Source: https://habr.com/ru/post/1629195/


All Articles