Тонкая ошибка при использовании временных переменных для получения итераторов в контейнер STL: как этого избежать? - PullRequest
1 голос
/ 21 марта 2012

Давайте рассмотрим этот класс:

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 1 и Statement 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 (/* [...] */)

Следует отметить, что класс *У 1016 * есть некоторые серьезные проблемы с дизайном:

  1. _map следует лучше инкапсулировать внутри класса (как для чтения, так и для доступа к записи), чтобы можно было избежать метода getTheMap()
  2. Если метод getTheMap() действительно необходим, он может вернуть ссылку на _map

Однако, учитывая класс X "как есть" (<- см. Правку ниже), есть лиспособ запретить пользователям переходить во временный итератор? </p>

EDIT: класс X можно изменить, но метод getTheMap должен существовать и возвращаться по значению.однако я также думал о предупреждениях компилятора.

Ответы [ 3 ]

2 голосов
/ 21 марта 2012

Одной из возможностей будет использование обертки, подобной этой:

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

Это предотвращает случайное использование временной карты типа карты, но делает ее там, где пользователь должен использовать копию карты.

1 голос
/ 21 марта 2012

Нет в C ++ 03. В C ++ 11 стандартной библиотеке уже должна быть включена такая защита.

0 голосов
/ 21 марта 2012

вы можете попытаться заставить getTheMap () вернуть исходный объект, используя std :: move, но я не уверен, будет ли это работать здесь.

Если нет, то я думаю, что возвращение уникального / shared_ptr члена будет лучшим вариантом.

...