智能指针+“this”被认为有害?
-
23-08-2019 - |
题
在使用智能指针的C++项目中,例如 boost::shared_ptr
, ,关于使用“的良好设计理念是什么?this
"?
考虑一下:
存储任何智能指针中包含的原始指针以供以后使用是危险的。您已经放弃了对对象删除的控制,并相信智能指针会在正确的时间执行此操作。
非静态类成员本质上使用
this
指针。它是一个原始指针,无法更改。
如果我曾经存储过 this
在另一个变量中或将其传递给另一个函数,该函数可能会在以后存储它或将其绑定在回调中,我正在创建当任何人决定创建指向我的类的共享指针时引入的错误。
鉴于此,我什么时候适合明确使用 this
指针? 是否有设计范例可以防止与此相关的错误?
解决方案
虽然我没有一般答案或一些习语,但有 boost::enable_shared_from_this
。它允许您获取一个管理已由shared_ptr管理的对象的shared_ptr。由于在成员函数中您没有对那些管理shared_ptr的引用,enable_shared_ptr确实允许您获取shared_ptr实例并在需要传递this指针时传递它。
但这并不能解决通过的问题 this
从构造函数内部,因为那时还没有共享指针正在管理您的对象。
其他提示
错误的问题
在使用智能指针的 C++ 项目中
这个问题实际上与智能指针无关。这仅与所有权有关。
智能指针只是工具
他们没有改变所有权的概念,尤其是。 需要在您的程序中拥有明确的所有权, ,所有权可以自愿转让,但不能由客户取得。
您必须了解智能指针(也包括锁和其他 RAII 对象)同时代表一个值和一个与该值相关的关系。A shared_ptr
是对对象的引用并建立关系:在此之前该对象不得被销毁 shared_ptr
, ,当这个 shared_ptr
被销毁时,如果是该对象的最后一个别名,则必须立即销毁该对象。(unique_ptr
可以看作是一个特例 shared_ptr
根据定义,其中混叠为零,因此 unique_ptr
始终是对象的最后一个别名。)
为什么应该使用智能指针
建议使用智能指针,因为它们仅用变量和函数声明来表达很多内容。
智能指针只能表达明确定义的设计,它们并不能消除定义所有权的需要。相比之下,垃圾收集无需定义谁负责内存释放。(但不要取消定义谁负责其他资源清理的需要。)
即使在非纯函数式垃圾收集语言中,您也需要明确所有权:如果其他组件仍然需要旧值,您不想覆盖对象的值。在 Java 中尤其如此,可变数据结构的所有权概念在线程程序中极其重要。
原始指针怎么样?
使用原始指针并不意味着没有所有权。它只是没有通过变量声明来描述。它可以在注释、设计文档等中进行描述。
这就是为什么许多 C++ 程序员认为使用原始指针而不是足够的智能指针是 下:因为它的表达能力较差(我故意避免使用“好”和“坏”这些术语)。我相信 Linux 内核通过一些 C++ 对象来表达关系会更具可读性。
您可以使用或不使用智能指针来实现特定的设计。适当使用智能指针的实现将被许多 C++ 程序员认为是优越的。
你真正的问题
在 C++ 项目中,关于使用“this”的良好设计理念是什么?
这太模糊了。
存储原始指针以供以后使用是危险的。
为什么需要一个指针供以后使用?
您已经放弃了对对象删除的控制,并相信负责的组件会在正确的时间执行此操作。
事实上,某些组件负责变量的生命周期。您不能承担以下责任:它必须被转移。
如果我将其存储在另一个变量中或将其传递给另一个函数,该函数可能会在以后存储它或将其绑定在回调中,那么我就会创建当任何人决定使用我的类时引入的错误。
显然,由于调用者没有被告知该函数将隐藏指针并在以后不受调用者控制的情况下使用它,因此您正在创建错误。
解决方案显然是:
- 将处理对象生命周期的责任转移给函数
- 确保指针仅在调用者的控制下保存和使用
仅在第一种情况下,您可能最终会在类实现中得到智能指针。
你的问题的根源
我认为你的问题是你正在努力使用智能指针使事情复杂化。智能指针是让事情变得更容易而不是更困难的工具。如果智能指针使您的规范变得复杂,那么请从更简单的角度重新考虑您的规范。
在出现问题之前,不要尝试引入智能指针作为解决方案。
仅引入智能指针来解决特定的明确定义的问题。因为你没有描述一个具体的明确的问题, 无法讨论具体的解决方案 (是否涉及智能指针)。
正确使用的一个例子是 return *this;
在像operator++()和operator<<()这样的函数中。
当您使用智能指针类时,您是对的,直接暴露是危险的”this
”。有一些相关的指针类 boost::shared_ptr<T>
可能有用:
boost::enable_shared_from_this<T>
- 提供让对象返回指向自身的共享指针的能力,该指针使用与指向该对象的现有共享指针相同的引用计数数据
boost::weak_ptr<T>
- 与共享指针一起工作,但不保存对该对象的引用。如果所有共享指针消失并且对象被释放,弱指针将能够告诉您该对象不再存在并将返回您
NULL
而不是指向无效内存的指针。您可以使用弱指针来获取指向有效引用计数对象的共享指针。
- 与共享指针一起工作,但不保存对该对象的引用。如果所有共享指针消失并且对象被释放,弱指针将能够告诉您该对象不再存在并将返回您
当然,这些都不是万无一失的,但它们至少会使您的代码更加稳定和安全,同时为您的对象提供适当的访问和引用计数。
如果您需要使用 this
, ,只需明确使用它即可。智能指针仅包装它们拥有的对象的指针 - 要么独占(unique_ptr
)或以共享方式(shared_ptr
).
我个人喜欢使用 这 访问类的成员变量时的指针。例如:
void foo::bar ()
{
this->some_var += 7;
}
这只是一个无害的风格问题。有些人喜欢,有些人不喜欢。
但使用 这 任何其他东西的指针都可能导致问题。如果你真的需要用它做一些奇特的事情,你真的应该重新考虑你的设计。我曾经看到一些代码,在类的构造函数中,它将 this 指针分配给存储在其他地方的另一个指针!这太疯狂了,我想不出这样做的理由。顺便说一下,整个代码是一团糟。
您能告诉我们您到底想用指针做什么吗?
另一种选择是使用侵入式智能指针,并处理对象本身而不是指针内的引用计数。这需要更多的工作,但实际上更高效且易于控制。
绕过这个的另一个原因是如果您想保留所有对象的中央注册表。在构造函数中,对象用 this 调用注册表的静态方法。它对于各种发布/订阅机制很有用,或者当您不希望注册表需要了解系统中的对象/类时。