Может ли шаблонный класс Pointer иметь виртуальный деструктор? - PullRequest
5 голосов
/ 28 июня 2011

Я столкнулся с удивительным открытием при реализации идиомы pimpl с классом самодельных указателей (я знаю: зачем кататься самостоятельно, но терпеть меня).Следующие три файла содержат минимальный пример:

Pointer.h:

#pragma once 

template <typename T>
class Pointer
{
public:
    Pointer(T*p=0)
        : _p(p)
    {
    }
    virtual ~Pointer()
    {
        delete _p;
    }
private:
    void operator=(const Pointer&);
    Pointer(const Pointer&);

private:
    T*_p;
};

Foo.h:

#pragma once
#include "Pointer.h"

struct Foo
{
    Foo();
    ~Foo();

private:
    void operator=(const Foo&);
    Foo(const Foo&);

private:
    Pointer<struct FooPrivate> p;
};

main.cpp:

#include "Foo.h"

int main(int argc, char* argv[])
{
    Foo foo;
    return 0;
}

Не берите в голову, как выглядят внутренности Foo.cpp.Когда я компилирую main.cpp с MSVC 2008, я получаю предупреждение:

pointer.h(13) : warning C4150: deletion of pointer to incomplete type 'FooPrivate'; no destructor called

Предупреждение можно избежать, удалив ключевое слово virtual из деструктора Pointers.

Это не имеет смысла для меня,Это предупреждение допустимо, или это ошибка в компиляторе MSVC?Если да, могу ли я спокойно проигнорировать предупреждение?

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

Ответы [ 4 ]

4 голосов
/ 28 июня 2011

Без virtual есть только одно место, где будет называться деструктор;в пределах ~Foo, в этот момент вы предположительно полностью определили FooPrivate.Если другой экземпляр Pointer<FooPrivate> создается в другом месте, вы можете получить предупреждение обратно, но поскольку вы этого не сделаете, компилятор может сказать, что вы ведете себя безопасно.

С virtual вы можете теоретически получитьPointer<FooPrivate>, и этот новый объект может быть уничтожен откуда-то, что FooPrivate не полностью определено.Компилятор не уверен, что вы этого не делаете, поэтому выдает предупреждение.Вы можете спокойно проигнорировать это в этом тривиальном случае, но если у вас есть реальная потребность в виртуальном деструкторе, было бы хорошей идеей принять это близко к сердцу.

2 голосов
/ 28 июня 2011

Поскольку вы предоставляете деструктор для класса Foo, предупреждение кажется совершенно неверным и ложным.

Просто чтобы убедиться, что я добавил этот код в файл [foo.cpp]:

#include "foo.h"
#include <iostream>
using namespace std;

struct FooPrivate
{
    FooPrivate() { cout << "FooPrivate::<init>" << endl; }
    ~FooPrivate() { cout << "FooPrivate::<destroy>" << endl; }
};

Foo::Foo()
    : p( new FooPrivate )
{
    cout << "Foo::<init>" << endl;
}

Foo::~Foo()
{
    cout << "Foo::<destroy>" << endl;
}

Который выдал то же предупреждение (с Visual C ++ 10.0), что и вы, но вывел

FooPrivate ::
Foo ::
Foo ::
FooPrivate ::

Очевидно, что исполняемый файл не выполняет то, что сказал sillywarning…

Cheers & hth.,

0 голосов
/ 28 июня 2011

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

0 голосов
/ 28 июня 2011

Вызов delete для неполного типа - неопределенное поведение.

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