Назначение boost:: checked_delete
Я не понимаю цели boost:: checked_delete. В документации указано:
Стандарт С++ позволяет, в 5.3.5/5, указатели на неполные типы классов удаляется с помощью выражения-удаления. Когда класс имеет нетривиальный деструктор или специфический для класса оператор delete, поведение undefined. Некоторые компиляторы выдает предупреждение, когда неполный тип удалены, но, к сожалению, не все и программисты иногда игнорируют или отключить предупреждения.
Поставляемая функция и класс шаблоны могут использоваться для предотвращения этих проблемы, поскольку они требуют полного тип и вызвать ошибку компиляции в противном случае.
Таким образом, стандарт С++ позволяет удалить неполные типы, что вызывает поведение undefined, если тип имеет нетривиальный деструктор. Какие? Как может неполный тип вообще иметь деструктор? Не является ли неполным типом только прототип?
Ответы
Ответ 1
Наиболее распространенным примером неполного типа является тот, который был объявлен только:
// this file does not include the definition of foo
class foo;
void bad(foo *f)
{
delete f; // undefined behavior if there exists foo::~foo
}
В действительности определение foo может выглядеть так:
class foo
{
public:
~foo() { ... };
};
Но если верхний код не "видел" определение класса и просто видит объявление класса, код будет компилироваться.
Ответ 2
Рассмотрим следующее:
foo.h:
#ifndef Foo_H
#define Foo_H
#include <boost/scoped_ptr.hpp>
#include <boost/utility.hpp>
class Foo : private boost::noncopyable
{
public:
Foo();
~Foo();
void do_something_interesting();
private:
class Impl; // incomplete type
boost::scoped_ptr<Impl> impl;
};
#endif
foo.cpp:
#include "Foo.h"
#include <string>
#include <iostream>
class Foo::Impl
{
public:
Impl() : name("Foo::Impl")
{}
void say_hi()
{ std::cout << name << " says hi!" << std::endl; }
std::string name;
};
Foo::Foo()
: impl(new Impl)
{}
Foo::~Foo()
{}
void Foo::do_something_interesting()
{ impl->say_hi(); }
Учитывая этот (надуманный) пример, вы не можете встраивать либо Foo::Foo
, либо Foo::~Foo
, потому что этот тип является неполным. Определяя оба в контексте, где тип Foo::Impl
является полным типом, вы можете безопасно удалить этот тип. boost::checked_delete
делает эту проверку безопасности для вас, и это чисто компиляция. Если вы либо встроите Foo::~Foo
, либо полностью опустите его, вы получите сообщение об ошибке boost::checked_delete
, где бы вы ни пытались уничтожить экземпляр Foo
.
Ответ 3
С++ позволяет использовать delete
для переменных, которые в то время являются указателями на неполные типы.
struct S; // incomplete
int main() {
S* s = NULL;
delete s; // legal
}
Компилятор в данный момент не знает, что такое S
. Если окажется, что S
имеет нетривиальный деструктор, то компилятор не должен обнаруживать эту проблему.
Практически говоря, вероятно, что когда компилятор встречает инструкцию delete
на неполном типе, он заполняет вызов тому, что он ожидает, будет типичным деструктором стандартного генератора компилятора. И если это то, чем оказывается деструктор, тогда все прекрасно. Но если окажется, что S
имеет нетривиальный деструктор, или если он предоставляет свой собственный специальный метод удаления, то то, что ранее заполнил компилятор, будет неправильным. Однако компилятор допустил, что он правильно скомпилировал инструкцию delete
и никогда не оглядывался назад. Если это предположение неверно, вы получите поведение undefined.
Функция Boost гарантирует, что она вызвана только для полных типов, что позволяет избежать поведения undefined, которое может возникать на неполных типах.