2010-09-27 3 views
0

Я хотел сделать специальную версию shared_ptr, которая выполняла бы определенные операции, когда она была создана или уничтожена, но мои планы, похоже, были сорваны реализацией того, что деструктор shared_ptr не является виртуальный, то есть когда я переопределяю его, мои указатели никогда не очищаются, когда последний экземпляр их уничтожается.Почему у shared_ptr нет виртуального дескриптора? (и как я могу обойти это?)

Единственной альтернативой, которая приходит на ум, является построение этого поведения в каждом классе, который я хочу использовать с моим гипотетическим пользовательским shared_ptr, и это невозможно (или возможно в некоторых случаях).

Edit:

Причины, я хочу это, потому что я хочу использовать некоторые классы как USERDATA объектов в Lua, и я хочу, чтобы каждый из моих объектов, которые я использую этот способ иметь таблицу fenv уникальную для него который будет очищен, когда все ссылки на объект будут удалены. Я планирую использовать адрес указателя, когда они входят в таблицу, содержащую таблицу fenv.

Допустим, у меня есть виджет, который может иметь другие виджеты в качестве детей. Я создаю два виджета в Lua, затем устанавливаю его как дочерний элемент другого и удаляю все ссылки lua на дочерний виджет (тот факт, что он является дочерним, обрабатывается на C++). Теперь GC может работать в любое время и удалять ребенка. Я не обязательно хочу, чтобы у ребенка был его деструктор, хотя я хочу сделать его shared_ptr. Таким образом, объекты C++ все еще могут использовать его после того, как Lua очистит его. Если я присвоил значения или функции этому файлу, я все равно хочу иметь к ним доступ. Только когда окончательная ссылка на мой дочерний виджет будет удалена, я хочу, чтобы fenv был удален полностью.

+3

Вы можете рассказать нам, что вы намерены делать по специализации 'shared_ptr'? Может быть, есть альтернативные подходы. – Naveen

+3

Как обычно, скажите нам цель не за шагом. Он не имеет виртуального деструктора, потому что он не предназначен для базового класса. – GManNickG

+0

Обновлен вопрос по причине, по которой я хочу это сделать. – Alex

ответ

8

Он уже имеет эту возможность, построенный в, без необходимости, чтобы позволить людям делать опасные вещи, как извлечь из него:

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

/* 
* Done as a function for simplicity. 
* But this can be done in so many ways 
*/ 
void MyCleanup(int* x) 
{ 
    std::cout << "DONE\n"; 
    delete x; 
} 

int main() 
{ 
    boost::shared_ptr<int> x(new int(5), MyCleanup); 

} 

Проблема с извлечением:
Только от верхней части моей головы.

class X: public shared_ptr<int> { /* STUFF. With a special destructor. */ }; 

int main() 
{ 
    /* what happens now? Similar to slicing but not quite */ 
    X    data1(new int(5)); 
    shared_ptr<int> data2; 
    shared_ptr<int> data3(data); 

    data2 = data1; 
} 
+1

Просто из любопытства, почему опасно выводить из shared_ptr? – Alex

+2

@Alex: потому что вы принимаете 'shared_ptr' за ** значение **, а не ссылку, и, таким образом, выведение подразумевает обработку объектов. Подумайте об этом: вы хотите распределить 'shared_ptr' на куче :)? –

0

если вы черпаете класс your_shared_ptr от shared_ptr и переопределить деструктор, ваш деструктор должен быть вызван в коде следующим образом:

{ 
    your_shared_ptr<int> x(new int); 
} 

Если вы используете его, как это, вместо того, чтобы:

{ 
    shared_ptr<int>* ptrptr = new your_shared_ptr<int>(new int); 
} 

то это не будет, но вам это действительно нужно?

Или я что-то не понимаю?

+0

Мне нужно, чтобы оба деструктора вызывались, что требует, чтобы shared_ptr имел виртуальный деструктор. – Alex

+1

no, destructor shared_ptr будет вызываться, даже если он не виртуальный. Выполните этот код, например: http://www.java2s.com/Code/Cpp/Class/Derivedclasscallitsbaseconstructor.htm (проверено с помощью Visual C++ 2008) – Philipp

+0

Да, если вы вывести 'std :: shared_ptr' и уничтожить производный класс, Будет вызван деструктор 'share_ptr's'. Вам понадобится виртуальный деструктор, когда вы будете разрушать полиморфно. –

1

Вы можете указать пользовательский объект удаления, который будет использоваться совместно с shared_ptr. Если вы пытаетесь вставить дополнительную информацию в shared_ptr, вы можете лучше помещать ее в объект удаления. Для меня это не очень чисто, но это работает.

class ExtraThingToDestroy 
{ 
    public: 
    ~ExtraThingToDestroy() { std::cout<<"Destroying the extra thing"<<std::endl; } 
}; 

template<typename T> 
class CustomDestructor 
{ 
    public: 
    CustomDestructor(ExtraThingToDestroy * v) : v_(v) {} 
    void operator()(T* t) { delete t; delete v_; } 
    ExtraThingToDestroy * v_; 
}; 

main() 
{ 
    shared_ptr<int> ptr(new int, MyExtraDestructor<int>(new ExtraThingToDestroy)); 
    shared_ptr<int> ptr2 = ptr; 
    //Now when ptr and all its copies get destroyed, 
    // the ExtraThingToDestroy will get deleted along with the int. 
} 
4

Просто сделайте объект-обертку; намного легче. Вы можете иметь объект-оболочку с экземпляром shared_ptr внутри него и по-прежнему использовать адрес распределения внутреннего объекта в качестве индекса. Это кажется намного лучше, чем сбрасывание с помощью деривации или пользовательских процедур очистки, если только я что-то не пропускаю.

Например:

class CWrapsLuaObject 
{ 
    CWrapsLuaObject(LuaObject* pObject) 
    { [assign internal ptr, do mapping, etc.] } 

    shared_ptr<LuaObject> m_spObject; 

    [...] 
}; 

shared_ptr<CWrapsLuaObject> spInstance(new CWrapsLuaObject(pObject)); 

Am I отсутствует, почему это не было бы самым простым решением (не принимая ничего от других предложенных решений, которые могли бы также работать)?