Pytanie Inicjalizuj referencję - ostrzeżenie C4355: "this": używane w bazie inicjatora lista podstawowa


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() {
}

Obecnie, gdy próbuję zainicjować odniesienie w B, używam metody 1. Jednak metoda 1 będzie oznaczać ostrzeżenie, które jest zrozumiałe.

W związku z tym muszę wycofać się przy użyciu metody 2, używając dynamicznego przydzielania pamięci.

Czy istnieje lepszy sposób, w jaki mogę korzystać, bez potrzeby ręcznej alokacji pamięci / dellocation (OK, znam inteligentny wskaźnik)?

Preferuję metodę 1, tylko że nie czuję się dobrze z ostrzeżeniem.


18
2017-08-18 06:24


pochodzenie




Odpowiedzi:


Zauważ, że to ostrzeżenie (więc jest to niebezpieczne, a nie nielegalne).

Niepokoi go kompilator, który podaje wskaźnik dla obiektu, który nie został w pełni zainicjowany. Zatem jeśli wskaźnik jest używany w konstruktorze klasy B, masz niezdefiniowane zachowanie.

Więc jeśli używasz tego, jedyną rzeczą, którą możesz zrobić, to przypisać wskaźnik do zmiennej składowej (referencji lub wskaźnika). Ale zauważ uważaj na przypisanie do zmiennej, ponieważ możesz wywołać niejawne rzutowanie (nie jestem pewien, czy to rzeczywiście jest problem, ale RTTI nie jest dostępny, dopóki obiekt nie zostanie w pełni uformowany).

Co próbujesz osiągnąć, przechowując referencję?


14
2017-08-18 06:33





Takie postępowanie jest ważne.

Musisz jednak upewnić się (mam na myśli, że kompilator nie może tego zrobić), że this nie jest używane do wywoływania funkcji wirtualnych, dopóki obiekt nie zostanie w pełni skonstruowany.


11
2017-08-18 06:34





W zależności od tego, co robisz, pewną metodą może być faktorowanie części A że B potrzeb, niż mieć A dziedzicz po części.

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

Oczywiście zależy to od tego, co robisz. Dzięki temu nigdy nie uzyskasz niezdefiniowanego zachowania.

Ale tak naprawdę to tylko ostrzeżenie. Jeśli obiecasz, że nigdy nie użyjesz this w konstruktorze B masz się dobrze i możesz wyciszyć ostrzeżenie w ten sposób:

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

Upewnij się jednak, że wiesz, co robisz. (Być może można go przeprojektować?)


7
2017-08-18 06:42





Jednym z oczywistych sposobów uniknięcia ostrzeżenia jest spowodowanie, że B zapisze wskaźnik na A, wtedy nie trzeba go inicjalizować na liście inicjalizacyjnej B / konstruktora B, i może poczekać, aż wykonywanie konstruktora A zakończy się. ...


2
2017-08-18 06:36





Traktuj to ostrzeżenie poważnie. Twój this Obiekt nie jest jeszcze w pełni skonstruowany, a przemijanie nie jest bezpieczne (jeśli zdarzy ci się przypadkowo wywołać funkcję this powołujesz się na UB). Istnieją również inne techniki zarządzania pamięcią. Spróbuj wyszukać projekt podzielników w STL.

Możesz również użyć wskaźników RAII / smart, aby osiągnąć ten sam efekt.

Lub, czy próbujesz napisać sorter śmieci / profiler pamięci?


0
2017-08-18 06:36