配列データの変更を防ぐ方法は?


9

次のようなクラスがあるとします(これは単なる例です)。

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; }
};

constコピーコンストラクタとの両方でdoNotChangeMyData機能それはとてもそれが作るptr変更することはできません。ただし、これにより、が指す配列の内容を変更できますptr

ptrの配列の内容がconstインスタンスでのみ変更されるのを防ぐ方法はありますか。「注意する」(または生のポインタから変更する)ことはできませんか?

私は次のようなことができることを知っています

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

しかし、私はむしろする必要はありません...


1
使用できますstd::vector
idclev 463035818

std::vector::operator[]()値を正しく変更できますか?
marvinIsSacul

@ formerlyknownas_463035818編集された質問なので、オプションではありません;)これは理論的な質問ですが、はい、vector機能します。
ChrisMM

2
@marvinIsSacul確かに、参照をstd::vector::operator[]() const返しますconst
idclev 463035818 '10

@ChrisMM私が期待したのは、部屋の象に言及したかっただけです:)
idclev 463035818

回答:


7

ポインタは伝播しませんconstconst型に追加するとdouble*yieldsになりdouble* constconst逆参照すると左辺値ではなくなります。

代わりに、次を使用できます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
};

または組み込み配列(非推奨):

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

3つのオプションすべてが伝播しconstます。

本当にポインタを使用したい場合は(強くお勧めしません)、少なくともa std::unique_ptrを使用して手動のメモリ管理を回避します。std::experimental::propagate_constライブラリの基本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.
    // ...
};

まだ標準には含まれていませんが、多くのコンパイラがサポートしています。もちろん、このアプローチは適切なコンテナよりも劣っています。


基礎となるデータ型を変更せずにこれを実行しようとすることは、何よりも理論的な質問です。できない場合は、不可能として受け入れます。
ChrisMM

@ChrisMMポインタソリューションで回答を更新しました。しかしなぜ:)
LF

「なぜ」は答えるのが難しい、もっと好奇心。「組み込み配列」、またはstd::arrayコンパイル時にサイズがわからない場合は機能しません。vectorオーバーヘッドを追加します。unique_ptrオーバーヘッドは追加されませんが、ポインターを共有する必要がshared_ptrある場合は、オーバーヘッドを追加する必要があります。私はVSが現在サポートしているとは思いませんpropagate_const(少なくともcppreferenceによって参照されるヘッダーファイルはには存在しません/std:c++latest):(
ChrisMM

1
@ChrisMMのオーバーヘッドはvector、特に手動のメモリ管理の労力と比較して、TBHを過大評価することがよくあります。また、ポインタを手動で共有する場合、参照カウントを使用する必要があるため、オーバーヘッドはに固有ではありませんshared_ptr。VS propagate_constがまだサポートしていない(GCCとClangの両方がIIRCをサポートしている)ことを知りませんでしたが、仕様に従って独自に展開することは難しくありません。
LF

オーバーヘッドは最小限であることに同意しますが、パフォーマンスが重要な場合(メモリと時間)に生のポインターを使用する理由があります。私は時々、vectorthenを使用して、そのコンテンツを.data()or 経由で取得&vec[0]し、代わりに直接それを使用します。共有の場合、私は多くの場合、作成および削除するポインターの所有者を1人所有していますが、他のクラスがデータを共有しています。
ChrisMM
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.