Это плохой дизайн для класса, чтобы дать доступ к своим данным (через ptr / it), когда эти данные могут быть удалены до того, как объект класса выйдет из области видимости? - PullRequest
3 голосов
/ 11 мая 2011

Классическим примером является аннулирование итератора:

std::string test("A");
auto it = test.insert(test.begin()+1,'B');
test.erase();
...
std::cout << *it;

Считаете ли вы, что этот вид API - плохой дизайн, и его будет трудно освоить / использовать для начинающих?

Дорого,с точки зрения производительности / памяти, решение было бы, в этом случае, назначить указатель / итератор пустой строке (или nullptr, но это не очень полезно), когда используется метод очистки.

Некоторые точности

Я думаю об этом проекте для возврата константных символов *, которые могут быть изменены внутри (возможно, они хранятся в std :: vector, который может быть очищен).Я не хочу возвращать std :: string (двоичная совместимость) и не хочу метод get (char *, std :: size_t) из-за аргумента размера, который нужно получить (слишком медленно).Также я не хочу создавать обертку вокруг std :: string или моего собственного строкового класса.

Ответы [ 3 ]

4 голосов
/ 11 мая 2011

Я бы порекомендовал прочесть Философия дизайна Степанова (страницы 9-11):

[Этот пример] написан в понятном объектно-ориентированном стиле с использованием методов получения и установки. Сторонники этого стиля говорят, что преимущество таких функций состоит в том, что они позволяют программистам впоследствии изменять реализацию. То, что они забывают упомянуть, так это то, что иногда ужасно хорошо разоблачать реализацию. Давайте посмотрим, что я имею в виду. Мне трудно представить эволюцию системы, которая позволила бы вам сохранить интерфейс получения и установки, но иметь возможность изменить реализацию. Я мог бы представить, что реализация перерастает int и вам нужно переключиться на long. Но это другой интерфейс. Я могу себе представить, что вы решили переключиться с массива на список, но это также заставит вас изменить интерфейс, поскольку на самом деле не очень хорошая идея индексировать в связанный список.

Теперь давайте посмотрим, почему действительно полезно выставлять реализацию. Давайте предположим, что завтра вы решите отсортировать целые числа. Как ты можешь это сделать? Не могли бы вы использовать библиотеку C qsort? Нет, так как он ничего не знает о ваших геттерах и сеттерах. Не могли бы вы использовать STL sort? Ответ тот же. Хотя вы разрабатываете свой класс, чтобы выдержать некоторые гипотетические изменения в реализации, вы не разработали его для очень распространенной задачи сортировки. Конечно, сторонники методов получения и установки предложат вам расширить свой интерфейс с помощью функции-члена sort. После этого вы обнаружите, что вам нужен бинарный поиск, медиана и т. Д. Очень скоро ваш класс будет иметь 30 функций-членов, но, конечно, он будет скрывать реализацию. И это может быть сделано, только если вы являетесь владельцем класса. В противном случае вам необходимо реализовать достойный алгоритм сортировки поверх интерфейса «сеттер-получатель» с нуля, и это гораздо более сложное и опасное действие, чем можно себе представить. ...

Сеттеры и геттеры усложняют наше ежедневное программирование, но обещают огромные выгоды в будущем, когда мы обнаружим лучшие способы хранения массивов целых чисел в памяти. Но я не знаю ни одного реалистичного сценария, когда сокрытие областей памяти внутри нашей структуры данных помогает, а воздействие наносит ущерб; поэтому я обязан предоставить гораздо более удобный интерфейс, который также соответствует привычному интерфейсу массивов C. Когда мы программируем в C++, нам не должно быть стыдно за его C наследие, но в полной мере использовать его. Единственные проблемы с C++ и даже единственные проблемы с C возникают, когда они сами не согласуются с их собственной логикой. ...

Мое замечание по поводу раскрытия местоположений адресов последовательных целых чисел не является остроумным. Потребовалось много усилий, чтобы убедить комитет по стандартизации, что такое требование существенное свойство векторов; однако они не согласны с тем, что итераторы вектора должны указатели и, следовательно, на нескольких основных платформах - в том числе Microsoft - это быстрее отсортировать ваш вектор, сказав невероятно уродливый

if (!v.empty()) {
    sort(&*v.begin(), &*v.begin() + v.size());
}

, чем предполагалось

sort(v.begin(), v.end());

Попытки навязать псевдо-абстрактность ценой эффективности могут быть побеждены, но за ужасную цену.

У Степанова есть много других интересных документов , особенно в разделе «Заметки к классу».

Да, есть несколько правил, касающихся ООП. Нет, я не уверен, что это действительно лучший способ сделать что-то. Когда вы работаете с STL, имеет смысл делать вещи, совместимые с STL. А когда ваша абстракция имеет низкий уровень (например, std::vector, что предназначено специально для упрощения работы с динамически размещаемыми массивами; т. Е. Она должна использоваться почти как массив с некоторыми дополнительными функциями), тогда некоторые из этих правил ООП будут полезны не имеет никакого смысла вообще.

Чтобы ответить на первоначальный вопрос: даже новичкам в конечном итоге нужно будет узнать об итераторах, времени жизни объекта и о том, что я назову сроком полезного использования объекта (т. Е. «Объект не выпал из области видимости, но больше не действителен»). использовать, как недействительный итератор "). Я не вижу смысла пытаться скрыть эти факты жизни от пользователя, поэтому лично я бы не стал исключать API на основе итераторов на этих основаниях. Реальный вопрос заключается в том, что ваш API предназначен для абстрагирования и что он должен представлять (аналогично тому факту, что vector является более хорошим массивом и предназначен для раскрытия его природы массива). Если вы ответите на это, у вас должно получиться лучшее представление о том, имеет ли смысл API на основе итераторов.

1 голос
/ 11 мая 2011

Как говорит Скотт Мейерс в Effective C ++: да, это действительно не очень хороший способ предоставлять доступ частным / защищенным членам через указатели, итераторы или ссылки, потому что вы никогда не знаете, что будет с ним делать клиентский код.

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

0 голосов
/ 11 мая 2011

Это плохая или ошибочная реализация, а не дизайн.
Что касается предоставления доступа к закрытым или защищенным элементам с помощью указателей, в основном это разрушает один из базовых принципов ООП Abstraction.

Я не уверен, что вопрос в том, Да, конечно, плохо иметь реализацию, которая делает недействительным итератор.Что такое настоящий Q здесь?

...