11

在使用智能指针的 C++ 项目中,例如boost::shared_ptr,关于使用“ this”的良好设计理念是什么?

考虑一下:

  • 存储包含在任何智能指针中以供以后使用的原始指针是很危险的。您已经放弃了对对象删除的控制,并相信智能指针会在正确的时间执行此操作。

  • 非静态类成员本质上使用this指针。这是一个原始指针,无法更改。

如果我曾经存储this在另一个变量中或将其传递给另一个函数,该函数可能会存储它以供以后使用,或者将其绑定到回调中,那么当任何人决定创建指向我的类的共享指针时,我就会创建错误。

鉴于此,我什么时候适合显式使用this指针? 是否有可以防止与此相关的错误的设计范例?

4

8 回答 8

15

错误的问题

在使用智能指针的 C++ 项目中

这个问题实际上与智能指针无关。这只是关于所有权。

智能指针只是工具

他们没有改变所有权的概念,尤其是。需要在您的程序中明确定义所有权,所有权可以自愿转让,但不能由客户获取。

您必须了解智能指针(也包括锁和其他 RAII 对象)同时表示一个值和 WRT 此值的关系。Ashared_ptr是一个对象的引用,建立关系:对象不能在 this 之前被销毁shared_ptr,当 thisshared_ptr被销毁时,如果是最后一个给这个对象起别名,则必须立即销毁该对象。(unique_ptr可以被视为一种特殊情况,shared_ptr根据定义,别名为零,因此unique_ptr始终是对象的最后一个别名。)

为什么应该使用智能指针

建议使用智能指针,因为它们仅使用变量和函数声明来表达很多内容。

智能指针只能表达定义良好的设计,它们不会消除定义所有权的需要。相反,垃圾收集消除了定义谁负责内存释放的需要。(但不要取消定义谁负责其他资源清理的需要。)

即使在非纯函数式垃圾收集语言中,您也需要明确所有权:如果其他组件仍需要旧值,您不希望覆盖对象的值。在 Java 中尤其如此,其中可变数据结构的所有权概念在线程程序中极为重要。

原始指针呢?

使用原始指针并不意味着没有所有权。它只是没有通过变量声明来描述。它可以在注释、您的设计文档等中进行描述。

这就是为什么许多 C++ 程序员认为使用原始指针而不是适当的智能指针是劣等的:因为它的表达能力较差(我故意避免使用“好”和“坏”这两个术语)。我相信使用一些 C++ 对象来表达关系,Linux 内核会更具可读性。

您可以使用或不使用智能指针来实现特定设计。适当使用智能指针的实现将被许多 C++ 程序员认为是优越的。

你真正的问题

在 C++ 项目中,关于使用“this”的良好设计理念是什么?

这是非常模糊的。

存储原始指针以供以后使用是很危险的。

为什么你需要一个指针供以后使用?

您已经放弃了对对象删除的控制,并相信负责的组件会在正确的时间执行此操作。

实际上,某些组件负责变量的生命周期。你不能承担责任:它必须被转移。

如果我曾经将它存储在另一个变量中或将其传递给另一个可能存储它以供以后使用的函数或将其绑定到回调中,我正在创建当任何人决定使用我的类时引入的错误。

显然,由于调用者没有被告知该函数将隐藏一个指针并在以后不受调用者控制的情况下使用它,因此您正在创建错误。

解决方案显然是:

  • 将处理对象生命周期的责任转移给函数
  • 确保指针仅在调用者的控制下保存和使用

只有在第一种情况下,您最终可能会在类实现中使用智能指针。

你问题的根源

我认为您的问题是您正在努力使用智能指针使事情复杂化。智能指针是使事情变得更容易而不是更难的工具。如果智能指针使您的规范复杂化,那么请从更简单的角度重新考虑您的规范。

在遇到问题之前,不要尝试引入智能指针作为解决方案。

只引入智能指针来解决特定的定义明确的问题。因为你没有描述一个明确定义的具体问题,所以不可能讨论一个具体的解决方案(涉及或不涉及智能指针)。

于 2011-10-26T03:13:40.407 回答
13

虽然我没有一般的答案或一些成语,但有boost::enable_shared_from_this. 它允许您获得一个管理已由 shared_ptr 管理的对象的 shared_ptr。由于在成员函数中您没有引用管理 shared_ptr 的那些,因此 enable_shared_ptr 确实允许您获取 shared_ptr 实例并在需要传递 this 指针时传递它。

但这并不能解决this从构造函数中传递的问题,因为那时还没有 shared_ptr 正在管理您的对象。

于 2008-12-19T20:55:25.763 回答
5

一个正确使用的例子是return *this;在像 operator++() 和 operator<<() 这样的函数中。

于 2008-12-19T21:22:40.153 回答
5

当您使用智能指针类时,您是对的,直接暴露“ this”是危险的。有一些与之相关的指针类boost::shared_ptr<T>可能有用:

  • boost::enable_shared_from_this<T>
    • 提供让对象返回指向自身的共享指针的能力,该共享指针使用与指向对象的现有共享指针相同的引用计数数据
  • boost::weak_ptr<T>
    • 与共享指针一起工作,但不持有对对象的引用。如果所有共享指针都消失并且对象被释放,弱指针将能够告诉您该对象不再存在,并将返回您NULL而不是指向无效内存的指针。您可以使用弱指针来获取指向有效引用计数对象的共享指针。

当然,这些都不是万无一失的,但它们至少可以使您的代码更加稳定和安全,同时为您的对象提供适当的访问和引用计数。

于 2008-12-19T22:20:26.263 回答
1

如果您需要使用this,只需明确使用它。智能指针仅包装它们所拥有的对象的指针 - 或者以独占方式 ( unique_ptr) 或以共享方式 ( shared_ptr)。

于 2008-12-19T20:56:39.503 回答
1

我个人喜欢在访问类的成员变量时使用this指针。例如:

void foo::bar ()
{
    this->some_var += 7;
}

这只是一个无害的风格问题。有些人喜欢,有些人不喜欢。

但是将this指针用于其他任何事情都可能会导致问题。如果你真的需要用它做一些花哨的事情,你真的应该重新考虑你的设计。我曾经看到一些代码,在类的构造函数中,它将 this 指针分配给存储在其他地方的另一个指针!这太疯狂了,我想不出这样做的理由。顺便说一句,整个代码是一团糟。

你能告诉我们你到底想用指针做什么吗?

于 2008-12-19T21:05:16.977 回答
1

另一种选择是使用侵入式智能指针,并在对象本身而不是指针中处理引用计数。这需要更多的工作,但实际上更有效且易于控制。

于 2009-01-19T09:27:37.257 回答
0

绕过这个的另一个原因是,如果您想保留所有对象的中央注册表。在构造函数中,一个对象用 this 调用注册中心的一个静态方法。它对各种发布/订阅机制很有用,或者当您不希望注册表需要了解系统中的对象/类时。

于 2008-12-19T22:07:43.013 回答