¿Cuáles son todas las funciones miembro creadas por el compilador para una clase? ¿Eso sucede todo el tiempo?

¿Cuáles son todas las funciones miembro creadas por el compilador para una clase? ¿Eso sucede todo el tiempo? como destructor. Mi preocupación es si se crea para todas las clases y por qué se necesita el constructor predeterminado.

Respuestas:5 Respuestas 5
Tiempo:hace 11 años, 11 meses
Última modificación:hace 2 años, 11 meses

Solución

C++98/03

Si son necesarios,

  1. el compilador generará un constructor predeterminado para usted a menos que declare cualquier constructor propio.
  2. el compilador generará un constructor de copia para usted a menos que declare el suyo propio.
  3. el compilador generará un operador de asignación de copia para usted, a menos que declare el suyo propio.
  4. el compilador generará un destructor para usted a menos que declare el suyo propio.

Como dijo Péter en un comentario útil, todos esos solo son generados por el compilador cuando son necesarios. (La diferencia es que, cuando el compilador no puede crearlos, está bien siempre y cuando no se usen).


C++11

C++11 agrega las siguientes reglas, que también son ciertas para C++14 (créditos a towi, consulte este comentario):

  • El compilador genera el constructor move si
    • no hay ningún constructor de copia declarado por el usuario, y
    • no hay ningún operador de asignación de copia declarado por el usuario, y
    • no hay ningún operador de asignación de movimiento declarado por el usuario y
    • no hay ningún destructor declarado por el usuario,
    • no está marcado d, delete
    • y todos los miembros y bases son móviles.
  • Del mismo modo, para el operador de asignación de movimiento, se genera si
    • no hay ningún constructor de copia declarado por el usuario, y
    • no hay ningún operador de asignación de copia declarado por el usuario, y
    • no hay ningún constructor de movimiento declarado por el usuario y
    • no hay ningún destructor declarado por el usuario,
    • no está marcado d, delete
    • y todos los miembros y bases son móviles.

Tenga en cuenta que estas reglas son un poco más elaboradas que las reglas de C++03 y tienen más sentido en la práctica.

Para una comprensión más fácil de lo que es qué en lo anterior:

class Thing {
public:
    Thing();                        // default constructor
    Thing(const Thing&);            // copy c'tor
    Thing& operator=(const Thing&); // copy-assign
    ~Thing();                       // d'tor
    // C++11:
    Thing(Thing&&);                 // move c'tor
    Thing& operator=(Thing&&);      // move-assign
};

Lectura adicional: si usted es un principiante en C ++, considere un diseño que no requiera que implemente ninguno de los cinco a.k.a The Rule Of Zero originalmente de un artículo escrito por Martinho Fernandes.

Otras respuestas

Borrador estándar C++17 N4659

https://github.com/cplusplus/draft/blob/master/papers/n4659.pdf 6.1 «Declaraciones y definiciones» tiene una nota que probablemente resume todas ellas:

3 [ Nota: En algunas circunstancias, las implementaciones de C ++ definen implícitamente las funciones miembros del constructor predeterminado (15.1), el constructor de copia (15.8), el constructor de movimiento (15.8), el operador de asignación de copia (15.8), el operador de asignación de movimiento (15.8) o el destructor (15.4). — nota final ] [ Ejemplo: Dado

#include <string>

struct C {
  std::string s;         // std::string is the standard library class (Clause 24)
};

int main() {
  C a;
  C b = a;
  b = a;
}

la implementación definirá implícitamente funciones para que la definición de C sea equivalente a

struct C {
  std::string s;
  C() : s() { }
  C(const C& x): s(x.s) { }
  C(C&& x): s(static_cast<std::string&&>(x.s)) { }
  // : s(std::move(x.s)) { }
  C& operator=(const C& x) { s = x.s; return *this; }
  C& operator=(C&& x) { s = static_cast<std::string&&>(x.s); return *this; }
  // { s = std::move(x.s); return *this; }
  ~ C() { }
};

— end example ]

The conditions under which those are declared are explained at: Conditions for automatic generation of default/copy/move ctor and copy/move assignment operator?

A cool way to ensure that something has a default is to try and make it use as explained at: What does «default» mean after a class’ function declaration?= default

The example below does that, and also exercises all the implicitly defined functions.

#include <cassert>
#include <string>

struct Default {
    int i;
    Default()                          = default;
    Default(const Default&)            = default;
    Default& operator=(Default&)       = default;
    Default& operator=(const Default&) = default;
    Default(Default&&)                 = default;
    Default& operator=(Default&&)      = default;
    ~Default()                         = default;
};

struct Instrument {
    int i;
    static std::string last_call;
    Instrument()                             { last_call = "ctor"; }
    Instrument(const Instrument&)            { last_call = "copy ctor"; }
    Instrument& operator=(Instrument&)       { last_call = "copy assign"; return *this; }
    Instrument& operator=(const Instrument&) { last_call = "copy assign const"; return *this; }
    Instrument(Instrument&&)                 { last_call = "move ctor";  }
    Instrument& operator=(Instrument&&)      { last_call = "move assign"; return *this; }
    ~Instrument()                            { last_call = "dtor"; }
};
std::string Instrument::last_call;

int main() {
    // See what the default constructors are doing.
    {
        // Default constructor.
        Default ctor;
        // i is uninitialized.
        // std::cout << ctor.i << std::endl;
        ctor.i = 1;

        // Copy constructor.
        Default copy_ctor(ctor);
        assert(copy_ctor.i = 1);

        // Copy assignment.
        Default copy_assign;
        copy_assign = ctor;
        assert(copy_assign.i = 1);

        // Copy assignment const.
        const Default const_ctor(ctor);
        Default copy_assign_const;
        copy_assign_const = const_ctor;
        assert(copy_assign_const.i == 1);

        // Move constructor.
        Default move_ctor(std::move(ctor));
        assert(move_ctor.i == 1);

        // Move assignment.
        Default move_assign;
        move_assign = std::move(ctor);
        assert(move_assign.i == 1);
    }

    // Check that the constructors are called by these calls.
    {
        // Default constructor.
        Instrument ctor;
        assert(Instrument::last_call == "ctor");

        // Copy constructor.
        Instrument copy_ctor(ctor);
        assert(Instrument::last_call == "copy ctor");

        // Copy assignment.
        copy_ctor = ctor;
        assert(Instrument::last_call == "copy assign");

        // Copy assignment const.
        const Instrument const_ctor(ctor);
        Instrument copy_assign_const;
        copy_assign_const = const_ctor;
        assert(Instrument::last_call == "copy assign const");

        // Move constructor.
        Instrument move_ctor(std::move(ctor));
        assert(Instrument::last_call == "move ctor");

        // Move assignment.
        Instrument move_assign;
        move_assign = std::move(ctor);
        assert(Instrument::last_call == "move assign");

        // Destructor.
        {
            Instrument dtor;
        }
        assert(Instrument::last_call == "dtor");
    }
}

GitHub aguas arriba.

Probado con GCC 7.3.0:

g++ -std=c++11 implicitly_defined.cpp

Deja un comentario