20
class A;

class B {
public:
    B(A& a) : a(a) {}
private:
    A& a;
};

/* Method 1 */
/* warning C4355: 'this' : used in base member initializer list */
/*
class A {
public:
    A() : b(*this) {}

private:
    B b;
};
*/

/* Method 2 */
/* But I need to manually perform memory dellocation. */
class A {
public:
    A() { b = new B(*this); }
    ~A() { delete b; }

private:
    B* b;
};

int main() {
}

Currently, when I try to initialize the reference in B, I am using Method 1. However, Method 1 will flag me warning which is understandable.

Hence, I have to fall back using Method 2, by using dynamic memory allocation.

Is there any better way I can use, without the need of manual memory allocation/ dellocation (OK. I know smart pointer)?

I prefer Method 1, just that I am not comfortable with the warning.

Cheok Yan Cheng
  • 47,586
  • 132
  • 466
  • 875

5 Answers5

14

Note this is a warning (so it is dangerous not illegal).

What the compiler is worried about is that you are passing a pointer for an object that has not been fully initialized. Thus if the pointer is used in the B class constructor you are in undefined behavior.

So if you do use this the only thing you can do is assign the pointer to a member variable (reference or pointer). But note be careful of the assignment to a variable as you may invoke an implicit cast (I am not sure if that is actually a problem but the RTTI is not available until the object is fully formed).

What are you trying to achieve by storing the reference?

Martin York
  • 257,169
  • 86
  • 333
  • 562
11

Doing this is valid.

However, you must ensure (I mean by yourself, there's no way the compiler can do this) that the this is not used to call virtual functions until the object is fully constructed.

Didier Trosset
  • 36,376
  • 13
  • 83
  • 122
7

Depending on what you're doing, a method might be to factor out the parts of A that B needs, than have A inherit from the part.

struct bar_base; // interface foo wants

struct foo
{
    foo(bar_base& pX) :
    mX(pX)
    {}

    bar_base& mX;
};

struct bar_base
{
    /* whatever else */ 
protected:
    bar_base& get_base(void)
    {
        // getting `this` went here; safe because bar_base is initialized
        return *this; 
    }
};

struct bar : bar_base
{
    bar(void) :
    // bar_base is already initialized, so:
    mX(get_base())
    {}

    foo mX;
};

Obviously, this depends on what you're doing. This makes sure you never get undefined behavior.

But really, it's just warning. If you promise to never use this in B's constructor, you're fine, and can silence the warning this way:

struct bar;

struct foo
{
    foo(bar& pX) :
    mX(pX)
    {}

    bar& mX;
};

struct bar
{
    bar(void) :
    mX(self())
    {}

    foo mX;

private:
    bar& self(void)
    {
        // fools the warning
        return *this;
    }
};

Make sure you know what you're doing, though. (Perhaps it could be re-designed?)

GManNickG
  • 494,350
  • 52
  • 494
  • 543
2

Well, one obvious way to avoid the warning is to make B store a pointer-to-A, then you don't have to initialise it in B's constructor/A's initialiser list, and can wait until the body of A's constructor is executing....

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
0

Take this warning seriously. Your this object is not yet fully constructed and passing around is not safe (if you ever accidentally call a function on this you invoke UB). Also, there are other techniques for memory management. Try looking up the STL design of allocators.

You could also use RAII/smart pointers to achieve the same effect.

Or, are you trying to write a garbage collector/memory profiler of sorts?

dirkgently
  • 108,024
  • 16
  • 131
  • 187