Menu
Coddy logo textTech

Costruttore di spostamento

Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 21 di 104.

Un costruttore di spostamento trasferisce le risorse da un oggetto temporaneo invece di copiarle. Mentre un costruttore di copia duplica i dati, un costruttore di spostamento "ruba" le risorse, lasciando l'oggetto sorgente in uno stato valido ma vuoto. In questo modo si evitano costose copie profonde quando l'oggetto sorgente sta per essere distrutto comunque.

Il costruttore di spostamento accetta un riferimento rvalue, indicato con &&:

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    // Costruttore di spostamento
    Buffer(Buffer&& other) noexcept {
        data = other.data;       // Acquisisci il puntatore
        size = other.size;
        other.data = nullptr;    // Lascia l'oggetto sorgente in uno stato valido
        other.size = 0;
    }
    
    ~Buffer() { delete[] data; }
};

La differenza fondamentale rispetto alla copia: non allochiamo nuova memoria. Ci limitiamo a prendere possesso della memoria esistente e a impostare il puntatore dell'origine su nullptr, così il suo distruttore non eliminerà i nostri dati.

I costruttori di spostamento vengono chiamati quando si inizializza a partire da oggetti temporanei o quando si usa std::move():

Buffer createBuffer() {
    return Buffer(1000);    // Costruttore di spostamento utilizzato (valore restituito)
}

Buffer b1(1000);
Buffer b2(std::move(b1));   // Spostamento esplicito - b1 ora è vuoto

Contrassegna i costruttori di spostamento come noexcept quando possibile. Questo indica al compilatore che l'operazione non genererà eccezioni, consentendo importanti ottimizzazioni nei contenitori come std::vector.

challenge icon

Sfida

Facile

Costruiamo un sistema di pacchetti di dati che dimostri come i costruttori di spostamento trasferiscano in modo efficiente la proprietà delle risorse. Creerai una classe DataPacket che gestisce un array di byte allocato dinamicamente e implementerai un costruttore di spostamento che «ruba» i dati invece di copiarli.

Creerai due file per organizzare il codice:

  • DataPacket.h: definisci una classe DataPacket che gestisce un payload di dati interi. La classe dovrebbe avere:
    • Membri privati: un puntatore a un array di interi (payload), un size per il numero di elementi e un packetId (stringa) per identificare il pacchetto
    • Un costruttore con parametri che accetta un ID del pacchetto e una dimensione, alloca l'array e lo riempie con valori da 0 a dimensione-1. Stampa "Packet <id> created with size <size>"
    • Un costruttore di spostamento contrassegnato con noexcept che trasferisce la proprietà del payload dall'oggetto sorgente. Stampa "Packet <id> moved". Ricorda di lasciare l'oggetto sorgente in uno stato vuoto ma valido (nullptr, dimensione 0)
    • Un distruttore che libera la memoria se il puntatore non è null e stampa "Packet <id> destroyed"
    • Un metodo getSize() che restituisce la dimensione corrente
    • Un metodo getId() che restituisce l'ID del pacchetto
    • Un metodo getSum() che restituisce la somma di tutti gli elementi del payload (restituisce 0 se il payload è null)
  • main.cpp: dimostra la costruzione per spostamento leggendo da input un ID del pacchetto e una dimensione. Poi:
    • Crea un DataPacket chiamato original con i valori di input
    • Stampa "Original - ID: <id>, Size: <size>, Sum: <sum>"
    • Crea un nuovo pacchetto chiamato transferred spostando i dati da original con std::move()
    • Stampa "After move:"
    • Stampa "Original - ID: <id>, Size: <size>, Sum: <sum>"
    • Stampa "Transferred - ID: <id>, Size: <size>, Sum: <sum>"

Il formato di input sarà:

  • Prima riga: ID del pacchetto (stringa)
  • Seconda riga: dimensione (intero)

Dopo lo spostamento, il pacchetto originale dovrebbe avere dimensione 0 e somma 0 (poiché i suoi dati sono stati trasferiti), mentre il pacchetto trasferito dovrebbe contenere tutti i dati originali. Questo dimostra come la semantica di spostamento eviti costose copie profonde semplicemente trasferendo la proprietà del puntatore.

Includi il file header in main.cpp usando #include "DataPacket.h" e non dimenticare di includere <utility> per std::move().

Provalo tu

#include <iostream>
#include <string>
#include <utility>
#include "DataPacket.h"

using namespace std;

int main() {
    // Leggi l'input
    string packetId;
    int size;
    cin >> packetId;
    cin >> size;

    // TODO: Crea un DataPacket chiamato 'original' con i valori di input

    // TODO: Stampa "Original - ID: <id>, Size: <size>, Sum: <sum>"

    // TODO: Crea un nuovo pacchetto chiamato 'transferred' spostando i dati da original
    // Suggerimento: usa std::move()

    // TODO: Stampa "After move:"

    // TODO: Stampa "Original - ID: <id>, Size: <size>, Sum: <sum>"

    // TODO: Stampa "Transferred - ID: <id>, Size: <size>, Sum: <sum>"

    return 0;
}
quiz iconMettiti alla prova

Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.

Tutte le lezioni di Programmazione orientata agli oggetti

Esercitati da solo: Compilatore C++ online