Цель повышения :: checked_delete - PullRequest
16 голосов
/ 18 мая 2010

Я не понимаю цель повышения :: checked_delete. В документации написано:

Стандарт C ++ позволяет, в 5.3.5 / 5, указатели на неполные типы классов быть удалены с помощью выражения удаления. Когда класс имеет нетривиальный деструктор, или специфичный для класса оператор удаления, поведение не определено. Некоторые компиляторы выдают предупреждение о неполном типе удалил, но, к сожалению, не все делать, а программисты иногда игнорируют или отключить предупреждения.

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

Таким образом, стандарт C ++ позволяет вам удалять неполные типы, что вызывает неопределенное поведение, если у типа есть нетривиальный деструктор. Какие? Как у неполного типа вообще может быть деструктор? Разве неполный тип не является просто прототипом?

Ответы [ 3 ]

17 голосов
/ 18 мая 2010

Наиболее распространенным примером неполного типа является тот, который был объявлен только:

// 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() { ... };
};

Но если верхний код не «увидел» определение класса и просто видит объявление класса, код скомпилируется.

4 голосов
/ 18 мая 2010

Рассмотрим следующее:

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.

2 голосов
/ 18 мая 2010

C ++ позволяет использовать delete для переменных, которые в то время были указателями на неполные типы.

struct S; // incomplete

int main() {
  S* s = NULL;
  delete s; // legal
}

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

На практике, вероятно, случается, что когда компилятор встречает инструкцию delete для неполного типа, он выполняет вызов того, что, как он ожидает, будет обычным деструктором, генерируемым компилятором по умолчанию. И если таковым оказывается деструктор, то все в порядке. Но если окажется, что S имеет нетривиальный деструктор, или если он предоставляет свой собственный специальный метод удаления, то то, что заполнял компилятор ранее, будет неверным. Однако компилятор позволил предположить, что он правильно скомпилировал инструкцию delete, и никогда не оглядываться назад. Когда это предположение неверно, вы получите неопределенное поведение.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...