C++ implicit copy constructor dla klasy, która zawiera inne obiekty

Wiem, że kompilator czasami dostarcza domyślnego konstruktora kopii, jeśli sam nie zaimplementujesz. Nie wiem, co dokładnie robi ten konstruktor. Jeśli mam klasę, która zawiera inne obiekty, z których żaden nie ma zadeklarowanego konstruktora kopiującego, jakie będzie to zachowanie? Na przykład klasa taka jak ta:

class Foo {
  Bar bar;
};

class Bar {
  int i;
  Baz baz;
};

class Baz {
  int j;
};

Teraz jeśli zrobię to:

Foo f1;
Foo f2(f1);

Co zrobi domyślny Konstruktor kopiujący? Czy Konstruktor kopiujący generowany przez kompilator w Foo wywoła konstruktor generowany przez kompilator w Bar to copy over bar, który następnie wywoła konstruktor generowany przez kompilator w Baz?

Author: Ben Voigt, 2009-11-27

5 answers

Foo f1;
Foo f2(f1);

Yes this will do what you expect it to:
Wywoływany jest Konstruktor kopiujący F2 Foo:: Foo (Foo const&).
Ta kopia konstruuje swoją klasę bazową, a następnie każdy element (rekurencyjnie)

Jeśli zdefiniujesz klasę w ten sposób:

class X: public Y
{
    private:
        int     m_a;
        char*   m_b;
        Z       m_c;
};

Następujące metody zostaną zdefiniowane przez kompilator.

  • Konstruktor (domyślnie) (2 wersje)
  • Konstruktor (Kopia)
  • Destructor (domyślnie)
  • przypisanie operator

Konstruktor: Default:

W rzeczywistości istnieją dwa domyślne konstruktory.
Jeden jest używany do zero-initialization, podczas gdy drugi jest używany do value-initialization. Użyte zależy od tego, czy użyjesz () podczas inicjalizacji, czy nie.

// Zero-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
    ,m_a(0)             // Default construction of basic PODS zeros them
    ,m_b(0)             // 
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Zero-Initialization version'
{
}

// Value-Initialization compiler generated constructor
X::X()
    :Y()                // Calls the base constructor
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
    //,m_a()            // Default construction of basic PODS does nothing
    //,m_b()            // The values are un-initialized.
    m_c()               // Calls the default constructor of Z
                        //     If this is compiler generated use 
                        //     the `Value-Initialization version'
{
}

Uwagi: jeśli klasa bazowa lub któryś z członków nie ma poprawnego, widocznego domyślnego konstruktora, to konstruktor domyślny nie może zostać wygenerowany. To nie jest błąd, chyba że Twój kod próbuje użyć domyślnego konstruktora (wtedy tylko kompilacji błąd czasu).

Konstruktor (Kopia)

X::X(X const& copy)
    :Y(copy)            // Calls the base copy constructor
    ,m_a(copy.m_a)      // Calls each members copy constructor
    ,m_b(copy.m_b)
    ,m_c(copy.m_c)
{}

Uwagi: jeśli klasa bazowa lub któryś z członków nie ma poprawnego widocznego konstruktora kopiującego, to Konstruktor kopiujący nie może zostać wygenerowany. Nie jest to błąd, chyba że Twój kod próbuje użyć konstruktora kopiującego (wtedy tylko błąd czasu kompilacji).

Operator Przypisania

X& operator=(X const& copy)
{
    Y::operator=(copy); // Calls the base assignment operator
    m_a = copy.m_a;     // Calls each members assignment operator
    m_b = copy.m_b;
    m_c = copy.m_c;

    return *this;
}

Uwagi: jeśli klasa bazowa lub którykolwiek z członków nie ma ważnego, wykonalnego operatora przypisania, operator przypisania nie może zostać wygenerowany. Nie jest to błąd, chyba że Twój kod próbuje użyć operatora przypisania (wtedy tylko błąd czasu kompilacji).

Destructor

X::~X()
{
                        // First runs the destructor code
}
    // This is psudo code.
    // But the equiv of this code happens in every destructor
    m_c.~Z();           // Calls the destructor for each member
    // m_b              // PODs and pointers destructors do nothing
    // m_a          
    ~Y();               // Call the base class destructor
  • jeśli dowolny konstruktor (łącznie z kopią) jest zadeklarowany, wtedy konstruktor domyślny nie jest zaimplementowany przez kompilator.
  • jeśli Konstruktor kopiujący jest zadeklarowany, kompilator go nie wygeneruje.
  • jeśli operator przypisania jest zadeklarowany, kompilator go nie wygeneruje.
  • jeśli Destruktor jest deklaruje, że kompilator nie wygeneruje takiego

Patrząc na Twój kod, generowane są następujące konstruktory kopiujące:

Foo::Foo(Foo const& copy)
    :bar(copy.bar)
{}

Bar::Bar(Bar const& copy)
    :i(copy.i)
    ,baz(copy.baz)
{}

Baz::Baz(Baz const& copy)
    :j(copy.j)
{}
 66
Author: Martin York,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-11-12 17:48:10

Kompilator dostarcza konstruktora kopii, chyba że zadeklarujesz (uwaga: nie zdefiniujesz). Konstruktor kopiujący generowany przez kompilator po prostu wywołuje Konstruktor kopiujący każdego członka klasy (i każdej klasy bazowej).

To samo dotyczy operatora przypisania i destruktora, BTW. Inaczej jest jednak w przypadku konstruktora domyślnego: jest on dostarczany przez kompilator tylko wtedy, gdy nie zadeklarujesz żadnego innego konstruktora samodzielnie.

 11
Author: sbi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 18:45:08

Tak, Konstruktor kopiujący generowany przez kompilator wykonuje kopię składową w kolejności, w jakiej składowa jest zadeklarowana w klasie zawierającej. Jeśli któryś z typów elementów nie oferuje konstruktora kopiującego, niedoszły Konstruktor kopiujący klasy zawierającej nie może zostać wygenerowany. Nadal może być możliwe napisanie go ręcznie, jeśli możesz zdecydować się na odpowiednie środki inicjalizacji wartości elementu, którego nie można skopiować-być może używając jednego z jego innych konstruktorów.

 2
Author: seh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 19:32:18

C++ domyślny Konstruktor kopiujący tworzy kopię. Płytka Kopia nie utworzy nowych kopii obiektów, do których może się odwoływać oryginalny obiekt; stare i nowe obiekty będą po prostu zawierały wyraźne wskaźniki do tej samej lokalizacji pamięci.

 0
Author: Phil,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-11-27 18:58:32

Kompilator wygeneruje potrzebne konstruktory dla Ciebie.

Jednakże, gdy tylko zdefiniujesz Konstruktor kopiujący, kompilator zrezygnuje z generowania czegokolwiek dla tej klasy i Da i błąd, jeśli nie masz zdefiniowanych odpowiednich konstruktorów.

Używając Twojego przykładu:

class Baz {
    Baz(const Baz& b) {}
    int j;
};
class Bar {
    int i;
    Baz baz;
};
class Foo {
    Bar bar;
};

Próba domyślnej instancji lub copy-construct Foo spowoduje błąd, ponieważ Baz nie jest kopiowalny, a kompilator nie może wygenerować domyślnego i kopiującego konstroktora dla Foo.

 0
Author: Coincoin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-07 20:37:05