2015-01-31 2 views
1

Я прочитал «пункт» о shared_ptr в книге Скотт Мейерс „Эффективное Modern C++“, где он говорит следующее:Что такое виртуальная функция внутри блока управления shared_ptr?

Реализация обычный блок управления является более сложным, чем можно было бы ожидать. Он использует наследование, и есть даже виртуальная функция. (Он используется для обеспечения надлежащего уничтожения объекта с указателем.) Это означает, что использование std :: shared_ptrs также требует затрат на оборудование для виртуальной функции, используемой блоком управления.

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

+0

Или он дал имя «виртуальной функции» механизму «стирания типа»? – GamovCoder

+1

[Тип удаления удаления] (http://stackoverflow.com/q/5450159/2756719) требует использования виртуальных функций. (Ну, технически он этого не требует, но это самый удобный способ его реализации.) –

ответ

2

Виртуальная функция требуется для обеспечения надлежащего удаления совместно используемого объекта. В отличие от unique_ptr, shared_ptr не требует полного знания этого типа при его создании. Например. Вы можете сделать это:

class Foo; 
std::shared_ptr<Foo> foo = make_foo(); 

Обратите внимание, что в коде выше мы не имеем полного типа Foo, только опережающее объявление. Если мы допустим, что foo выходит из области видимости, объект, на который он указывает, будет корректно удален, потому что когда Foo был создан в make_foo, был создан делетер, который знает полный тип Foo и, следовательно, может вызвать соответствующие деструкторы. (Например Возможно make_foo создает Bar, который наследует от Foo и возвращает его. shared_ptr «s будет обрабатывать этот штраф.)

функция на объекте DeleteR, что shared_ptr создает для управления делеция Foo будет виртуальным, что позволяет shared_ptr к вызовите правильный деструктор.

Грубо это может быть что-то вроде этого:

struct deleter_interface { 
    virtual void ~deleter_interface = default; 
    virtual void dispose() = 0; 
}; 

template <typename T> 
struct deleter : deleter_interface { 
    T* ptr_; 
    deleter(T* ptr) : ptr_(ptr) {} 
    virtual void dispose() { delete ptr_; } 
}; 

template <typename T> 
shared_ptr { 
    T* ptr_; 
    deleter_interface* deleter_; 
    ... 
}; 

template <typename T> 
shared_ptr<T>::shared_ptr<T>(T* ptr) 
    : ptr_(ptr), deleter_(new deleter<T>(ptr)) {} 

template <typename T> 
shared_ptr<T>::~shared_ptr<T>() { deleter_->dispose(); delete deleter_; } 

Хотя это кажется более сложным, строго необходимым, чтобы позволить shared_ptr использоваться без полного типа. Например, что если вы хотите сделать это:

В ах:

struct Foo; 
std::shared_ptr<Foo> a = make_object(); 
// ... let a go out of scope 

И в a.cc:

struct Foo { ... }; 
struct Bar : Foo { ... }; 
std::shared_ptr<Foo> make_object() { return std::shared_ptr<Foo>(new Bar); } 

Если мы не имеем виртуальную функцию, используемую в Deleter кода, то Bar не будет разрушен правильно. С виртуальной функцией не имеет значения, что заголовок (a.h) никогда не видит определения Foo или Bar.

+0

hm ... Я думаю, что нет никакого способа создать объект 'deleter_interface', потому что он имеет чистую виртуальную функцию. И как 'deleter_' обнаруживает тип?Мы можем реализовать это поведение без виртуальной функции. – GamovCoder

+0

Да. Я быстро бросил код. Я обновил, так что это действительно работает. Такое поведение может быть выполнено без виртуальной функции, но только если вы всегда имели полный тип всякий раз, когда вы создавали экземпляр shared_ptr. Я добавлю дополнительное объяснение в ответ. – Shane

+0

Я имею в виду, что у нас может быть конструктор шаблонов, который получает аргумент другого типа. например: 'шаблон класс Общая { шаблон Shared (U * PTR): Deleter (PTR); // теперь deleter знает действительный тип ptr ' Но вы показали мне еще один способ реализовать это :) Спасибо! P.S. Как насчет того, если я хочу передать свой делетер? – GamovCoder