[英]C++ Shared pointers. How can I change the underlying object's pointer for all copies?
虛擬課我有一個奇怪的情況,我需要設計幫助。
我有一個解決方案,但我正在尋找一個更好的解決方案。 這是我編寫的代碼,它完成了我正在尋找的東西,但它依賴於指向指針的指針。
我覺得如果我可以直接操作共享指針的底層數據指針,我可以減少一層抽象。
我想用一個InterfaceWrapper,而不是兩個。
#include <stdio.h>
#include <memory>
#include <vector>
class Interface
{
public:
virtual void WriteIt() = 0;
virtual ~Interface() { }
};
class Foo : public Interface
{
void WriteIt() { printf ("Foo\n"); }
};
class Bar : public Interface
{
void WriteIt() { printf ("Bar\n"); }
};
// This class wraps Interface so we can call WriteIt on desctruction
// I'd like to do this in the Interface class, but you can't call virtual methods during destruction.
class InterfaceWrapper : public std::unique_ptr<Interface>
{
public:
InterfaceWrapper(Interface * i) : std::unique_ptr<Interface>(i) { }
~InterfaceWrapper() { (*this)->WriteIt(); }
};
// This class provides counted destruction to InterfaceWrapper
class InterfaceWrapper2
{
public:
InterfaceWrapper2 () : _ptr(new InterfaceWrapper(new Foo)) { }
void MakeBar() { _ptr->reset(new Bar); }
private:
std::shared_ptr<InterfaceWrapper> _ptr;
};
int main (void)
{
std::vector<InterfaceWrapper2> thing_vector;
// The default implementation will, print "Foo" on destruction.
InterfaceWrapper2 thing;
// That destructor should only happen once for all copies of 'thing'
thing_vector.push_back(thing);
// MakeBar changes the virtual implementation so it prints "Bar" instead of "Foo"
thing.MakeBar();
// When destructors happen, the program should just print "Bar" once.
return 0;
}
歡迎任何事情,但我特別感興趣的是使用boost在C ++ 03上工作的解決方案(我的例子是C ++ 11,但我的'真正的'代碼是使用boost :: shared_ptr的C ++ 03)。
我基本上是在尋找在我的示例代碼中實現InterfaceWrapper2的更好方法。 main()
是我想要完成的最好的解釋。 只記得行為被困在那些虛擬類中。
這就是你要追求的嗎?
更新3
如果你想保留接口機制,以下是通過編寫標准庫特性來編寫包裝器的一種非常簡潔的方法:
class InterfaceWrapper {
using UI = std::unique_ptr<Interface>;
std::shared_ptr<UI> _sui {new UI{new Foo}, [](UI*p){ (*p)->WriteIt(); delete p; }};
public:
void MakeBar() { _sui->reset(new Bar); }
};
更新2
在意識到std::function<>
已經是一個可以綁定到任何有狀態仿函數的動態,單函數,可變接口之后,我想到了以下簡化版本:
#include <memory> #include <iostream> #include <vector> struct dynamic_release { template <typename F> dynamic_release(F&& f) : _f(std::forward<F>(f)) { } template <typename F> dynamic_release& operator=(F&& f) { _f = std::forward<F>(f); return *this; } ~dynamic_release() { _f(); } private: std::function<void()> _f; }; void do_foo() { std::cout << "Foo\\n"; } void do_bar() { std::cout << "Bar\\n"; } int main(void) { using InterfaceWrapper = std::shared_ptr<dynamic_release>; using Thing = InterfaceWrapper::element_type; { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(do_foo); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); } // prints "Foo" once { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(do_foo); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); *thing = do_bar; // Prints nothing thing_vector.push_back(thing); } // prints "Bar" once }
要在想要使用無狀態basic_dynamic_release
函數實現更高效率的情況下啟用優化,請添加允許使用不同basic_dynamic_release
函數類型的basic_dynamic_release
模板(例如, void(*)()
):
#include <memory> #include <iostream> namespace detail { template <typename InterfaceCallable> struct basic_dynamic_release { basic_dynamic_release() = default; template <typename F> basic_dynamic_release(F&& f) : _f(std::forward<F>(f)) { } template <typename F> basic_dynamic_release& operator=(F&& f) { _f = std::forward<F>(f); return *this; } ~basic_dynamic_release() { _f(); } private: InterfaceCallable _f; }; } using dynamic_release = detail::basic_dynamic_release<std::function<void()>>; #include <vector> void do_foo() { std::cout << "Foo\\n"; } void do_bar() { std::cout << "Bar\\n"; } int main(void) { using InterfaceWrapper = std::shared_ptr<detail::basic_dynamic_release<void(*)(void)>>; using Thing = InterfaceWrapper::element_type; { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(do_foo); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); } // prints "Foo" once { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(do_foo); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); *thing = do_bar; // Prints nothing thing_vector.push_back(thing); } // prints "Bar" once }
為了允許默認構造的實例具有良好定義的接口實現,添加工廠(這是為了使它非常通用):
#include <memory> #include <iostream> namespace detail { template <typename T> struct default_construction final { T operator()() const { return {}; } }; template <typename InterfaceCallable, typename Factory = default_construction<InterfaceCallable> > struct basic_dynamic_release { basic_dynamic_release() = default; template <typename F> basic_dynamic_release(F&& f) : _f(std::forward<F>(f)) { } template <typename F> basic_dynamic_release& operator=(F&& f) { _f = std::forward<F>(f); return *this; } ~basic_dynamic_release() { _f(); } private: InterfaceCallable _f = Factory()(); }; using dynamic_interface = std::function<void()>; template <typename Factory = default_construction<dynamic_interface> > using dynamic_release = basic_dynamic_release<dynamic_interface, Factory>; } #include <vector> void do_foo() { std::cout << "Foo\\n"; } void do_bar() { std::cout << "Bar\\n"; } struct foo_default { detail::dynamic_interface operator()() const { return do_foo; } }; int main(void) { using InterfaceWrapper = std::shared_ptr<detail::dynamic_release<foo_default> >; using Thing = InterfaceWrapper::element_type; { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); } // prints "Foo" once { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); *thing = &do_bar; // Prints nothing thing_vector.push_back(thing); } // prints "Bar" once }
舊的答案傾向於使用boost::variant
靜態多態性,代價是管理稍微復雜一些,但具有更大的靈活性:
我選擇用靜態多態替換動態多態,這會消除額外的分配,這也會帶來生命周期管理(曾經是
unique_ptr
)。我認為這使得最終解決方案有點簡化,同時更通用(自然提供一些擴展點)。
#include <boost/variant.hpp> #include <memory> #include <iostream> namespace nature { // detail namespace template <typename> struct Nature; template<> struct Nature<struct FooTag> { void do_it() { std::cout << "Foo" << "\\n"; } }; template<> struct Nature<struct BarTag> { void do_it() { std::cout << "Bar" << "\\n"; } }; using FooNature = Nature<FooTag>; using BarNature = Nature<BarTag>; using AnyNature = boost::variant<FooNature, BarNature>; struct Holder { AnyNature held; ~Holder() { DoIt()(held); } private: struct DoIt : boost::static_visitor<> { void operator()(AnyNature& any) const { return boost::apply_visitor(*this, any); } template <typename N> void operator()(N& nature) const { return nature.do_it(); } }; }; } #include <vector> int main(void) { using InterfaceWrapper = std::shared_ptr<nature::Holder>; using Thing = InterfaceWrapper::element_type; { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(); // FooNature is default thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); } // prints "Foo" once { std::vector<InterfaceWrapper> thing_vector; auto thing = std::make_shared<Thing>(); thing_vector.push_back(thing); thing_vector.push_back(thing); thing_vector.push_back(thing); thing->held = nature::BarNature {}; // prints nothing thing_vector.push_back(thing); } // prints "Bar" once }
打印
Foo Bar
借用sehe的精彩洞察力 ,這個版本為您提供了一些與您在C ++中可以做的更接近的東西.2003:
class InterfaceWrapper {
typedef std::unique_ptr<Interface> UI;
std::shared_ptr<UI> p_;
public:
InterfaceWrapper () : p_(std::make_shared<UI>(new Foo)) {}
~InterfaceWrapper () { if (p_.unique()) (*p_)->WriteIt(); }
void MakeBar() { p_->reset(new Bar); }
const UI & operator -> () { return *p_.get(); }
};
我選擇在InterfaceWrapper
的析構函數中添加條件,而不是自定義刪除器。 這允許構造函數使用std::make_shared
。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.