7

次のオブジェクトを検討してください。

class Obj
{
    public:
        Obj() : val(new int(1)) {}
        int& get() {return *val;}
        const int& get() const {return *val;}

    private:
        std::shared_ptr<int> val;
};

予想どおり、オブジェクトが構築されてコピーが作成されると、Obj によって公開された shared_ptr を介してすべて同じ値を変更できます。

    Obj nonconst1;
    Obj nonconst2(nonconst1);
    nonconst2.get() = 2;
    cout << nonconst1.get() << ", " << nonconst2.get() << endl;

非 const の 1 つからオブジェクトをコピー構築することも可能const Objです。これは、値の読み取りは許可するが書き込みは許可しないという点で正しいことを行うようです。予想どおり、次のコードはコンパイル エラーになります。

    const Obj const1(nonconst1);
    const1.get() = 3;

ただし、const オブジェクトから非 const Obj をコピー構築することは可能であり、これによって値を変更することができます。

    Obj nonconst3(const1);
    nonconst3.get() = 3;

私には、これは const-correct とは思えません。

コピー コンストラクターの動作を許可しながら、この動作を防ぐ方法はありますか? 私の実際の使用例では、Obj の std コンテナーを使用できるようにしたいと考えています。

4

6 に答える 6

2

「私には、これは const に正しく感じられません」まだそうです: 非 const で非 constgetメソッドを呼び出しているだけですObj。それは何も悪いことではありません。

求めている動作が本当に必要な場合は、 const プロキシのようなものを使用できますObjが、クライアントはもちろんそれを処理できる必要があります。

class Obj
{
  //...
  //original class definition goes here
  //...
  friend class ConstObj;
};  

class ConstObj
{
  public:
    ConstObj( const Obj& v ) : val( v.val ) {}
    const int& get() const { return *val; }

   private:
    std::shared_ptr<int> val;
};

//usage:
class WorkingWithObj
{
public:
  WorkingWithObj();
  Obj DoSomethingYieldingNonConstObj();
  ConstObj DoSomethingYieldingConstObj();
};

WorkingWithObj w;
Obj nonconst( w.DoSomethingYieldingNonConstObj() );
nonconst.get() = 3;

ConstObj veryconst( nonconst );
veryconst.get() = 3; //compiler error

ConstObj alsoconst( w.DoSomethingYieldingConstObj() );
alsoconst.get() = 3; //compiler error
于 2013-04-30T08:35:12.940 に答える
1

コピー コンストラクターの動作を許可しながら、この動作を防ぐ方法はありますか? 私の実際の使用例では、Obj の std コンテナーを使用できるようにしたいと考えています。

オブジェクトからのコピーに別のコピー コンストラクターを指定できます。constつまり、共有ポインターのコピーを回避し、代わりに NULL ポインターを使用して非 const オブジェクトを作成したり、ポイント先の数値のディープ コピーを実行したりできます。 . ただし、この種のことを行うことにはかなり注意が必要です-コピーされた変数のconstnessに応じて異なる動作を取得するのは奇妙です-プログラムの動作について推論するのが非常に難しくなるのではないかと心配しています。ただし、いくつかの動作を選択するか、現在の動作を受け入れる必要がありますstd::vector<>。これは、コピーが作成される場合があるためです。単純に未定義のままにすることはできません。

于 2013-04-30T08:42:32.540 に答える
1

それはconstの正確さを壊しません。が指す整数オブジェクトvalは別個のオブジェクトであり、元のオブジェクトが独占的に所有しているわけではありません。Objその値を変更しても、オブジェクトの状態には影響しません。

于 2013-04-30T08:37:32.553 に答える
0

Obj共有ポインターのコンテンツをコピーするコピー コンストラクターを手動で実装します。これらは異なる int インスタンスを指しているため、これによりconst1viaの内容を変更する必要がなくなります。nonconst3

ただし、 const 以外のインスタンスのディープ コピーは避けたいと考えていますObj(問題なく、古い共有ポインターを再利用することを目的としています)。このためには、const と non-const の両方のコピー コンストラクターを提供し、const のコピー コンストラクターのみをコピーする必要があります。

class Obj
{
  public:
    //...
    Obj(Obj &o) : val(o.val) {}                            // not a deep copy
    Obj(const Obj &o) : val(std::make_shared(o.get())) {}  // deep copy
    //...
}
于 2013-04-30T08:34:23.433 に答える
0

いいえ、ありません...しかし、 (非const getterで)書き込むことができる場合はCOWdeep-copyポインターを使用できます。value

または、2 つ書くこともできますcopy-ctors(for refdo シャロー コピー、for crefdo ディープ コピー)。

   A(A& obj) : pointer(obj.pointer) {}
   A(const A& obj) : pointer(new int(*obj.pointer)) {}
于 2013-04-30T08:35:41.910 に答える