LINUX.ORG.RU

Контейнеры в C


2

3

Будучи сиплюсплюсником, меня давно интересует как те же задачи выполняются в C. Знаю, на ЛОРе есть множество приверженцев C, надеюсь они ответят на пару простых вопросов.

Я являюсь активным сторонником идеи «Алгоритм должен работать настолько быстро, насколько позволяет железо». С этой точки зрения C++ даёт потрясающие возможности из-за своей системы кодогенерации. Да, я имею в виду шаблоны.

Не будем зарываться в дебри boost'а, возьмём простую задачу - контейнеры.

Для примера я взял односвязные списки в GTK GSList и Qt QList. QList позволяет хранить как простые, так и сложные типы с конструкторами, деструкторами, типы с общими данными. При этом накладных расходов на выделение в куче не происходит, а при реаллокации выбирается нужный алгоритм в зависимости от QTypeInfo<T>, к примеру для int будет вызван memcpy(), а для QString оператор копирования. Кроме того блок данных заранее резервируется и для сложных типов вызывается placement constructor. Для удаления данных по указателям используется алгоритм qDeleteAll(from,to), который сам дёрнет нужные конструкторы. Беглый просмотр GSList показал, что в нём можно хранить только указатели, со всеми вытекающими накладными расходами на аллокацию/уничтожение памяти, ручное кастирование, слежение за утечками, фрагментацию памяти.

Аналогичная ситуация со связными списками GList и QLinkedList.

Это даже не затрагивая вопрос о типизации, в C++ компилятор сразу даст по рукам при попытке записать в контейнер неверный тип или с помощью неявного преобразования с explicit-конструктором.

Далее, счётчики ссылок и деструкторы.

К примеру, в C++ список строк будет выглядеть как QList<QString>, при этом можно забыть про внутреннюю структуру строки - конструктор, деструктор и операторы копирования сами занимаются подсчётом ссылок, разделением и уничтожением данных. Вернуть строку из функции проще простого:

QString foo()
{
    return listOfStrings.at( 5 );
}

Этот код абсолютно безопасен и оптимален с точки зрения использования памяти и процессора, поскольку время тратится только на увеличение счётчика и копирование указателя.

В GTK я сходу нашёл GString:

struct GString 
{
  gchar *str;
  gint len;
};

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

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

Или может GTK неудачный пример, тогда дайте ссылку на правильную C-библиотеку с контейнерами.

★★★★★
Ответ на: комментарий от ahonimous

а на обычном С++0x можно написать так:

map<string,Person> persons; // instead of class  
persons[ "Name" ] = { "Nick" }; // add  
for( string& name : persons ) cout << name; // list all  
persons[ "Name" ]; // by name  
persons.find( { "Nick" } ); // reverse  

и будет работать быстрее, ибо hash - тормоз, а в мапе rb-tree

shty ★★★★★
()
Ответ на: комментарий от shty

> и будет работать быстрее, ибо hash - тормоз, а в мапе rb-tree

hash надо подбирать руками под задачу - тогда есть возможность выжать в среднем 20-30% прироста( или потерять ), так что соглашусь - лучше сразу брать map, а потом, если что, уже играться с нахождением оптимальной хэш-функции

ahonimous
()
Ответ на: комментарий от Dendy

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

по-моему в ut-hash видел реализацию linked list (не поручусь за качество правда)

shty ★★★★★
()

>>Беглый просмотр GSList показал

Давно пора за очками к окулисту.

со всеми вытекающими накладными расходами на аллокацию/уничтожение памяти,

А то в крестах не надо и компилятор сам соберет мусор. А уж выполнить g_list_foreach (list, g_free, NULL) - это просто офигеть какое слежение за уничтожением памяти. Иди читай маны, прежде чем тут газ пускать.

ручное кастирование

Где? Я просто делаю g_list_append (list, pointer), и так же получаю данные: pointer = list->data, где тут какое-то кастирование?

фрагментацию памяти

И давно в крестах фрагментации нету? Используй g_lice и не суши людям мозги.

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

Увы и ах, но С - не для быдлокодеров, которые по ночам поклоняются Qt и больше ни о чем не знают и ничего не видели. В С работа со строками делается через char *, приведенная структура GString - это вообще частный случай реализации. В нормальном коде строки не будут гулять по всему коду так, что за ними будет сложно следить. Если у тебя это так - то не пиши больше такой код.

Наплевать на производительность, выделять всё в куче

И? А стек у тебя резиновый, да? Если ты делаешь детский алгоритм на 5 объектов, то твой вариант, может, и прокатит, но на чем-то большем - свалится.

делать глубокое копирование сложных структур

Зачем, когда есть GList?

Вывод: ты вообще не знаком с С и лезешь, куда зря.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от ahonimous

и будет работать быстрее, ибо hash - тормоз, а в мапе rb-tree

hash надо подбирать руками под задачу - тогда есть возможность выжать в среднем 20-30% прироста( или потерять ),

согласен, но если пишешь контейнер то придётся извращаться с выносом расчёта hash наружу + никто этим пользоваться особо не будет, зачем если есть rb-tree, которое и так даёт хороший прирост к скорости (зачастую больший), правда надо помнить про память :)

соглашусь - лучше сразу брать map, а потом, если что, уже играться с нахождением оптимальной хэш-функции

дык :)

shty ★★★★★
()
Ответ на: комментарий от Dendy

>>В функции personForNick() можно возвращать сразу структуру, не заботясь есть ли она или нет в контейнере - конструктор пустого Person() создаст «нулевые» строки, точно так же работает функция nameForNick() - вернёт нулевую строку.

Вот ты и попался. У тебя, при отсутствии данных происходит выделение памяти и возвращение абсолютно бесполезного куска вызывающему. В С же просто вернется NULL безо всякой аллокации памяти. А что будет, если я сделаю в цикле 1000 неудачных запросов по именам? Да, правильно, память будет напрягаться, в отличие от С.

Заметьте, функция allPersons() возвращает значение, которое можно где-то сохранить, при этом опять же не нужно заботиться о глубоком копировании данных, потому как внутри оператора копирования просто увеличивается счётчик ссылок.

A GList я сохранить не смогу, не.

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

См. выше - ты бредишь, выпей таблетки срочно.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

> в отличие от С.

что значит «в отличие от С»? то что он решил не пользоваться методом find, не означает, что его нет

ahonimous
()
Ответ на: комментарий от MuZHiK-2

>>со всеми вытекающими накладными расходами на аллокацию/уничтожение памяти,

А то в крестах не надо и компилятор сам соберет мусор.

нет, для этого есть яба, педон и прочие дот неты

однако, в C++ есть деструкторы и умные указатели, которые, хоть и не решают проблему, тем не менее серьёзно помогают избежать многих косяков

В нормальном коде строки не будут гулять по всему коду так, что за ними будет сложно следить. Если у тебя это так - то не пиши больше такой код.

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

shty ★★★★★
()
Ответ на: комментарий от Dendy

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

Внутри QList они, представь, тоже хранятся по указателям. Офигеть.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от Dendy

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

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

MuZHiK-2 ★★★★
()
Ответ на: комментарий от Dendy

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

Ты ли там кричал про оптимальность? А получается, что у тебя выделена память как на сам элемент, так еще на обертку, чтобы его хранить. Пиши еще.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от ahonimous

>>что значит «в отличие от С»? то что он решил не пользоваться методом find, не означает, что его нет

Это значит, что в его случае ему все равно придется возвращать объект, даже пустой. В С же я могу вернуть NULL, не выделяя память вообще. Ну это к его «суперкрутому» подходу с Qt и COW.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от shty

>>однако, в C++ есть деструкторы и умные указатели, которые, хоть и не решают проблему, тем не менее серьёзно помогают избежать многих косяков

То, что ты не можешь писать нормальный код - сугубо твои личные проблемы, а не проблемы С. Для сложных объектов в GLib есть refcount.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от shty

>>а если я работаю над анализом текстов на естественном языке? в какой момент у меня должны пропасть строчки?

Когда их использование больше не требуется. Очевидно.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от Dendy

Кастирование - приведение типов. Аллокация - выделение памяти.

Я к тому, что по-русски надо говорить, а не какими-то идиотскими терминами.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от MuZHiK-2

однако, в C++ есть деструкторы и умные указатели, которые, хоть и не решают проблему, тем не менее серьёзно помогают избежать многих косяков

То, что ты не можешь писать нормальный код - сугубо твои личные проблемы, а не проблемы С.

почему в таком несложном вопросе необходимо переходить на личности, не хватает аргументов? :)

Для сложных объектов в GLib есть refcount.

подсчёт ссылок стоит несколько в стороне от концепции конструкторов/деструкторов, он совсем не для того

shty ★★★★★
()
Ответ на: комментарий от MuZHiK-2

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

Когда их использование больше не требуется. Очевидно.

спасибо, Кэп :) я вообще-то про то, что, в общем случае, Ваша фраза:

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

не является корректной и наличие char* в том или ином месте кода не является надёжным показателем его нормальности/ненормальности

shty ★★★★★
()
Ответ на: комментарий от shty

>>почему в таком несложном вопросе необходимо переходить на личности, не хватает аргументов? :)

Потому что не надо переносить личные проблемы на какой-то ЯП.

подсчёт ссылок стоит несколько в стороне от концепции конструкторов/деструкторов, он совсем не для того

А для чего он?

MuZHiK-2 ★★★★
()
Ответ на: комментарий от shty

>>не является корректной и наличие char* в том или ином месте кода не является надёжным показателем его нормальности/ненормальности

Про гулять я имел ввиду паранойю ТС. Что надо следить за указателями. В нормальном коде автор знает, когда удалять указатель.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

>>почему в таком несложном вопросе необходимо переходить на личности, не хватает аргументов? :)

Потому что не надо переносить личные проблемы на какой-то ЯП.

простите, Вы про какие конкретно проблемы сейчас говорите?

>>подсчёт ссылок стоит несколько в стороне от концепции конструкторов/деструкторов, он совсем не для того

А для чего он?

а Вы как думаете? для определения количества ссылок на данный ресурс :)

shty ★★★★★
()
Ответ на: комментарий от MuZHiK-2

Про гулять я имел ввиду паранойю ТС. Что надо следить за указателями.

ну так надо же следить :)

В нормальном коде автор знает, когда удалять указатель.

я бы сказал «опытный программист знает когда и что надо делать с указателями» :) однако, следует отметить, что тут есть много нюансов, иначе бы не было столько ошибок в программном обеспечении

и кстати у С++ есть свои грабли, просто определённых ошибок при правильном подходе (!) он таки позволяет избежать

shty ★★★★★
()
Ответ на: комментарий от shty

>>простите, Вы про какие конкретно проблемы сейчас говорите?

Про твои проблемы с указателями.

а Вы как думаете? для определения количества ссылок на данный ресурс :)

А как ты думаешь, что будет, когда количество ссылок станет равным 0?

MuZHiK-2 ★★★★
()
Ответ на: комментарий от shty

>>ну так надо же следить :)

И? В крестах, представь, за указателями тоже надо следить.

иначе бы не было столько ошибок в программном обеспечении

Вот, а все потому, что код пишут неопытные быдлокодеры, а потом такие неосиляторы, как ТС, ноют, что им что-то не так.

и кстати у С++ есть свои грабли, просто определённых ошибок при правильном подходе (!) он таки позволяет избежать

Избежать ошибок полностью не удастся, вероятность их можно только снизить. О чем, ТС, кстати, промолчал - про все те километровые портянки шаблонов в крестах, про множественное наследование и т.д.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

>>простите, Вы про какие конкретно проблемы сейчас говорите?

Про твои проблемы с указателями.

ну и теперь только осталось привести пруф, который бы подтверждал правильность Ваши слов

shty ★★★★★
()
Ответ на: комментарий от MuZHiK-2

>>а Вы как думаете? для определения количества ссылок на данный ресурс :)

А как ты думаешь, что будет, когда количество ссылок станет равным 0?

ничего не будет :) Вы путаете конкретную реализацию с концепцией

shty ★★★★★
()
Ответ на: комментарий от shty

>>ну и теперь только осталось привести пруф, который бы подтверждал правильность Ваши слов

твои слова: однако, в C++ есть деструкторы и умные указатели, которые, хоть и не решают проблему, тем не менее серьёзно помогают избежать многих косяков

очевидно, что у тебя какой-то дискомфорт с указателями и без деструкторов.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от shty

>>ничего не будет :) Вы путаете конкретную реализацию с концепцией

Обычно refcount используется для освобождения ресурсов. ТС завел разговор про Glib (а не GTK, как ему казалось), где refcount используется для удаления объектов. И, если ты заметил, я тоже говорил про Glib. Если ты подумал про что-то свое - это, опять же, твои проблемы.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

ну так надо же следить :)

И? В крестах, представь, за указателями тоже надо следить.

конечно, я нигде обратного и не утверждал, а теперь к сути: std::tr1::shared_ptr, такой способ автоматизации процесса пойдёт?

>>иначе бы не было столько ошибок в программном обеспечении

Вот, а все потому, что код пишут неопытные быдлокодеры

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

а потом такие неосиляторы, как ТС, ноют, что им что-то не так

вообще-то ТС говорил (прошу отметить отдельно - совсем не ныл) про реализацию обобщённых контейнеров в си, и в данном вопросе я с ним вполне солидарен

shty ★★★★★
()
Ответ на: комментарий от MuZHiK-2

>>и кстати у С++ есть свои грабли, просто определённых ошибок при правильном подходе (!) он таки позволяет избежать

Избежать ошибок полностью не удастся, вероятность их можно только снизить. О чем, ТС, кстати, промолчал

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

про все те километровые портянки шаблонов в крестах, про множественное наследование и т.д.

а что не так с шаблонами и почему Вы называете их портянками? почему многокилометровые?

множественное наследование - это всего лишь парадигма, не нравится - никто использовать не заставляет :)

shty ★★★★★
()
Ответ на: комментарий от shty

>>конечно, я нигде обратного и не утверждал, а теперь к сути: std::tr1::shared_ptr, такой способ автоматизации процесса пойдёт?

Можешь здорово поиметь проблемы с потоками, придется копировать -> следить за такими указателями.

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

ВНЕЗАПНО: все допускают ошибки. Но при чем тут ЯП?

вообще-то ТС говорил (прошу отметить отдельно - совсем не ныл) про реализацию обобщённых контейнеров в си, и в данном вопросе я с ним вполне солидарен

Как раз ныл, причем очень смачно. И ему уже указали не один раз на реализацию обобщенных контейнеров - хоть GList, хоть GArray.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

>>ну и теперь только осталось привести пруф, который бы подтверждал правильность Ваши слов

твои слова: однако, в C++ есть деструкторы и умные указатели, которые, хоть и не решают проблему, тем не менее серьёзно помогают избежать многих косяков

очевидно, что у тебя какой-то дискомфорт с указателями и без деструкторов.

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

суть же моего высказывания проста: чем меньше вещей за которыми мне приходится следить вручную - тем меньше возможности для ошибки, С++ в данном случае (!) предоставляет возможности по автоматизации данного процесса за относительно доступную цену, не вижу почему бы не воспользоваться ими

shty ★★★★★
()
Ответ на: комментарий от shty

>>а почему он он на этой аксиоме должен был специально заострять внимание? напомню: разговор шёл о контейнерах и их реализации

Еще раз: реализации ему привели, но он продолжил ныть и нести хрень про выделение памяти на стеке и возвращение объектов.

а что не так с шаблонами и почему Вы называете их портянками? почему многокилометровые?

Видимо, ты не видел серьезного кода на шаблонах и не отлаживал их.

множественное наследование - это всего лишь парадигма, не нравится - никто использовать не заставляет :)

Ой как мы сразу зачесались-то! Стоило только тыкнуть носом - сразу кричать «это не мое!», да? Нет уж, раз кресты такие клевые, используй весь спектр их «клевости». Иначе от крестов ничего не останется, получится голый С.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от shty

>>С++ в данном случае (!) предоставляет возможности по автоматизации данного процесса за относительно доступную цену, не вижу почему бы не воспользоваться ими

Хорошо, давай покажи мне конкретно, в чем проблема использовать GArray или GList.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

ничего не будет :) Вы путаете конкретную реализацию с концепцией

Обычно refcount используется для освобождения ресурсов.

внезапно, refcount используют для подсчёта ссылок :)

ТС завел разговор про Glib (а не GTK, как ему казалось), где refcount используется для удаления объектов.

хорошо, давайте с другой стороны, как подсчёт количества ссылок поможет освободить ресурсы при удалении объекта?

shty ★★★★★
()
Ответ на: комментарий от MuZHiK-2

А уж выполнить g_list_foreach (list, g_free, NULL) - это просто офигеть какое слежение за уничтожением памяти

А то. В C++ всё делают деструкторы, руками вызывать new/delete надо в очень редких случаях. В C только и следи за тем в какой момент надо освобождать память — такие детали только отвлекают от решения задачи.

Reset ★★★★★
()
Ответ на: комментарий от shty

>>внезапно, refcount используют для подсчёта ссылок :)

Ты дурак что ли? Подсчет ссылок обычно используется для освобождения ресурсов.

хорошо, давайте с другой стороны, как подсчёт количества ссылок поможет освободить ресурсы при удалении объекта?

Просто: если refcount становится равным 0, то вызываются методы _dispose() и _finalize(), где ты можешь освободить ресурсы объекта так, как пожелаешь. Да, сынок, аналог деструктора, если тебе будет так проще понять.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

Можешь здорово поиметь проблемы с потоками, придется копировать -> следить за такими указателями.

за обычными указателями в многопоточной программе следить в разы сложнее чем за связкой shared_ptr/weak_ptr

Reset ★★★★★
()
Ответ на: комментарий от Reset

В C только и следи за тем в какой момент надо освобождать память — такие детали только отвлекают от решения задачи.

Ничего не отвлекают. А кому не нравится - выбирают плюсы :)

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от Reset

>>В C++ всё делают деструкторы

А деструкторы сами пишутся, не?

В C только и следи за тем в какой момент надо освобождать память — такие детали только отвлекают от решения задачи.

Память не резиновая. Есть задачи, когда она критична.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от Reset

>>за обычными указателями в многопоточной программе следить в разы сложнее чем за связкой shared_ptr/weak_ptr

А насколько оно портабельно?

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

А деструкторы сами пишутся, не?

Почти всегда сами. И что самое главное, вызываются они всегда сами.

Память не резиновая. Есть задачи, когда она критична.

При чем тут автоматический вызов деструкторов и это? Или кто-то попутал деструкторы с gc ?

Reset ★★★★★
()
Ответ на: комментарий от MuZHiK-2

СВЕТИЛО ПРОСНУЛОСЬ, Всем бояться!

Вывод: ты вообще не знаком с С и лезешь, куда зря.

Он и не заявлял что знает в достаточной мере С ) Он просто хотел увидеть, как реализовать наиболее правильно на С описанную им задачу.

Увы и ах, но С - не для быдлокодеров

О великий, запилите пример, в котором вы явите всю мощь своего интелекта помноженного на мощь С. Да так, что нам останется только цокать языком и восклицать: «Шедеврально!».

shelA
()
Ответ на: комментарий от Eddy_Em

Угу, конечно. Ты просто сложно ничего до сих пор не писал. Советую в программе которую ты недавно писал перейти с float * на vector < float > , при этом само ядро будет по прежнему работать с float *, а вот в высокоуровневом коде, который дергает функции ядра, уже не надо будет заботиться о памяти. И ты увидишь насколько всё упростится и что самое главное, без ущерба для производительности.

Reset ★★★★★
()
Ответ на: комментарий от MuZHiK-2

конечно, я нигде обратного и не утверждал, а теперь к сути: std::tr1::shared_ptr, такой способ автоматизации процесса пойдёт?

Можешь здорово поиметь проблемы с потоками, придется копировать -> следить за такими указателями.

эм, а просто с указателями в данном случае Вы не поимеете проблемы? :)

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

ВНЕЗАПНО: все допускают ошибки. Но при чем тут ЯП?

:) подумайте

И ему уже указали не один раз на реализацию обобщенных контейнеров - хоть GList, хоть GArray.

мы же ведь уже обсудили недостатки данного подхода, вот тут, или снова начнём?

shty ★★★★★
()
Ответ на: комментарий от Reset

Советую в программе которую ты недавно писал перейти с float * на vector < float > , при этом само ядро будет по прежнему работать с float *, а вот в высокоуровневом коде, который дергает функции ядра, уже не надо будет заботиться о памяти.

Да ладно, мне несложно проконтролировать вызовы free. Я обычно каждый раз, когда пишу конструкцию с malloc/calloc, дописываю сразу же кусок с free, который потом вставляю в нужную функцию.

Eddy_Em ☆☆☆☆☆
()
Ответ на: комментарий от ahonimous

нет, для этой задачи они делают #include <sys/tree.h>, а потом имеют жесточайший секас с отладкой :)

Reset ★★★★★
()
Ответ на: комментарий от Reset

>>Почти всегда сами. И что самое главное, вызываются они всегда сами.

Только в случае локально созданных объектов. Как только начинаются указатели - тоже приходится следить.

При чем тут автоматический вызов деструкторов и это? Или кто-то попутал деструкторы с gc ?

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

MuZHiK-2 ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.