Как c ++ auto_ptr относится к управляемым указателям (Java, C # ...) - PullRequest
0 голосов
/ 28 января 2010

Я из управляемого мира, и автоматическое управление памятью c ++ мне совершенно неясно

Если я правильно понимаю, я инкапсулирую указатель в объекте стека, и когда auto_ptr выходит из области видимости, он автоматически вызывает delete для указанного объекта?

Как использовать его и как естественным образом избегать проблем, связанных с c ++?

Ответы [ 6 ]

3 голосов
/ 28 января 2010

auto_ptr - самая простая реализация RAII в C ++. Ваше понимание верно, всякий раз, когда вызывается его деструктор, базовый указатель получает delete d.

Это один шаг вверх от C, где у вас нет деструкторов, и любой значимый RAII невозможен.

Следующий шаг на пути к автоматическому управлению памятью - shared_ptr. Он использует подсчет ссылок, чтобы отслеживать, жив ли объект. Это позволяет программисту создавать объекты немного более свободно, но все же не так мощно, как сборка мусора в Java и C #. Одним из примеров, где этот метод не работает, являются циклические ссылки. Если A имеет указатель с подсчетом ссылок на B, а B имеет указатель с подсчетом ссылок на A, они никогда не будут уничтожены, даже если ни один другой объект не использует их.

Современные объектно-ориентированные языки используют некоторые разновидности mark и sweep . Этот метод позволяет управлять циклическими ссылками и достаточно надежен для большинства задач программирования.

2 голосов
/ 28 января 2010

Да, std::auto_ptr вызывает delete для своего контента, когда он выходит из области видимости. Вы используете auto_ptr только в том случае, если нет совместного владения.
auto_ptr не особенно гибок, его нельзя использовать с объектами, созданными с помощью new[] или чем-либо еще.

К общему владению обычно обращаются с помощью общих указателей, например, boost имеет реализации. Наиболее распространенное использование, например. в Boosts shared_ptr использует схему подсчета ссылок и очищает объект, когда последний интеллектуальный указатель выходит из области видимости. shared_ptr имеет одно большое преимущество - оно позволяет вам задавать пользовательские удалители. При этом вы можете поместить в него все виды ресурсов и просто указать, какой инструмент удаления он должен использовать.

1 голос
/ 31 марта 2011

Выберите императивный язык (например, C, C ++ или ADA), который предоставляет типы указателей.

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

Тщательно рассмотрите вопрос семантики копирования и семантики ссылок. Реализуйте переводчик для языка, используя DrRacket.

1 голос
/ 28 января 2010

Вместо того, чтобы пытаться понять auto_ptr и его отношение к ссылкам, собираемым мусором, вы должны действительно попытаться увидеть базовый шаблон:

В C ++ все локальные объекты имеют свои деструкторы, вызываемые, когда они выходят из области видимости. Это может быть использовано для очистки памяти. Например, , мы могли бы написать класс, который в своем конструкторе получает указатель на память, выделенную из кучи, а в своем деструкторе освобождает этот указатель.

Это в значительной степени то, что делает auto_ptr. (К сожалению, auto_ptr также имеет несколько необычную причудливую семантику для назначения и копирования)

Это также то, что делают boost::shared_ptr или другие умные указатели. В этом нет магии. Это просто классы, которым в своем конструкторе дан указатель, и, поскольку они обычно размещаются в стеке, они в какой-то момент автоматически выйдут из области видимости, и поэтому вызывается их деструктор, который может удалить указатель Вы изначально перешли к конструктору. Вы можете написать такие классы самостоятельно. Опять же, никакой магии, просто прямое применение правил жизни C ++: когда локальный объект выходит из области видимости, вызывается его деструктор.

Многие другие классы исключают посредников и просто позволяют одному и тому же классу выполнять как распределение, так и освобождение. Например, std::vector вызывает new по мере необходимости для создания своего внутреннего массива - и в своем деструкторе он вызывает delete для его освобождения.

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

auto_ptr, или умные указатели в целом, не являются святым Граалем. Они не «решают» проблему управления памятью. Они являются одной из полезных частей рецепта, но чтобы избежать ошибок управления памятью и головной боли, вам необходимо понимать базовый шаблон (обычно известный как RAII ) - то есть, когда у вас есть распределение ресурсов, оно должен быть привязан к локальной переменной, которая также отвечает за ее очистку.

Иногда это означает, что вы сами вызываете new для выделения памяти, а затем передаете результат в auto_ptr, но чаще всего это означает, что вы сначала не вызываете new - просто создайте нужный вам объект в стеке, и пусть он вызывает new, как требуется внутри. Или, возможно, даже не нужно звонить new внутри страны. Хитрость в управлении памятью заключается в том, чтобы просто полагаться на объекты, выделенные локальным стеком, а не на выделение кучи. Не используйте new по умолчанию.

1 голос
/ 28 января 2010

Вы должны использовать boost::shared_ptr вместо std::auto_ptr.

auto_ptr и shared_ptr просто сохраняют экземпляр указателя и, поскольку они являются локальными объектами стека, они освобождаются, когда выходят из области видимости. Как только они освобождаются, они вызывают удаление по внутреннему указателю.

Простой пример: Actall shared_ptr и auto_ptr являются более сложными (у них есть методы для назначения и преобразования / доступа к внутреннему указателю):

template <typename T>
struct myshrdptr
{
    T * t;
    myshrdptr(T * p) : t(p) {}
    ~myshrdptr()
    {
        cout << "myshrdptr deallocated" << endl;
        delete t;
    }

    T * operator->() { return t; }
};

struct AB
{
    void dump() { cout << "AB" << endl; }
};

void testShrdptr()
{
    myshrdptr<AB> ab(new AB());
    ab->dump();
    // ab out of scope destructor called
    // which calls delete on the internal pointer
    // which deletes the AB object
}

Откуда-то еще:

int main()
{
   testShrdptr();
   cout << "done ..." << endl;
}

вывести что-то вроде (вы можете видеть, что деструктор называется):

AB
myshrdptr deallocated
done ...
1 голос
/ 28 января 2010

Вот как вы используете умный указатель. Для примера я буду использовать shared_ptr.

{
    shared_ptr<Foo> foo(new Foo);
    // do things with foo
}
// foo's value is released here

Практически все интеллектуальные указатели стремятся достичь чего-то похожего на то, что объект, удерживаемый в интеллектуальном указателе, освобождается в конце области действия интеллектуального указателя. Тем не менее, есть три типа интеллектуальных указателей, которые широко используются, и они имеют очень разную семантику того, как обрабатывается владение:

  1. shared_ptr использует «общее владение»: shared_ptr может храниться более чем в одной области действия / объекте, и все они владеют ссылкой на объект. Когда последняя ссылка отваливается, объект удаляется. Это делается с помощью подсчета ссылок.
  2. auto_ptr использует «передаваемую собственность»: значение auto_ptr может храниться только в одном месте, и каждый раз, когда присваивается auto_ptr, цессионарий получает право собственности на объект, а цедент теряет его ссылка на объект. Если выход из области auto_ptr завершен, а объект не передан другому auto_ptr, объект удаляется. Поскольку одновременно существует только один владелец объекта, подсчет ссылок не требуется.
  3. unique_ptr / scoped_ptr использует «непередаваемое владение»: объект удерживается только в том месте, где он был создан, и не может быть перенесен в другое место. Когда программа покидает область, в которой создается unique_ptr, объект удаляется, без вопросов.

Это много, я позволю, но я надеюсь, что все скоро затонет. Надеюсь, это поможет!

...