Подтвердить что ты не робот

Почему std:: pair подвергает переменные-члены?

Из http://www.cplusplus.com/reference/utility/pair/ мы знаем, что std::pair имеет две переменные-члены, first и second.

Почему дизайнеры STL решили разоблачить две переменные-члены, first и second, вместо того, чтобы предлагать getFirst() и getSecond()?

4b9b3361

Ответ 1

Для исходного С++ 03 std::pair функции доступа к членам не будут полезны.

Как и в С++ 11 и более поздних версиях (теперь мы находимся на С++ 14, с быстрым приближением С++ 17) std::pair является частным случаем std::tuple, где std::tuple может иметь любой Количество предметов. Таким образом, имеет смысл иметь параметризованный геттер, поскольку было бы нецелесообразно изобретать и стандартизировать произвольное количество имен элементов. Таким образом, вы можете использовать std::get для std::pair.

Итак, причины дизайна историчны, что текущий std::pair является конечным результатом эволюции в сторону большей общности.


В других новостях:

о

". Насколько мне известно, будет лучше инкапсулировать две переменные-члены выше и дать getFirst(); и getSecond()

нет, этот мусор.

Это, как сказать, молот всегда лучше, будь вы вождение в гвоздях, крепление винтами или обрезка куска дерева. Особенно в последнем случае молот - это просто не полезный инструмент. Молотки могут быть очень полезными, но это не значит, что они "лучше" вообще: это просто вздор.

Ответ 2

Getters и seters обычно полезны, если вы считаете, что получение или установка значения требует дополнительной логики (изменение некоторого внутреннего состояния). Это может быть легко добавлено в метод. В этом случае std::pair используется только для предоставления двух значений данных. Ни больше ни меньше. И, таким образом, добавление многословия геттера и сеттера было бы бессмысленным.

Ответ 3

Причина в том, что никакой реальной инвариантности не требуется накладывать на структуру данных, поскольку std::pair моделирует контейнер универсального назначения для двух элементов. Другими словами, объект типа std::pair<T, U> считается действительным для любого возможного элемента first и second типа T и U, соответственно. Точно так же последующие мутации в значении его элементов не могут действительно влиять на действительность std::pair как таковой.

Алекс Степанов (автор STL) явно представляет этот общий принцип дизайна во время своего курса "Эффективное программирование с компонентами" , комментируя singleton контейнер (т.е. контейнер одного элемента).

Таким образом, хотя сам принцип может быть источником споров, это является причиной формы std::pair.

Ответ 4

Getters и seters полезны, если вы считаете, что для абстракции требуется изолировать пользователей от выбора дизайна и изменений в этих вариантах, сейчас или в будущем.

Типичным примером для "сейчас" является то, что у установщика/получателя может быть логика для проверки и/или вычисления значения - например, использовать установщик для номера телефона вместо прямого выставления поля, чтобы вы могли проверить Формат; используйте геттер для коллекции, чтобы получатель мог предоставить доступному только для чтения представление элемента (коллекции) вызывающему.

Канонический (хотя и плохой) пример для "изменений в будущем" - Point - вы должны выставлять x и y или getX() и getY()? Обычный ответ заключается в использовании геттеров/сеттеров, потому что в будущем вам захочется изменить внутреннее представление от декартова до полярного, и вы не хотите, чтобы ваши пользователи подвергались воздействию (или чтобы они зависели от этого проектного решения).

В случае std::pair - это намерение, что этот класс теперь и навсегда представляет два и ровно два значения (произвольного типа) напрямую и предоставляет их значения по требованию. Это. И поэтому дизайн использует прямой доступ к члену, а не проходит через getter/setter.

Ответ 5

Можно утверждать, что std::pair было бы лучше иметь функции доступа для доступа к своим членам! В частности, для вырожденных случаев std::pair может быть преимущество. Например, если хотя бы один из типов является пустым, не конечным классом, объекты могут быть меньше (пустая база может быть сделана базой, которая не нуждается в получении собственного адреса).

В то время, когда был изобретен std::pair, эти особые случаи не рассматривались (и я не уверен, что пустая оптимизация базы была разрешена в проекте рабочего документа в то время). Из семантической точки нет никаких оснований для доступа к функциям доступа, однако: понятно, что аксессурам необходимо вернуть изменчивую ссылку для объектов const. В результате аксессор не предоставляет какой-либо формы инкапсуляции.

С другой стороны, это затрудняет работу оптимизатора, чтобы увидеть, что происходит, когда используются функции доступа. потому что вводятся дополнительные точки последовательности. Я мог представить, что Мэн Ли и Александр Степанов даже измерили, есть ли разница (и я тоже). Даже если они этого не сделали, предоставление доступа к членам напрямую, конечно, не медленнее, чем переход через функцию доступа, в то время как обратное не обязательно верно.

Я не был частью решения, а стандарт С++ не имеет обоснования, но я предполагаю, что это было преднамеренное решение сделать членов членов публичных данных членами.

Ответ 6

Основная цель геттеров и сеттеров - получить контроль над доступом. То есть, если вы выставляете "первый" в качестве переменной, любой класс может читать и писать (если не const), не сообщая классу, в котором он входит. В ряде случаев это может создавать серьезные проблемы.

Например, скажем, у вас есть класс, который представляет количество пассажиров на лодке. Вы сохраняете количество пассажиров как целое число. Если вы выставляете это число в виде пустой переменной, для него могут быть записаны внешние функции. Это может оставить вас в случае, когда на самом деле на самом деле 10 пассажиров, но кто-то изменил переменную (возможно, случайно) на 50. Это случай для получателя по количеству пассажиров (но не сеттер, который будет представлять тот же проблема).

Примером для getters и seters будет класс, который представляет собой математический вектор, в котором вы хотите кэшировать определенную информацию об векторе. Предположим, вы хотите сохранить длину. В этом случае изменение vec.x, вероятно, изменит длину/величину. Таким образом, вам нужно не только сделать x завернутым в getter, вы должны предоставить setter для x, который знает, чтобы обновить длину кэшированного вектора. (Конечно, большинство реальных математических библиотек не кэшируют эти значения и, следовательно, выставляют переменные.)

Итак, вопрос, который вы должны задать себе в контексте их использования, заключается в следующем: действительно ли этому классу необходимо будет контролировать или быть предупрежденным об изменениях этой переменной?

Ответ в чем-то вроде std:: pair - это плоский "нет". Нет никакого дела для контроля доступа к членам класса, единственная цель которых состоит в том, чтобы содержать этих членов. Конечно, парам не нужно знать, были ли затронуты эти переменные, учитывая, что это только два его члена, и, следовательно, он не имеет состояния для обновления, должен либо измениться. пара не знает, что она на самом деле содержит, и ее значение, поэтому отслеживание того, что она содержит, не стоит усилий.

В зависимости от компилятора и того, как он настроен, getters и seters могут вводить служебные данные. Вероятно, это не важно в большинстве случаев, но если бы вы поставили их на нечто фундаментальное, как std::pair, это было бы нетривиальной проблемой. Таким образом, их добавление должно было бы оправдаться - что, как я только что объяснил, не может быть.

Ответ 7

Я был потрясен количеством комментариев, которые не показывают базового понимания объектно-ориентированного дизайна (доказывает ли это, что С++ не является OO-языком?). Да, дизайн std:: pair имеет некоторые исторические черты, но это не делает плохой дизайн хорошим; и он не должен использоваться в качестве предлога для отрицания этого факта. Прежде чем я набросился на него, позвольте мне ответить на некоторые из вопросов в комментариях:

Разве вы не думаете, что int также должен иметь сеттер и getter

Да, с точки зрения дизайна мы должны использовать аксессуры, потому что мы ничего не теряем, но получаем дополнительную гибкость. Некоторые новые алгоритмы могут захотеть упаковать дополнительные биты в ключ/значения, и вы не можете кодировать/декодировать их без аксессуаров.

Зачем обертывать что-то в геттер, если в геттере нет логики?

Откуда вы знаете, что в геттере/сеттере не будет логики? Хороший дизайн не должен ограничивать возможность реализации на основе предположения. Он должен предлагать как можно большую гибкость. Помните, что дизайн std: pair также решает дизайн итератора, и, предлагая пользователям напрямую обращаться к переменным-членам, итератор должен возвращать структуры, которые фактически хранят ключ/значения вместе. Это оказывается большим ограничением. Существуют алгоритмы, которые должны содержать их отдельно. Существуют алгоритмы, которые явно не сохраняют ключ/значения. Теперь они должны копировать данные во время итерации.

Вопреки распространенному мнению, наличие объектов, которые ничего не делают, кроме хранения переменных-членов с помощью геттеров и сеттеров, - это не "способ, которым нужно делать"

Другое дикое предположение.

Хорошо, я бы остановился здесь.

Чтобы ответить на исходный вопрос: std:: pair решила выставить переменные-члены, потому что тот, кто ее проектировал, не распознал и/или не установил приоритет важности гибкого контракта. Очевидно, у них была очень узкая идея/видение того, как должны быть реализованы пары ключевого значения в карте/хэш-таблице, и чтобы ухудшить их, они позволили такому узкому взгляду на реализацию разлиться сверху, чтобы поставить под угрозу дизайн. Например, что, если я хочу реализовать замену std: unordered_map, который хранит ключ и значения в отдельных массивах на основе открытой схемы адресации с линейным зондированием? Это может значительно повысить производительность кэша для пар с маленькими клавишами и большими значениями, поскольку вам не требуется длинный прыжок через пробелы, занятые значениями, для проверки ключей. Если бы std:: pair выбрали аксессоров, для этого было бы тривиально писать итератор стиля STL. Но теперь просто невозможно достичь этого, не вызывая дополнительного копирования данных.

Я заметил, что они также поручают использовать открытое хеширование (т.е. замкнутое соединение) для реализации std:: unordered_map. Это не только странно с точки зрения дизайна (почему вы хотите ограничить, как все реализовано?), Но и довольно тупые с точки зрения реализации. Цепные хэш-таблицы с использованием связанного списка, возможно, являются самыми медленными из всех категорий. Пойдите в Google в Интернете, мы можем легко найти, что std: unordered_map часто является ковригом теста хэш-таблицы. Он даже имеет тенденцию быть медленнее, чем Java HashMap (я не знаю, как им удалось отстать в этом случае, так как HashMap также является цепной хэш-таблицей). Старое оправдание состоит в том, что цепная хеш-таблица имеет тенденцию улучшаться, когда load_factor приближается к 1, что абсолютно недействительно, потому что 1) в открытом семействе адресов есть множество методов для решения этой проблемы - когда-либо слышал о хмелеобразовании или хешировании с помощью robin-hood последний действительно был там в течение 30 причудливых лет; 2) цепная хэш-таблица добавляет служебные данные указателя (хорошие 8 байтов на 64-разрядных машинах) для каждой записи, поэтому, когда мы говорим, что load_factor неупорядоченного_мапа приближается к 1, это не 100% -ное использование памяти! Мы должны принять это во внимание и сравнить производительность unordered_map с альтернативами с одинаковым использованием памяти. И получается, что такие альтернативы, как Google Dense HashMap, в 3-4 раза быстрее, чем std:: unordered_map.

Почему они актуальны? Потому что интересно, что обязательное открытое хеширование делает дизайн std:: pair менее плохим, теперь, когда нам не нужна гибкость альтернативной структуры хранения. Более того, наличие std:: pair делает практически невозможным принятие более новых/лучших алгоритмов для записи замещающей замены std:: unordered_map. Иногда вы задаетесь вопросом, сделали ли они это намеренно, чтобы плохой дизайн std:: pair и пешеходная реализация std:: unordered_map могли выживать дольше. Конечно, я шучу, поэтому кто бы ни писал, не обижайтесь. На самом деле люди, использующие Java или Python (хорошо, я допускаю Python на растяжку), хотели бы поблагодарить вас за то, что они чувствовали себя хорошо о том, чтобы быть "так же быстро, как С++".