Menu
Coddy logo textTech

Costruttore di copia

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

Un costruttore di copia crea un nuovo oggetto come copia di un oggetto esistente. Viene chiamato quando inizializzi un oggetto a partire da un altro oggetto dello stesso tipo, passi un oggetto per valore o restituisci un oggetto per valore.

Il costruttore di copia accetta un riferimento const a un oggetto della stessa classe:

class Player {
    std::string name;
    int health;
public:
    Player(std::string n, int h) : name(n), health(h) {}
    
    // Costruttore di copia
    Player(const Player& other) {
        name = other.name;
        health = other.health;
    }
};

Player original("Hero", 100);
Player copy = original;    // Costruttore di copia chiamato
Player another(original);  // Chiama anche il costruttore di copia

Se non definisci un costruttore di copia, il compilatore ne genera uno che esegue una copia superficiale - copia direttamente il valore di ciascun membro. Questo funziona bene per i tipi semplici, ma causa problemi quando la tua classe gestisce memoria dinamica.

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s) {
        data = new int[size];
    }
    
    // Costruttore di copia profonda
    Buffer(const Buffer& other) : size(other.size) {
        data = new int[size];              // Alloca nuova memoria
        for (size_t i = 0; i < size; i++)
            data[i] = other.data[i];       // Copia il contenuto
    }
    
    ~Buffer() { delete[] data; }
};

Senza un costruttore di copia personalizzato, entrambi gli oggetti punterebbero alla stessa memoria. Quando uno viene distrutto, l’altro avrebbe un puntatore pendente. Una copia profonda alloca nuova memoria e copia i dati effettivi, assicurando che ogni oggetto possieda le proprie risorse.

challenge icon

Sfida

Facile

Costruiamo un sistema di cronologia dei messaggi che dimostri l'importanza della copia profonda quando la tua classe gestisce memoria dinamica. Creerai una classe MessageLog che memorizza i messaggi in un array allocato dinamicamente e implementerai un costruttore di copia corretto per assicurarti che ogni copia abbia la propria memoria indipendente.

Creerai due file per organizzare il codice:

  • MessageLog.h: definisci una classe MessageLog che gestisce una raccolta di messaggi usando la memoria dinamica. La tua classe dovrebbe avere:
    • Membri privati: un puntatore a un array di stringhe per memorizzare i messaggi, una capacity per il numero massimo di messaggi e un count per il numero attuale di messaggi
    • Un costruttore con parametri che accetta una capacità, alloca l'array nell'heap e inizializza count a 0
    • Un costruttore di copia che esegue una copia profonda: alloca nuova memoria e copia tutti i messaggi dall'oggetto sorgente
    • Un distruttore che libera la memoria allocata e stampa "MessageLog destroyed"
    • Un metodo addMessage(string msg) che aggiunge un messaggio se c'è spazio
    • Un metodo getCount() che restituisce il conteggio attuale dei messaggi
    • Un metodo getMessage(int index) che restituisce il messaggio all'indice specificato
  • main.cpp: dimostra che il tuo costruttore di copia crea una copia indipendente. Leggi un valore di capacità e due messaggi dall'input, poi:
    • Crea un MessageLog chiamato original con la capacità specificata nell'input
    • Aggiungi entrambi i messaggi a original
    • Crea una copia chiamata backup usando il costruttore di copia
    • Aggiungi un terzo messaggio "New message" solo a original
    • Stampa "Original count: <count>"
    • Stampa "Backup count: <count>"
    • Stampa tutti i messaggi di backup, uno per riga

Il formato dell'input sarà:

  • Prima riga: capacità (intero)
  • Seconda riga: primo messaggio (stringa)
  • Terza riga: secondo messaggio (stringa)

Se la copia profonda è implementata correttamente, il backup dovrebbe avere 2 messaggi mentre l'originale ne ha 3: questo dimostra che hanno memoria indipendente. Senza un costruttore di copia corretto, entrambi condividerebbero lo stesso array e le modifiche apportate a uno influenzerebbero l'altro!

Includi il tuo file header in main.cpp usando #include "MessageLog.h".

Provalo tu

#include <iostream>
#include <string>
#include "MessageLog.h"
using namespace std;

int main() {
    // Leggi l'input
    int capacity;
    cin >> capacity;
    cin.ignore();
    
    string msg1, msg2;
    getline(cin, msg1);
    getline(cin, msg2);

    // TODO: Crea un MessageLog chiamato 'original' con la capacità indicata nell'input

    // TODO: Aggiungi entrambi i messaggi a original

    // TODO: Crea una copia chiamata 'backup' usando il costruttore di copia

    // TODO: Aggiungi "New message" solo a original

    // TODO: Stampa "Original count: <count>"

    // TODO: Stampa "Backup count: <count>"

    // TODO: Stampa tutti i messaggi di backup, uno per riga

    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