Вход/Регистрация
Эффективное использование STL
вернуться

Мейерс Скотт

Шрифт:

Что из этого следует? К счастью, ничего особенно сложного:

• если переносимость вас не интересует, если вы хотите изменить значение элемента в контейнере

set/multiset
и ваша реализация STL это разрешает — действуйте. Помните о том, что ключевая часть элемента (то есть часть элемента, определяющая порядок сортировки элементов в контейнере) должна сохраниться без изменений;

• если программа должна быть переносимой, элементы контейнеров

set/multiset
модифицироваться не могут (по крайней мере, без преобразования
const_cast
).

Кстати, о преобразованиях. Вы убедились в том, что изменение вторичных данных элемента

set/multiset
может быть вполне оправданно, поэтому я склонен показать, как это делается — а точнее, делается правильно и переносимо. Сделать это нетрудно, но при этом приходится учитывать тонкость, о которой забывают многие программисты — преобразование должно приводить к ссылке. В качестве примера рассмотрим вызов
setTitle
, который, как было показано, не компилируется в некоторых реализациях:

EmpIDSet::iterator i = se.find(selectedID);

if (i != se.end) {

 i->setTitle("Corporate Deity"); // Некоторые реализации STL

} // выдают ошибку в этой строке,

// поскольку *i имеет атрибут const

Чтобы этот фрагмент нормально компилировался и работал, необходимо устранить константность

*i
. Правильный способ выглядит так:

if (i != se.end){ // Устранить

 const_cast<Empioyee&>(*i).setTitle("Corporate Deity"); // константность *i

}

Мы берем объект, на который ссылается

i
, и сообщаем компилятору, что результат должен интерпретироваться как ссылка на (неконстантный) объект
Employee
, после чего вызываем
setTitle
для полученной ссылки. Я не буду тратить время на долгие объяснения и лучше покажу, почему альтернативное решение работает совсем не так, как можно было бы ожидать.

Многие программисты пытаются воспользоваться следующим кодом:

if (i != se.end){ // Преобразовать *i

 static_cast<Employee>(*i).setTitle("Corporate Deity"); // к Employee

}

Приведенный фрагмент эквивалентен следующему:

if (i != se.end){ // То же самое,

 ((Employee)(*i)).setTitle("Corporate Deity"); // но с использованием

} // синтаксиса С

Оба фрагмента компилируются, но вследствие эквивалентности работают неправильно. На стадии выполнения объект

*i
не модифицируется, поскольку в обоих случаях результатом преобразования является временный анонимный объект — копия
*i
, и
setTitle
вызывается для анонимного объекта, а не для
*i
! Обе синтаксические формы эквивалентны следующему фрагменту:

if (i != se.end) {

 Employee tempCopy(*i); // Скопировать *i в tempCopy

 tempCopy.setTitle("Corporate Deity"); // Изменить tempCopy

}

Становится понятно, почему преобразование должно приводить именно к ссылке — тем самым мы избегаем создания нового объекта. Вместо этого результат преобразования представляет собой ссылку на существующийобъект, на который указывает

i
. При вызове
setTitle
для объекта, обозначенного ссылкой, функция вызывается для
*i
, чего мы и добивались.

Все сказанное хорошо подходит для контейнеров set и multiset, но при переходе к map/multimap ситуация усложняется. Вспомните, что

map<K, V>
и
multimap<K, V>
содержат элементы типа
pair<const K, V>
. Объявление
const
означает, что первый компонент пары определяетсякак константа, а из этого следует, что любые попытки устранить его константность приводят к непредсказуемому результату. Теоретически реализация STL может записывать такие данные в область памяти, доступную только для чтения (например, в страницу виртуальной памяти, которая после исходной записи защищается вызовом системной функции), и попытки устранить их константность в лучшем случае ни к чему не приведут. Я никогда не слышал о реализациях, которые бы поступали подобным образом, но если вы стремитесь придерживаться правил, установленных в Стандарте, — никогдане пытайтесь устранять константность ключей в контейнерах
map
и
multimap
.

  • Читать дальше
  • 1
  • ...
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: