我正在寻找一个懒惰创建的可共享指针。我有一个假设的大班Thing。事情是巨大的,因此制造成本很高,但是虽然它们在代码中的任何地方使用(共享,经过大量传递,修改,存储以供以后使用等),但它们实际上通常不会被最终使用,因此会延迟实际创建直到实际访问它们是可取的。因此,需要懒惰地创造,并且需要可分享。让我们调用这个封装指针包装器SharedThing。
class SharedThing {
...
Thing* m_pThing;
Thing* operator ->() {
// ensure m_pThing is created
...
// then
return m_pThing
);
}
...
SharedThing pThing;
...
// Myriads of obscure paths taking the pThing to all dark corners
// of the program, some paths not even touching it
...
if (condition) {
pThing->doIt(); // last usage here
}
到目前为止,我们已经提出了四种选择:
typedef std::shared_ptr<Thing> SharedThing;
SharedThing newThing() {
return make_shared<Thing>();
}
...
// SharedThing pThing; // pThing points to nullptr, though...
SharedThing pThing(new Thing()); // much better
SharedThing pThing = newThing(); // alternative
第1点和第6点缺乏得分是一个杀手;没有选择1。
class SharedThing: public shared_ptr<Thing> {};
并覆盖特定成员,以确保在取消引用shared_ptr时,它会及时创建Thing。
此选项优于1并且可能没问题,但看起来很混乱和/或黑客......
class SharedThing {
std::shared_ptr<Thing> m_pThing;
void EnsureThingPresent() {
if (m_pThing == nullptr) m_pThing = std::make_shared<Thing>();
}
public:
SharedThing(): m_pThing(nullptr) {};
Thing* operator ->() {
EnsureThingCreated();
return m_pThing.get();
}
}
为operator *和const版本添加额外的包装方法。
这个在4上惨遭失败,所以这个也就失败了。
class SharedThing {
typedef unique_ptr<Thing> UniqueThing;
shared_ptr<UniqueThing> m_pThing;
}
并添加3.1中的所有其他方法
除了建议的性能之外,这似乎没问题(但需要测试)。
class LazyCreatedThing {
Thing* m_pThing;
}
typedef shared_ptr<LazyCreatedThing> SharedThing;
SharedThing makeThing() {
return make_shared<LazyCreatedThing>();
}
并添加各种运算符 - &gt;重载使LazyCreatedThing看起来像Thing *
在这里惨败5使这成为禁忌。
迄今为止最好的选择似乎是3.2;让我们看看我们还能想出什么! :)
答案 0 :(得分:3)
我会实现LazyThing
包装器。我想调整Thing
界面而不是std::shared_ptr
界面要容易得多。
class Thing
{
public:
void Do()
{
std::cout << "THING" << std::endl;
}
};
class LazyThing
{
public:
void Do()
{
getThing().Do();
}
private:
Thing& getThing()
{
if (!thing_)
thing_ = std::make_unique<Thing>();
return *thing_;
}
std::unique_ptr<Thing> thing_;
};
现在,您可以将它与任何智能指针一起使用,甚至可以在堆栈上创建它:
LazyThing lazy;
auto sharedLazy = std::make_shared<LazyThing>();
auto uniqueLazy = std::make_unique<LazyThing>();
或者如你的例子所示:
typedef std::shared_ptr<LazyThing> SharedLazyThing;
SharedLazyThing newThing() {
return std::make_shared<LazyThing>();
}
...
auto pThing = newThing();
<强>更新强>
如果您想保证共享语义并且不打扰调用newThing()
或任何其他工厂方法,请放弃shared_ptr
接口。那里不需要它。
将SharedLazyThing
实现为具有共享语义的值类型。棘手的是你需要添加另一个间接层来提供共享Thing
对象的延迟构造。
class SharedLazyThing
{
using UniqueThing = std::unique_ptr<Thing>;
public:
void Do()
{
getThing().Do();
}
private:
Thing& getThing()
{
if (!*thing_)
*thing_ = std::make_unique<Thing>();
return **thing_;
}
std::shared_ptr<UniqueThing> thing_ = std::make_shared<UniqueThing>();
};
现在,您可以随处使用SharedLazyThing
。
SharedLazyThing thing1;
SharedLazyThing thing2(thing1);
SharedLazyThing thing3 = thing1;
答案 1 :(得分:0)
也许我误解了这个问题但不能像这样简单吗?
class Factory
{
private:
std::shared_ptr<My1stType> my1st_ {};
std::shared_ptr<My2ndType> my2nd_ {};
std::shared_ptr<My3rdType> my3rd_ {};
// …
public:
std::shared_ptr<My1stType>
get1st()
{
if (!this->my1st_)
this->my1st_ = std::make_shared<My1stType>(/* … */);
return this->my1st_;
}
// …
};
如上所示,这不是线程安全的,但是,如果这对您很重要。