Menu
Coddy logo textTech

Konstruktor przenoszący

Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 21 z 104.

Konstruktor przenoszący przenosi zasoby z obiektu tymczasowego zamiast je kopiować. Podczas gdy konstruktor kopiujący duplikuje dane, konstruktor przenoszący „przejmuje” zasoby, pozostawiając obiekt źródłowy w prawidłowym, ale pustym stanie. Pozwala to uniknąć kosztownych kopii głębokich, gdy obiekt źródłowy i tak ma zostać zniszczony.

Konstruktor przenoszący przyjmuje referencję do r-wartości, oznaczaną przez &&:

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : size(s), data(new int[s]) {}
    
    // Konstruktor przenoszący
    Buffer(Buffer&& other) noexcept {
        data = other.data;       // Przejmij wskaźnik
        size = other.size;
        other.data = nullptr;    // Pozostaw obiekt źródłowy w poprawnym stanie
        other.size = 0;
    }
    
    ~Buffer() { delete[] data; }
};

Kluczowa różnica w porównaniu z kopiowaniem: nie przydzielamy nowej pamięci. Po prostu przejmujemy własność istniejącej pamięci i ustawiamy wskaźnik źródła na nullptr, aby jego destruktor nie usunął naszych danych.

Konstruktory przenoszące są wywoływane podczas inicjalizacji z obiektów tymczasowych lub przy użyciu std::move():

Buffer createBuffer() {
    return Buffer(1000);    // Użyto konstruktora przenoszącego (wartość zwracana)
}

Buffer b1(1000);
Buffer b2(std::move(b1));   // Jawne przeniesienie – b1 jest teraz pusty

Oznaczaj konstruktory przenoszące jako noexcept, gdy to możliwe. Informuje to kompilator, że operacja nie zgłosi wyjątków, co umożliwia ważne optymalizacje w kontenerach takich jak std::vector.

challenge icon

Wyzwanie

Łatwy

Zbudujmy system pakietów danych, który pokaże, jak konstruktory przenoszące sprawnie przekazują własność zasobów. Utworzysz klasę DataPacket, która zarządza dynamicznie przydzielaną tablicą bajtów, i zaimplementujesz konstruktor przenoszący, który „przejmuje” dane zamiast je kopiować.

Utworzysz dwa pliki, aby uporządkować kod:

  • DataPacket.h: Zdefiniuj klasę DataPacket, która zarządza ładunkiem danych typu całkowitego. Twoja klasa powinna mieć:
    • Prywatne składowe: wskaźnik do tablicy liczb całkowitych (payload), size określający liczbę elementów oraz packetId (ciąg znaków) identyfikujący pakiet
    • Konstruktor sparametryzowany, który przyjmuje identyfikator pakietu i rozmiar, przydziela pamięć dla tablicy i wypełnia ją wartościami od 0 do size-1. Wyświetl "Packet <id> created with size <size>"
    • Konstruktor przenoszący oznaczony jako noexcept, który przenosi własność ładunku z obiektu źródłowego. Wyświetl "Packet <id> moved". Pamiętaj, aby pozostawić obiekt źródłowy w prawidłowym, pustym stanie (nullptr, size 0)
    • Destruktor, który zwalnia pamięć, jeśli wskaźnik nie jest pusty, i wyświetla "Packet <id> destroyed"
    • Metodę getSize(), która zwraca bieżący rozmiar
    • Metodę getId(), która zwraca identyfikator pakietu
    • Metodę getSum(), która zwraca sumę wszystkich elementów ładunku (zwraca 0, jeśli payload jest pusty)
  • main.cpp: Pokaż działanie konstruktora przenoszącego, odczytując z wejścia identyfikator pakietu i jego rozmiar. Następnie:
    • Utwórz obiekt DataPacket o nazwie original z wprowadzonymi wartościami
    • Wyświetl "Original - ID: <id>, Size: <size>, Sum: <sum>"
    • Utwórz nowy pakiet o nazwie transferred, przenosząc dane z original za pomocą std::move()
    • Wyświetl "After move:"
    • Wyświetl "Original - ID: <id>, Size: <size>, Sum: <sum>"
    • Wyświetl "Transferred - ID: <id>, Size: <size>, Sum: <sum>"

Format danych wejściowych:

  • Pierwszy wiersz: identyfikator pakietu (ciąg znaków)
  • Drugi wiersz: rozmiar (liczba całkowita)

Po przeniesieniu oryginalny pakiet powinien mieć rozmiar 0 i sumę 0 (ponieważ jego dane zostały przeniesione), natomiast przeniesiony pakiet powinien zawierać wszystkie oryginalne dane. To pokazuje, jak semantyka przenoszenia pozwala uniknąć kosztownych głębokich kopii przez samo przeniesienie własności wskaźnika.

Dołącz plik nagłówkowy w main.cpp za pomocą #include "DataPacket.h" i nie zapomnij dołączyć <utility>, aby użyć std::move().

Spróbuj swoich sił

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

using namespace std;

int main() {
    // Wczytaj dane wejściowe
    string packetId;
    int size;
    cin >> packetId;
    cin >> size;

    // TODO: Utwórz obiekt DataPacket o nazwie 'original' z wartościami wejściowymi

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

    // TODO: Utwórz nowy pakiet o nazwie 'transferred', przenosząc dane z original
    // Wskazówka: Użyj std::move()

    // TODO: Wypisz "After move:"

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

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

    return 0;
}
quiz iconSprawdź się

Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.

Wszystkie lekcje w sekcji Programowanie obiektowe

Poćwicz samodzielnie: Kompilator C++ online