Auto_ptr дизайн

На мой взгляд, класс должен обеспечивать четко определенную абстракцию, и никакие частные члены не должны изменяться без знания класса. Но когда я проверил «auto_ptr» (или любой другой умный указатель), это правило нарушено. См. Следующий код

class Foo{
public:
   Foo(){}
};

int main(int argc, char* argv[])
{
   std::auto_ptr<Foo> fooPtr(new Foo);
   delete fooPtr.operator ->();
   return 0;
}

Перегрузка оператора (->) дает базовый указатель, и его можно изменить без знания «auto_ptr». Я не могу считать это плохим дизайном, так как умные указатели созданы фанатами C++, но мне интересно, почему они это допустили. Есть ли способ написать умный указатель без этой проблемы.

Цени свои мысли.

Я не могу вспомнить много полезных классов, в которых нет ничего подобного. Это удобно, но не глупи с этим. delete[] &vector[0];

Mooing Duck 22.03.2012 02:15
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
1
861
6

Ответы 6

Нет, полностью запретить такое неправильное использование в C++ невозможно.

Как правило, пользователь любого библиотечного кода никогда не должен вызывать delete для любых обернутых указателей, если это специально не задокументировано. И, на мой взгляд, весь современный код C++ должен быть спроектирован таким образом, чтобы на пользователя классов никогда не возлагалась полная ответственность вручную высвобождать полученные ресурсы (т.е. вместо этого использовать RAII).

Примечание: std::auto_ptr<T> больше не лучший вариант. Его плохое поведение при копировании может привести к серьезным ошибкам кодирования. Часто лучше использовать вместо них std::tr1::scoped_ptr<T> или std::tr1::shared_ptr<T> или их варианты Увеличение.

Более того, в C++ 0x std::unique_ptr<T> функционально заменит std::auto_ptr<T> как более безопасный в использовании класс. Некоторое обсуждение темы и недавнюю реализацию C++ 03 для эмуляции unique_ptr можно найти в здесь.

Спасибо. Я использовал auto_ptr только для объяснения. Я использую boost :: shared_ptr. RAII выглядит интересно

Navaneeth K N 15.01.2009 06:17

Чтобы обеспечить быстрый, удобный, «похожий на указатель» доступ к базовому объекту, оператор operator->, к сожалению, должен немного «утечь» свою абстракцию. В противном случае интеллектуальные указатели должны были бы вручную заключать в оболочку все члены, которые могут быть открыты. Для этого либо требуется большая работа по «настройке» со стороны тех, кто создает интеллектуальный указатель, либо уровень метапрограммирования, которого просто нет в C++. Кроме того, как указывает Пирста, даже если эта дыра была заткнута, есть еще много других (возможно, нестандартных) способов подорвать механизмы контроля доступа C++.

В этом есть смысл. Спасибо

Navaneeth K N 15.01.2009 06:18

Is there any way to write a smart pointer without this problem.

Это непросто и, как правило, нет (то есть вы не можете сделать это для каждого общего класса Foo).

Единственный способ, который я могу придумать для этого, - это изменить объявление класса Foo: сделать деструктор Foo закрытым (или определить частный оператор delete как член класса Foo), а также указать в объявлении класса Foo, что std::auto_ptr<Foo> является friend.

Да, я думаю, это единственный способ. Но необходимо изменить Foo и все классы, которые будут использоваться с auto_ptr. Это будет перебор.

Navaneeth K N 15.01.2009 06:15

Люди, использующие std :: auto_ptr, должны знать, что это такое и как его использовать. Иногда C++ делает невозможным плохое; но иногда это невозможно, и вместо этого он упрощает только хорошие вещи (где «хорошо» - это «помнить об удалении объекта», а плохо - «удалять его более одного раза»).

ChrisW 15.01.2009 06:22

В C++ разработчику предоставляются все инструменты, чтобы сломать программу. Лучше всего задокументировать поведение, а также правильное использование кода. И следовать лучшим практикам, описанным в других источниках. ;)

Pyry Jahkola 15.01.2009 06:29

Умный указатель должен обладать двумя желательными свойствами:

  1. Необработанный указатель может быть получен (например, для перехода к устаревшим библиотечным функциям)
  2. Необработанный указатель не может быть получен (для предотвращения двойного удаления)

Очевидно, эти свойства противоречивы и не могут быть реализованы одновременно!shared_ptr<Foo> даже Boost и др. есть get(), значит, у них эта "проблема". На практике первое важнее, поэтому второе должно уйти.

Кстати, я не уверен, почему вы обратились к немного неясному operator->(), когда обычный старый метод get() вызывает ту же проблему:

std::auto_ptr<Foo> fooPtr(new Foo);
delete fooPtr.get();

Re "для перехода к устаревшим библиотечным функциям": нет необходимости вызывать устаревшие функции. Любая функция, которая не заботится о владении не следует, принимает в качестве аргумента умный указатель, но необработанный (или ссылку). Помимо того факта, что право собственности может мешать (многие интеллектуальные указатели не допускают наличия нескольких владельцев), функция не должна навязывать своим клиентам определенный бренд управления владением, если только это не нужно.

Marc van Leeuwen 27.06.2014 16:25

@MarcvanLeeuwen: Хороший момент, хотя я бы заменил «не заботится о праве собственности» на «не принимает владение» - каждый интерфейс должен заботиться о своей семантике владения (т.е. быть явным).

j_random_hacker 27.06.2014 16:55

@MarcvanLeeuwen: На самом деле ... Можете ли вы представить себе ситуацию, в которой для функции, не связанной с принятием прав собственности, было бы лучше принимать аргумент от T*, а не от T&? Я не могу, и думаю, что второй вариант безопаснее.

j_random_hacker 27.06.2014 16:57

Я имел в виду «не заботится» в том смысле, что все, что делает функция, не имеет отношения к владению, а не потому, что она хочет создавать утечки памяти. Но вместо захвата владения это также может быть владение раздавать, сохраняя что-то, что должно принадлежать указателю, переданному по изменяемой ссылке. И из другого комментария, наиболее очевидная ситуация, когда необходим T*, - это когда указатель может быть нулевым. Более гибким аргументом может быть то, что вы знаете, что функция в любом случае должна инициализировать переменную локального указателя; вы также можете передать этот указатель.

Marc van Leeuwen 27.06.2014 19:58

Я не думаю, что это показывает, что auto_ptr имеет проблему с инкапсуляцией. При работе с указателями, принадлежащими владельцам, очень важно понимать, кто чем владеет. В случае auto_ptr он владеет указателем, который он содержит [1]; это часть абстракции auto_ptr. Следовательно, удаление этого указателя любым другим способом нарушает контракт, предоставляемый auto_ptr.

Я согласен с тем, что относительно легко неправильно использовать auto_ptr [2], что очень не идеально, но в C++ вы никогда не сможете избежать фундаментальной проблемы «кому принадлежит этот указатель?», Потому что, к лучшему или худшему, C++ не управляет памятью за вас.

[1] Цитата с cplusplus.com: «объекты auto_ptr имеют особенность владения назначенными им указателями»: http://www.cplusplus.com/reference/std/memory/auto_ptr/

[2] Например, вы можете ошибочно полагать, что он имеет семантику значения, и использовать его в качестве параметра векторного шаблона: http://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=4&ved=0CEEQFjAD&url=http%3A%2F%2Fwww.gamedev.net%2Ftopic%2F502150-c-why-is- stdvectorstdauto_ptrmytype - плохо% 2F & ei = XU1qT5i9GcnRiAKCiu20BQ & usg = AFQjCNHigbgumbMG3MTmMPla2zo4LhaE1Q & sig2 = WSyJF2eWrq2aB2qw8dF3

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

Также учтите это. Любой тип контейнера дает вам итераторы над ними; если it является таким итератором, то &*it является указателем на элемент в контейнере; если вы скажете delete &*it, то вы мертвы. Но указание адресов его пунктов не является дефектом контейнерного типа.

Другие вопросы по теме