Menu
CoddyTech

Semantik Pemindahan & Rvalue

Termasuk bagian Pemrograman Berorientasi Object dalam perjalanan C++ di Coddy. Pelajaran 83 dari 104.

Dalam C++, setiap ekspresi adalah lvalue (memiliki identitas yang bertahan, dapat diambil alamatnya) atau rvalue (bersifat sementara dan akan segera dihapus). Memahami perbedaan ini membuka pemahaman tentang move semantics, sebuah optimasi ampuh yang menghindari penyalinan yang tidak perlu.

rvalue reference, yang dideklarasikan dengan &&, terikat secara khusus pada object sementara. Ini memungkinkan kamu "mengambil" sumber daya dari object yang akan segera hilang:

#include <iostream>
#include <utility>

class Buffer {
    int* data;
    size_t size;
public:
    Buffer(size_t s) : data(new int[s]), size(s) {
        std::cout << "Constructed\n";
    }
    
    // Konstruktor pemindahan mengambil alih sumber daya
    Buffer(Buffer&& other) noexcept 
        : data(other.data), size(other.size) {
        other.data = nullptr;  // Biarkan sumber tetap dalam keadaan valid
        other.size = 0;
        std::cout << "Moved\n";
    }
    
    ~Buffer() { delete[] data; }
};

int main() {
    Buffer b1(1000);
    Buffer b2(std::move(b1));  // Memanggil konstruktor pemindahan
}

Fungsi std::move sebenarnya tidak memindahkan apa pun, fungsi ini hanya meng-cast lvalue menjadi rvalue reference, yang menandakan bahwa kamu bersedia melepaskan resource object tersebut. Pemindahan yang sebenarnya terjadi di move constructor atau operator assignment move.

Semantik pemindahan meningkatkan performa secara drastis saat bekerja dengan object yang membutuhkan banyak sumber daya, seperti container atau string. Alih-alih menyalin data berukuran megabita secara mendalam, kamu cukup memindahkan kepemilikan pointer, sebuah operasi yang membutuhkan waktu konstan terlepas dari ukurannya.

challenge icon

Tantangan

Mudah

Mari kita buat class DataBuffer yang mengelola sumber daya dan menunjukkan cara kerja semantik pemindahan. Kamu akan melihat bagaimana memindahkan sumber daya alih-alih menyalinnya dapat sangat meningkatkan efisiensi saat kepemilikan memori yang dialokasikan secara dinamis dialihkan.

Kamu akan menyusun kodemu dalam tiga file:

  • DataBuffer.h: Definisikan class DataBuffer yang mengelola array integer yang dialokasikan secara dinamis.

    Class-mu harus memiliki anggota privat untuk pointer data (int*), ukuran (size_t), dan nama (std::string) untuk membantu melacak buffer saat operasi berlangsung.

    Deklarasikan hal-hal berikut:

    • Constructor yang menerima nama std::string dan ukuran size_t, mengalokasikan array, dan mencetak: [name] constructed with size [size]
    • Move constructor yang menerima referensi rvalue, mengambil alih sumber daya, dan mencetak: [name] moved from [source_name] (buffer yang menerima pemindahan menggunakan nama sumber)
    • Destructor yang mencetak [name] destroyed (atau empty destroyed jika buffer dipindahkan)
    • Method getSize() yang mengembalikan ukuran saat ini
    • Method getName() yang mengembalikan nama buffer

    Ingat untuk menandai move constructor-mu dengan noexcept dan membiarkan object sumber dalam keadaan kosong yang valid (nullptr, size 0, name "empty").

  • DataBuffer.cpp: Implementasikan semua method yang dideklarasikan di header-mu. Saat destructor berjalan, hapus data hanya jika pointer-nya tidak null. Sertakan <iostream> untuk output.
  • main.cpp: Baca dua input:
    1. Nama untuk buffer-mu (string)
    2. Ukuran untuk buffer-mu (integer)

    Buat DataBuffer dengan nama dan ukuran yang diberikan. Lalu buat buffer kedua dengan memindahkan isi buffer pertama menggunakan std::move(). Setelah pemindahan, cetak keadaan kedua buffer:

    • Original: [name] size=[size]
    • New: [name] size=[size]

    Sertakan <utility> untuk std::move.

Contohnya, dengan input Alpha dan 100:

Alpha constructed with size 100
Alpha moved from Alpha
Original: empty size=0
New: Alpha size=100
Alpha destroyed
empty destroyed

Dengan input Buffer dan 50:

Buffer constructed with size 50
Buffer moved from Buffer
Original: empty size=0
New: Buffer size=50
Buffer destroyed
empty destroyed

Perhatikan bagaimana move constructor mengalihkan kepemilikan memori yang dialokasikan tanpa menyalin data apa pun. Buffer asli dibiarkan dalam keadaan kosong tetapi valid, dan saat kedua buffer dihancurkan di akhir program, hanya buffer yang masih memiliki memori yang benar-benar menghapusnya.

Coba sendiri

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

int main() {
    std::string name;
    int size;
    
    std::cin >> name;
    std::cin >> size;
    
    // TODO: Buat DataBuffer dengan nama dan ukuran yang diberikan
    
    // TODO: Buat buffer kedua dengan memindahkan isi dari buffer pertama menggunakan std::move()
    
    // TODO: Tampilkan keadaan kedua buffer:
    // Asli: [name] size=[size]
    // Baru: [name] size=[size]
    
    return 0;
}
quiz iconUji dirimu

Pelajaran ini berisi kuis singkat. Mulai pelajarannya untuk menjawab kuis dan melacak progresmu.

Semua pelajaran di Pemrograman Berorientasi Object

Latihan sendiri: Compiler C++ online