1

このクラスを考えてみましょう:

class X {
    std::map<uint32_t, uint32_t> _map;
public:
    X() { /* Populate the map */ }

    std::map<uint32_t, uint32_t> getTheMap() { return _map; }
};

そしてこのバグのあるコード:

X x;
// Statement 1
std::map<uint32_t, uint32_t>::const_iterator it = x.getTheMap().begin();
// Statement 2
std::map<uint32_t, uint32_t>::const_iterator et = x.getTheMap().end();

for (; it != et; it++) {
    /* Access the map using the iterator it */
}

間違った部分は、Statement 1andStatement 2で、各ステートメントの最後に破棄される一時オブジェクトへのイテレータを取得していることです。その結果、for()ループ内の動作は未定義です。

メソッドの正しい使用getTheMap()法は次のとおりです。

std::map<uint32_t, uint32_t> map = x.getTheMap();
std::map<uint32_t, uint32_t>::const_iterator it = map.begin();
std::map<uint32_t, uint32_t>::const_iterator et = map.end();

for (/* [...] */)

Xクラスにはいくつかの重大な設計上の問題があることに注意する必要があります。

  1. _mapクラス内に(読み取りアクセスと書き込みアクセスの両方で)より適切にカプセル化する必要があるため、getTheMap()メソッドを回避できます
  2. メソッドが本当に必要な場合はgetTheMap()、への参照を返すことができます。_map

ただし、クラス X が「そのまま」(<-- 以下の編集を参照) の場合、ユーザーがイテレータを一時的に取得できないようにする方法はありますか?

編集: クラスXは変更できますが、getTheMapメソッドが存在し、値によって返される必要があります。ただし、コンパイラの警告についても考えていました。

4

3 に答える 3

2

1 つの可能性は、次のようなラッパーを使用することです。

class X {
  typedef std::map<uint32_t,uint32_t> Map;
  Map _map;

  struct MapWrap {
    const Map &mapref;

    MapWrap(const Map &mapref_arg)
    : mapref(mapref_arg)
    {
    }

    operator Map() const { return mapref; }
  };


public:
  MapWrap getTheMap()
  {
    return MapWrap(_map);
  }
};

あなたがこれを得るために:

X x;
std::map<uint32_t,uint32_t>::const_iterator iter = x.getTheMap().begin(); // error
std::map<uint32_t,uint32_t> m = x.getTheMap(); // no error

これにより、マップのように一時的なものを誤って使用することを防ぎますが、ユーザーがマップのコピーを使用する必要がある場所になります。

于 2012-03-21T14:47:51.353 に答える
1

C++03にはありません。C ++ 11では、標準ライブラリでこのような保護がすでに有効になっているはずです。

于 2012-03-21T14:57:10.857 に答える
0

std::move を使用して getTheMap() に元のオブジェクトを強制的に返させることもできますが、それがここで機能するかどうかはわかりません。

そうでない場合は、メンバーの unique/shared_ptr を返すことが最良の選択肢になると思います。

于 2012-03-21T15:19:56.293 に答える