Я клиент класса A, деструктор которого определен как protected. Вдобавок я не могу изменить его интерфейс (я сознательно написал «сторонний класс», хотя имел в виду, что по какой-либо причине вам не разрешено изменять его интерфейс. Итак, как я могу использовать boost :: shared_ptr в таком случае ? Деструктор виртуальный:

Class Foo {
    public:
        void Destroy () {}
    protected:
    virtual ~Foo () {}
}

Для Foo он предоставляет метод "Destroy".

Прямое использование Следующий код не компилируется:

  boost::shared_ptr <Foo> a = boost::make_shared <Foo> ();

Сообщение компилятора: ... ошибка ... "Foo :: ~ Foo () недоступен ...

Также компилятор на моем рабочем месте не поддерживает c ++ 11.

1
Daniel Heilper 26 Мар 2014 в 13:54
1
Деструктор виртуальный?
 – 
Eric Fortin
26 Мар 2014 в 13:59
Да, забыл упомянуть...
 – 
Daniel Heilper
26 Мар 2014 в 14:03
Что вы пытаетесь сделать, потому что предоставленная вами информация не указывает на то, что что-то не так с выполнением std::shared_ptr.
 – 
user2675345
26 Мар 2014 в 14:04
Использование прямого shared_ptr без какого-либо специального удаления приводит к ошибке компилятора. Boost::shared_ptr вызывает удаление по умолчанию ~A
 – 
Daniel Heilper
26 Мар 2014 в 14:07
Пользовательскому удалению не нужно вызывать деструктор, если его вызывает функция Destroy.
 – 
Eric Fortin
26 Мар 2014 в 14:08

2 ответа

Лучший ответ

Вы можете создать промежуточный класс в качестве вспомогательной полиморфной базы:

Пример с использованием только c ++ 03 Live On Coliru :

#include <boost/shared_ptr.hpp>
#include <iostream>

class Foo { // "abstract"
    public:
        virtual void Destroy () { std::cout << __FUNCTION__ << "\n"; }
    protected:
        virtual ~Foo () {}
};

class FooBase : public Foo {
    public:
        static void Deleter(FooBase* p) {
            if (p)
                p->Destroy();
            delete p;
        }

    // protected:
        virtual ~FooBase() { std::cout << __FUNCTION__ << "\n"; }
};

class FooDerived : public FooBase
{
    ~FooDerived() { std::cout << __FUNCTION__ << "\n"; }
};

int main()
{
    boost::shared_ptr<FooBase> p(new FooDerived, FooBase::Deleter);
}

Печать:

Destroy
~FooDerived
~FooBase

Примечание . Каким может быть деструктор на самом деле protected сейчас. Это гарантирует, что все разрушения пройдут через FooBase::Deleter

2
sehe 26 Мар 2014 в 15:06
Возможно, FooDerived выводит Foo непосредственно в случае OP. Если нет, то это такое хорошее решение!
 – 
ikh
26 Мар 2014 в 14:55
Исправлено. boost::bind больше не используется. Обратите внимание, что деструктор теперь может быть красивым и приватным, если хотите (/cc @ikh)
 – 
sehe
26 Мар 2014 в 14:56
Почему определен FooDerived? недостаточно ли FooBase?
 – 
Daniel Heilper
26 Мар 2014 в 15:01
FooDerived — пример класса, производного от Foo. но решение можно использовать, если все классы, производные Foo, могут быть изменены.
 – 
ikh
26 Мар 2014 в 15:03
Я хотел /продемонстрировать/, что полиморфное поведение соблюдается (вы можете установить общий указатель на производный указатель). Кроме того, не забывайте, FooBase имеет единственную цель - служить сервером в качестве "удаляемого" посредника.
 – 
sehe
26 Мар 2014 в 15:03

Просто.

boost::shared_ptr<Foo> ptr(new Foo(), [](Foo *p){ p->Destroy(); });

Заключение ответов: Это проблема неправильного дизайна библиотеки (деструктор абстрактного класса должен быть public ..). Таким образом, вы не можете использовать shared_ptr с этой библиотекой, или вам следует изменить деструктор на public, чтобы использовать shared_ptr, отредактировав файл заголовка библиотеки, даже если он такой плохой < / сильный>.

0
ikh 26 Мар 2014 в 15:13
Обратите внимание, он использует boost::shared_ptr
 – 
billz
26 Мар 2014 в 14:16
Нужно ли удалить также вызов delete на p?
 – 
Chris Drew
26 Мар 2014 в 14:18
В boost - будет ли освобожден необработанный указатель (когда счетчик ссылок равен 0)?
 – 
Daniel Heilper
26 Мар 2014 в 14:20
1
Извините, @ikh, это исключительно плохой совет (изменение заголовка). Это нарушает ODR. Это не противоречит стандарту, это просто ненадежно. Планировка класса может даже измениться. Так что не надо. Кроме того, delete nullptr; в порядке. Без проблем
 – 
sehe
26 Мар 2014 в 14:43
3
Вы совершенно упускаете суть. Вам не нужно это менять. Если деструктор закрытый, вы все равно не сможете вызвать для него delete, с умными указателями или без них. Вероятно, он не предназначен для удаления вручную.
 – 
sehe
26 Мар 2014 в 14:45