Bagaimana mencegah modifikasi untuk menyusun data?

9

Katakanlah saya memiliki kelas yang terlihat seperti ini (ini hanya sebuah contoh):

class A {
    double *ptr;
public:
    A() : ptr( new double[100] ) {}
    A( const A &other ) {
        other.ptr[7] = 15;
    }
    void doNotChangeMyData() const {
        ptr[43] = 14;
    }
    void changeMyData() {
        ptr[43] = 14;
    }
    ~A() { delete[] ptr; }
};

The constbaik konstruktor copy dan doNotChangeMyDatafungsi membuatnya sehingga ptrtidak dapat diubah; namun, ini masih memungkinkan saya untuk memodifikasi isi array yang ditunjuk oleh ptr.

Apakah ada cara untuk mencegah agar isi ptrarray tidak diubah consthanya dalam keadaan, kurang "hati-hati" (atau berganti dari pointer mentah)?

Saya tahu saya bisa melakukan sesuatu seperti

void doNotChangeMyData() const {
    const double *const ptr = this->ptr;
    ptr[43] = 14; // then this would fail to compile
}

Tapi saya lebih suka tidak harus ...

ChrisMM
sumber
1
Anda bisa menggunakanstd::vector
idclev 463035818
std::vector::operator[]()dapat mengubah nilai, kan?
marvinIsSacul
@ formerlyknownas_463035818 Pertanyaan yang diedit jadi bukan pilihan;) Ini lebih merupakan pertanyaan teoretis, tapi ya, vectorakan berhasil.
ChrisMM
2
@marvinIsSacul yakin, tetapi std::vector::operator[]() constmengembalikan constreferensi
idclev 463035818
@ ChrisMM apa yang saya harapkan, hanya ingin menyebutkan gajah di ruangan :)
idclev 463035818

Jawaban:

7

Pointer tidak diperbanyak const. Menambah constjenis double*hasil double* const, yang menghasilkan nilai yang tidak constbernilai ketika didereferensi.

Sebagai gantinya, Anda dapat menggunakan std::vector:

class A {
    std::vector<double> data(100);
public:
    // no explicit copy ctor or dtor
};

a std::array:

class A {
    std::array<double, 100> data{};
public:
    // no explicit copy ctor or dtor
};

atau array bawaan (tidak disarankan):

class A {
    double data[100] {};
public:
    // no explicit copy ctor or dtor
};

Ketiga opsi tersebut merambat const.

Jika Anda benar - benar ingin menggunakan pointer (sangat tidak disarankan), setidaknya gunakan a std::unique_ptruntuk menghindari manajemen memori manual. Anda dapat menggunakan std::experimental::propagate_constpembungkus dari dasar-dasar perpustakaan 2 TS:

class A {
    std::experimental::propagate_const<std::unique_ptr<double[]>> ptr;
public:
    A()
        : ptr{new double[100] {}}
    {
    }
    // manual copy ctor
    A(const A& other)
        : ptr{new double[100]}
    {
        std::copy_n(other.ptr.get(), 100, ptr.get());
    }
    // defaulted move ctor & dtor
    // assignment operator, etc.
    // ...
};

Ini belum dalam standar, tetapi banyak kompiler mendukungnya. Tentu saja, pendekatan ini lebih rendah daripada wadah yang tepat.

LF
sumber
mencoba melakukan ini tanpa mengubah tipe data yang mendasarinya, lebih merupakan pertanyaan teoretis daripada apa pun. Jika tidak memungkinkan, maka saya akan menerimanya sebagai tidak mungkin.
ChrisMM
@ ChrisMM Saya sudah memperbarui jawabannya dengan solusi pointer. Tapi mengapa :)
LF
"Mengapa" sulit untuk dijawab, lebih merupakan rasa ingin tahu. "Array std::arraybawaan " atau tidak berfungsi, jika Anda tidak tahu ukuran pada waktu kompilasi. vectormenambahkan overhead; unique_ptrtidak menambahkan overhead tetapi jika pointer perlu dibagikan, maka Anda perlu shared_ptrmenambahkan overhead. Saya tidak berpikir VS saat ini mendukung propagate_const(setidaknya file header yang disebut oleh cppreference tidak ada dengan /std:c++latest) :(
ChrisMM
1
@ ChrisMM overhead dari vectorTBH sering terlalu tinggi, terutama dibandingkan dengan upaya manajemen memori manual. Juga, jika Anda membagikan pointer secara manual, Anda harus menggunakan jumlah referensi, sehingga biaya overhead tidak khas shared_ptr. Saya tidak tahu bahwa VS belum mendukung propagate_const(GCC dan Clang sama-sama mendukungnya IIRC), tetapi tidak sulit untuk meluncurkan milik kami sesuai dengan spesifikasi.
LF
Saya setuju bahwa overhead minimal, tetapi ada alasan untuk menggunakan pointer mentah ketika kinerja sangat penting (memori dan waktu). Saya kadang-kadang menggunakan vectorlalu mengambil isinya melalui .data()atau &vec[0]dan langsung bekerja dengannya. Dalam kasus yang dibagikan, saya sering memiliki satu pemilik pointer yang membuat dan menghapus, tetapi kelas lain berbagi data.
ChrisMM