Как = удалить на деструкторе предотвращает выделение?

В этом вопросе SO указано, что эта конструкция запрещает размещение экземпляра экземпляра.

class FS_Only {
    ~FS_Only() = delete;  // disallow stack allocation
};

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

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

Ответ 1

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

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

Ответ 2

Я понимаю, что невозможно удалить этот экземпляр, явно или неявно.

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

Объявление автоматической переменной (или "распределение стека", если хотите) не просто вызывает создание экземпляра, когда программа достигает точки объявления; это также приводит к его уничтожению, когда программа покидает этот блок. С удаленным деструктором это невозможно сделать, поэтому объявление не разрешено.

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

Таким образом, единственный способ создать один из них - new, и как только вы это сделаете, вы никогда не сможете его уничтожить. Однако это не полностью предотвращает выделение стека:

char memory[sizeof(FS_Only)] alignas(FS_Only);
FS_Only * not_fs = new (memory) FS_Only;

Также зачем это нужно?

На мой взгляд, вы бы этого не сделали. Обязательная утечка памяти - это ужасный способ гарантировать, что объект не будет уничтожен в неподходящее время. Вместо этого используйте методы, такие как RAII, чтобы управлять любыми объектами, которым требуется динамическое время жизни, гарантируя, что у них всегда есть четко определенный владелец (или разделяемые владельцы), ответственный за их удаление после использовать. Умные указатели в стандартной библиотеке С++ 11 являются хорошей отправной точкой.

Ответ 3

Маркировка деструктора как удаленная сделает невозможным уничтожение объекта. Не имеет значения, находится ли он в стеке или в куче. Все уничтожение объекта (независимо от того, автоматически ли оно выходит из области видимости или делает на нем delete), вызывает деструктор. И поскольку именно компилятор обрабатывает вызов деструктора, он заметит, был ли он помечен как удаленный и выдает ошибку.

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

Ответ 4

При попытке объявить FS_Only в стеке

{ 
    FS_Only fs;
    //...
}

деструктор будет автоматически вызываться на закрывающей скобке, чтобы вы получили ошибку компиляции.
Конечно, вы не сможете delete a new 'ed:

{ 
    FS_Only * fs = new FS_Only();
    //...
    delete fs; //also won't compile
}

Ответ 5

У меня есть два ответа:


Из "Язык программирования С++":

У вас cant есть локальная переменная, которая не может быть уничтожена (§17.2.2)...


Своими словами:

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

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

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

Ответ 6

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

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