C ++: constructor, constructor de copia, constructor de movimiento, operador de asignación de copia, escenarios de aplicación de operador de asignación de movimiento

  Escenarios de aplicación de constructor, constructor de copia, constructor de movimiento, operador de asignación de copia, operador de asignación de movimiento

#include <iostream>
using namespace std;

class ConstructTest{
public:
    ConstructTest(){
        cout<<"default Constructor\n";
        dim_=0;
        base_= nullptr;
    };   
    ~ConstructTest(){
        cout<<"Destructor:base "<<base_<<endl;
        if (base_ != nullptr){
            delete base_;
        }
    }
    ConstructTest(int dim){
        cout<<"Constructor with param"<<endl;
        dim_=dim;
        base_ = new int [dim];
        for (int i = 0; i < dim_; ++i) {
            *(base_ + i) = 0;
        }
    }
    ConstructTest (const ConstructTest & a){
        cout<<"copy Constructor"<<endl;
        dim_= a.dim_;
        base_ = new int [dim_];
        for (int i = 0; i < dim_; ++i) {
            *(base_ + i) = *(a.base_+i);
        }
    }
    ConstructTest& operator =(const ConstructTest & a){
        cout<<"copy assign "<<endl;
        dim_= a.dim_;
        base_ = new int [dim_];
        for (int i = 0; i < dim_; ++i) {
            *(base_ + i) = *(a.base_+i);
        }
        return *this;
    }
    ConstructTest& operator =( ConstructTest && a)noexcept{
        cout<<"moving copy assign "<<endl;
        //避免自己移动自己
        if ( this == &a )
            return *this;
        delete base_;
        dim_ = a.dim_;
        base_ = a.base_;
        a.base_ = nullptr;
        return *this;
    }
    ConstructTest (ConstructTest && a) noexcept{
        cout<<"moving copy Constructor"<<endl;
        dim_ = a.dim_;
        base_ = a.base_;
        a.base_ = nullptr;
    }

public:
    int  dim_;
    int * base_;
};
ConstructTest square(ConstructTest para){
    ConstructTest ret(para.dim_);
    ret.base_ = new int [para.dim_];
    for (int i = 0; i < para.dim_; ++i) {
        *(ret.base_+i) = *(para.base_+i) * (*(para.base_+i));
    }
    return  ret;
}

int main(){
    ConstructTest c1(3);
    ConstructTest c2(c1);
    ConstructTest c4 ;
    c4=c2;
    cout<<"------------------------\n";
    ConstructTest c5 ;
    c5=square(c4);
    ConstructTest c6 = std::move(c5);
    cout<<"------------------------\n";
    ConstructTest c7;
    c7=ConstructTest();
    ConstructTest c8 = square(c7);
    ConstructTest c9 = ConstructTest();
    cout<<"<<<<<<finish >>>>>>>>\n";
}

La diferencia entre C ++ y JAVA es que C ++ distingue entre tipos de valor y tipos de referencia, no todos los tipos de referencia como JAVA. Para crear un objeto, JAVA usa <nombre de clase> nombre de objeto = nuevo ...... y C ++ usa <nombre de clase> nombre de objeto. Para objetos ordinarios pasados ​​por valor a parámetros formales, hay un proceso de asignación implícito. La copia constructor llamado en este momento. Los siguientes constructores usan escenarios:

Constructor: al crear un objeto, se llama cuando se inicializa el objeto.

Copiar (copiar) constructor: utiliza el mismo objeto de clase para inicializar un nuevo objeto.

Operador de asignación de copia: asignación entre dos objetos antiguos.

El llamado "movimiento" consiste en malversar los recursos de la memoria de una persona para uso personal.

Mover constructor: al crear un objeto, se llama cuando se inicializa con un objeto temporal. También se llama cuando el valor de retorno se pasa a una copia del valor de retorno.

Operador de asignación de movimiento: se llama cuando se utiliza un objeto temporal para asignar un valor a un objeto antiguo.

 

Yo uso CLION, agrego el siguiente código en CMakeList.txt para cancelar la optimización del compilador

add_compile_options(-fno-elide-constructors)

En el caso de que no haya optimización del compilador, el resultado de salida:

Constructor with param
copy Constructor
default Constructor
copy assign
------------------------
default Constructor
copy Constructor
Constructor with param
moving copy Constructor
Destructor:base 0
moving copy assign
Destructor:base 0
Destructor:base 0x3e1da8
------------------------
moving copy Constructor
------------------------
default Constructor
default Constructor
moving copy assign
Destructor:base 0
copy Constructor
Constructor with param
moving copy Constructor
Destructor:base 0
moving copy Constructor
Destructor:base 0
Destructor:base 0x3e1da8
default Constructor
moving copy Constructor
Destructor:base 0
<<<<<<finish >>>>>>>>
Destructor:base 0
Destructor:base 0x3e1918
Destructor:base 0
Destructor:base 0x3e1dd8
Destructor:base 0
Destructor:base 0x3e1d90
Destructor:base 0x3e1d78
Destructor:base 0x3e1d60

Process finished with exit code 0

En la primera a la cuarta línea de la función principal, se muestran el constructor, la construcción de la copia y la asignación de la copia.

Desde la línea 5:

 cout<<"------------------------\n";
    ConstructTest c5 ;
    c5=square(c4);
    ConstructTest c6 = std::move(c5);
    cout<<"------------------------\n";

Primero crea el objeto c5 con el constructor predeterminado,

En c5 = cuadrado (c4), primero asigne el parámetro real c4 al parámetro cuadrado param. En este momento, se utiliza el constructor de copia.

En el cuerpo de la función cuadrada, se crea un objeto local ret, y en este momento se llama al constructor,

Luego cree una copia del valor de retorno ret, llame al constructor de movimiento en este momento, y luego el cuerpo de la función finaliza, el parámetro formal param se destruye,

Luego asigne una copia del ret original a c5, y el proceso consiste en asignar un valor al objeto existente c5, por lo que se llama a la asignación de movimiento.

Luego, destruya la copia de ret y ret.

Use std :: move () para mover manualmente el contenido de c5 a una referencia temporal, inicialice la referencia temporal a c6 y llame al constructor de movimiento.

    cout<<"------------------------\n";
    ConstructTest c7;
    c7=ConstructTest();
    ConstructTest c8 = square(c7);
    ConstructTest c9 = ConstructTest();
    cout<<"<<<<<<finish >>>>>>>>\n";

El constructor predeterminado primero crea un objeto c7.

Usa el constructor predeterminado para crear un objeto temporal y "mueve" el objeto temporal a c7; destruye el objeto temporal después de mover

En c8 = cuadrado (c7), primero asigne el parámetro real c7 al parámetro cuadrado param. En este momento, se utiliza el constructor de copia.

En el cuerpo de la función cuadrada, se crea un objeto local ret, y en este momento se llama al constructor,

Luego cree una copia del valor de retorno ret, llame al constructor de movimiento en este momento, y luego el cuerpo de la función finaliza, el parámetro formal param se destruye,

Luego asigne la copia original de ret a c8 e inicialice el objeto c8 del objeto que aún no se ha creado, por lo que se llama a la construcción de movimiento .

Luego, destruya la copia de ret y ret.

Cree un objeto temporal, inicialícelo con c9 y finalmente destruya el objeto temporal;

Al final del programa, todas las variables se destruyen.

Ideas de programación

    En el caso de variables miembro de clase con punteros, se enfrentará al problema de cómo liberar recursos. Debe ser la estructura de copia predeterminada, la asignación de copia, la estructura de movimiento y la asignación de movimiento son solo copias superficiales. Es decir, el puntero de copia apunta a la dirección de memoria. Después de la copia superficial, los punteros de miembro en los dos objetos apuntarán al mismo espacio de memoria Si elimina el puntero de miembro en el destructor, eventualmente enfrentará el mismo espacio de memoria dos veces. En C ++, si accede a memoria que no forma parte de este programa, se producirá un "error de segmento", es decir, el puntero está fuera de límites. 

    Al programar, puede usar boost :: shared_ptr para administrar la memoria asignada dinámicamente, y puede ignorar el problema de liberación de recursos en este momento. Porque el puntero inteligente puede limpiar automáticamente el espacio de la memoria cuando el recuento de referencia de la memoria dinámica se restablece a cero.

   Si desea utilizar punteros ordinarios para administrar la memoria dinámica, debe considerar la cuestión de los recursos, la copia superficial y la copia profunda. Si desea utilizar la copia profunda, debe escribir el constructor, copiar constructor, copiar operador de asignación, mover construcción, mover operador de asignación;

  Inicialice todas las variables miembro, incluidos los punteros en el constructor; reasigne el espacio de memoria para los punteros en la construcción de copias y la asignación de copias, y asigne el valor de memoria correspondiente; en la construcción de movimientos y la asignación de movimientos, copie el contenido devuelto después de copiar el contenido del objeto temporal Los punteros se vuelven a vaciar para lograr el propósito de no copiar la memoria y no liberar la memoria dos veces, que es el llamado "mover".

Supongo que te gusta

Origin blog.csdn.net/superSmart_Dong/article/details/108178633
Recomendado
Clasificación