在处理一个项目时,我遇到了一个有趣的问题,当通过其构造函数将一个对象传递给另一个对象时,传入的对象保证比接收者对象长寿(就内存生存期而言)。请记住,我仍在学习 C++11/C++14 的来龙去脉,因此我正在寻找建设性的讨论,以帮助我理解 C++11/的内存管理和生命周期C++14 风格的语义。
本题的设置如下:
class TopLevelClass {
public:
void someMethod (int someValue) {
// Do some work
}
std::unique_ptr<Context> getContext () {
return std::make_unique<Context>(this);
}
};
class Context {
public:
Context (TopLevelClass* tlc) : _tlc(tlc) {}
void call (int value) {
// Perform some work and then call the top level class...
_tlc->someMethod(value);
}
protected:
TopLevelClass* _tlc;
};
尽管此设置的有效替代方法是传递 TopLevelClass
作为 call
的参数Context
的方法类,这在我正在说明的场景中是不可能的:可以访问 Context
的客户端代码对象可能无法访问 TopLevelClass
目的。
虽然上面说明的代码在功能上符合我的需要,但我觉得好像存在代码味道。即,存储 TopLevelClass
的句柄作为原始指针的对象并没有传达 Context
的事实类不负责管理此指针的生命周期(因为在这种情况下,TopLevelClass
保证比任何 Context
对象都长寿)。此外,在使用 C++11 时,我对使用原始指针而不是智能指针犹豫不决(根据 Scott Meyer 在 Effective Modern C++ 中的建议)。
我探索的一种替代方法是将句柄传递给 TopLevelClass
使用共享指针并将此句柄存储在 Context
中类作为共享指针。这需要 TopLevelClass
继承自 std::enabled_shared_from_this
通过以下方式:
class TopLevelClass : public std::enable_shared_from_this<TopLevelClass> {
public:
// Same "someMethod(int)" as before...
std::unique_ptr<Context> getContext () {
return std::make_unique<Context>(shared_from_this());
}
};
class Context {
public:
Context (std::shared_ptr<TopLevelClass> tlc) : _tlc(tlc) {}
// Same "call(int)" as before...
protected:
std::shared_ptr<TopLevelClass> _tlc;
};
这种方法的缺点是,除非 std::shared_ptr
存在 TopLevelClass
先验,然后是 std::bad_weak_ptr
将抛出异常(有关更多信息,请参阅 this post )。因为,就我而言,没有 std::shared_ptr<TopLevelClass>
在代码中创建,我无法使用 std::enable_shared_from_this<T>
方法:我仅限于返回 TopLevelClass
的单个实例使用 static
原始指针,按照我项目的要求,如下
static TopLevelClass* getTopLevelClass () {
return new TopLevelClass();
}
有没有一种方法可以表达 Context
不负责管理其对 TopLevelClass
的句柄例如,自 TopLevelClass
将保证活得比任何 Context
目的?我也乐于接受有关更改设计的建议,这些设计将完全解决问题,只要设计更改不会使上述设计的简单性过于复杂(即,创建许多不同的类以绕过简单地传递一个指向 Context
的构造函数的单个指针。
感谢您的帮助。
最佳答案
以您的方式传递原始指针绝对应该意味着没有所有权被转移。
如果你听到有人说“不要使用原始指针”,你可能错过了这句话的一部分 - 它应该是“不要使用拥有原始指针”,即不应该某个地方有一个原始指针,您需要对其调用 delete。除了可能在一些低级代码中。如果您知道被指向的对象比获取指针的对象存在时间更长,那么只传递指针绝对没有错。
您是说“也就是说,将 TopLevelClass 对象的句柄存储为原始指针并不能传达 Context 类不负责管理该指针的生命周期这一事实”。 相反,存储原始指针的意思恰恰是——“这个对象不管理这个指针指向的对象的生命周期”。在 C++98 风格的代码中,它并不一定意味着。
使用指针的另一种方法是使用引用。但是有一些注意事项,因为您必须在构造函数中初始化它,并且它不能像指针一样设置为 nullptr(这也可能是一件好事)。即:
class TopLevelClass {
public:
void someMethod (int someValue) {
// Do some work
}
std::unique_ptr<Context> getContext () {
return std::make_unique<Context>(*this);
}
};
class Context {
public:
Context(TopLevelClass &tlc) : _tlc(tlc) {}
void call (int value) {
// Perform some work and then call the top level class...
_tlc.someMethod(value);
}
private:
TopLevelClass &_tlc;
};
这里有一些关于这个主题的文章:
C++ 核心指南:
https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Rr-ptr
Herb Sutter 的一些早期文章:
http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/
http://herbsutter.com/2013/05/30/gotw-90-solution-factories/
http://herbsutter.com/2013/06/05/gotw-91-solution-smart-pointer-parameters/
http://herbsutter.com/elements-of-modern-c-style/
可能还有很多来自 CppCon 以及 Cpp 和 Beyond 的视频,但我有点懒得用 google 搜索合适的视频。
关于c++ - 当一个对象保证比其包含的对象长寿时,应该如何存储该对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33575557/