将智能指针用作编程标准
Usage of Smart Pointers as a Programming Standard?
我越来越多地听说,尽管我已经实现了有效的内存泄漏系统,但我应该使用智能指针而不是裸指针。
关于使用智能指针,正确的编程方法是什么?即使我检查了分配的内存块上的内存泄漏,它们真的应该被使用吗?这还取决于我吗?如果我不使用它们,这会被认为是编程的弱点吗?
如果强烈建议使用智能指针(例如:std::auto_ptr),我应该使用它们而不是每个裸指针吗?
您应该使用RAII来处理所有资源分配。
智能指针只是该规则的一个常见特例。
智能指针不仅仅是shared_ptr
。存在具有不同所有权语义的不同智能指针。使用适合您需求的。(主要的是scoped_ptr
、shared_ptr
、weak_ptr
和auto_ptr
/unique_ptr
(在可用的情况下更喜欢后者)。根据编译器的不同,它们可能作为TR1的一部分在标准库中可用,也可能根本不可用,在这种情况下,您可以通过Boost库获得它们。
是的,你绝对应该使用这些。它不会让你付出任何代价(如果做得正确,你的性能将为零),它会给你带来很多好处(内存和其他资源会自动释放,你不必记得手动处理,使用资源的代码会变得更短、更简洁)
请注意,并不是每个指针使用都代表某种资源所有权,因此也不是所有的原始指针使用都是错误的。如果你只需要指向别人拥有的对象,那么一个原始指针是非常合适的。但是,如果您拥有该对象,那么您应该通过赋予类本身RAII语义或将其封装在智能指针中来获得它的适当所有权。
不能盲目地用std::auto_ptr
代替每个原始指针。特别是,auto_ptr
在转让时转让所有权,这对某些目的来说很好,但对其他目的来说绝对不是。
存在多种智能指针(例如,shared_ptr、weak_ptr、auto_ptr/unique_ptr等)是有原因的。每种指针都实现不同的目的。"原始"指针的一个主要弱点是它有很多不同的用途(之所以具有这种多功能性,很大程度上是因为它对任何一个目的几乎没有帮助)。聪明的指针往往更专业,这意味着他们可以更聪明地做好一件事,但也意味着你必须为这份工作选择合适的指针,否则它最终会把错误的事情完全搞砸。
智能指针允许自动定义它所引用对象的生命周期。这是需要理解的主要内容。
因此,不,您不应该在任何地方都使用智能指针,只有当您希望自动化对象的生命周期,而不是让一个对象从出生到死亡管理这些对象时。它就像任何工具一样:它解决特定类型的问题,而不是所有的问题。
对于每个对象,你应该考虑它将经历的生命周期,然后选择一个最简单、正确、有效的解决方案。有时它会被shared_ptr,因为您希望该对象由多个组件使用,并在不再使用时自动销毁。有时您只需要当前作用域/父对象中的对象,所以scoped_ptr可能更合适。有时您只需要实例的一个所有者,因此unique_ptr是合适的。也许你会发现这样的情况,你知道一种算法可以定义/自动化对象的生命周期,所以你会为它编写自己的智能指针。
举个相反的例子,使用池禁止您使用smart_ptr。在这种特殊情况下(但在嵌入式软件中很常见),裸指针可能是一种更受欢迎的简单高效的解决方案。
有关更多解释,请参阅(我的)回答:https://softwareengineering.stackexchange.com/questions/57581/in-c-is-it-a-reflection-of-poor-software-design-if-objects-are-deleted-manuall/57611#57611
即使我检查了分配的内存块上的内存泄漏,它们真的应该被使用吗
是
智能指针的全部目的是,它帮助您实现RAII(SBRM),这基本上让资源本身承担其解除分配的责任,并且资源不必明确地依赖您记住来解除分配。
如果我不使用它们,这会被认为是编程的弱点吗
NO,
如果您不使用智能指针(RAII),那么自己明确管理资源并不是一个弱点,而是一种不便或不必要的麻烦。实现RAII的智能指针的目的是提供高效、轻松的资源处理方式,如果你不使用它,你就不会使用它。强烈建议使用它纯粹是因为它提供了许多优势。
如果强烈建议使用智能指针 (例如:std::auto_ptr),我应该使用它们而不是每个裸指针吗
是
你应该尽可能地使用智能指针,因为使用它们没有缺点,只是有很多优点
不要使用auto_ptr
,因为它已经被弃用了!!根据需要,您可以使用各种其他智能指针。你可以参考上面的链接来了解更多关于它们的信息。
这是一个棘手的问题,而且目前有一种模式到处使用智能指针并不会让事情变得更容易。聪明的指针在某些情况下会有所帮助,但你肯定不能在任何地方使用它们,不用思考。有很多不同的类型聪明的指针,你必须考虑哪一个是合适的在任何情况下;即便如此,你的大多数指针(至少在典型的我工作过的领域中的应用程序)应该是原始指针。
无论采用何种方法,都有几点值得一提:
-
除非迫不得已,否则不要使用动态分配应用程序,仅需要动态分配的内容是具有特定生存期的对象,由应用程序决定思维方式不要对具有值语义的对象使用动态分配。
-
关于实体对象应用程序域:应该根据到程序逻辑。不管是否有指向不管他们是不是。如果它们的破坏造成了问题,那么你就有了程序逻辑中的某个错误(未正确处理事件,等等),并且使用智能指针不会改变任何事情。
实体对象的一个典型示例可能是服务器,在客户端连接时创建,在客户端断开连接。在许多此类情况下,最适当的管理将是delete this
,因为它是将接收的连接断开连接事件。(包含指向此类对象的指针的对象必须向其注册,以便了解其摧毁但这样的指针纯粹是为了导航,不应该是聪明的指针。)
当人们试图使用智能指针时,你通常会发现什么所有都是内存泄漏;典型的参考计数器不会处理循环,当然,典型的应用程序充满了循环:Connection
将指向与其连接的Client
,并且CCD_ 13将包含其所连接的CCD_。如果智能指针是boost::shared_ptr
,也有一个确定的悬挂指针的风险:创建两个指针要容易得多boost::shared_ptr
到同一地址(这导致两个计数器参考文献)。
如果强烈建议使用智能指针(例如:std::auto_ptr),我应该使用它们而不是每个裸指针吗?
在我看来,是的,你应该为你拥有的每个指针使用它。
以下是我对C++中的资源管理的看法(不同意我的看法):
- 良好的资源管理需要从所有权的角度进行思考
- 资源应该由对象管理(RAII)
- 通常,单一所有权优先于共享所有权
- 理想情况下,创建者也是对象的所有者。(但是,在某些情况下,所有权转移是有序的。)
这导致了以下做法:
-
将
boost::scoped_ptr
设为本地变量和成员变量的默认选项。请记住,对成员变量使用scoped_ptr
会使类不可复制。如果你不想这样,请看下一点。 -
将
boost::shared_ptr
用于容器或启用共享所有权:// Container of MyClass* pointers:
typedef boost::shared_ptr<MyClass> MyClassPtr;
std::vector<MyClassPtr> vec;
-
std::auto_ptr
(C++03)可用于所有权转移。例如,作为工厂或克隆方法的返回值:// Factory method returns auto_ptr
std::auto_ptr<Button> button = Button::Create(...);
// Clone method returns auto_ptr
std::auto_ptr<MyClass> copy = obj->clone();
// Use release() to transfer the ownership to a scoped_ptr or shared_ptr
boost::scoped_ptr<MyClass> copy(obj->clone().release());
-
如果你需要存储一个你不拥有的指针,那么你可以使用一个原始指针:
this->parent = inParentObject;
-
在某些情况下,需要
boost::weak_pointer
。有关详细信息,请参阅文档。
通常,您应该更喜欢智能指针,但也有几个例外。
如果您需要重铸指针,例如提供const
版本,那么使用智能指针几乎是不可能的。
智能指针用于控制对象的生存期。通常,当您传递指向函数的指针时,该函数不会影响生存期;该函数不会尝试删除对象,也不会存储指针的副本。在函数返回之前,调用代码无法删除对象。在这种情况下,哑指针是完全可以接受的。
是。假设您有可用的C++0x,请使用unique_ptr
或shared_ptr
(视情况而定)来包装new
的所有原始指针。在make_shared
的帮助下,shared_ptr
具有很高的性能。如果你不需要引用计数,那么unique_ptr
会让你获得更好的性能。它们在集合和其他auto_ptr
是哑指针的情况下都表现得很好。
在任何地方使用智能指针(shared_ptr或其他)都是个坏主意。使用shared_ptr来管理对象/资源的生存期是好的,但将它们作为参数传递给函数等不是一个好主意。这增加了循环引用和其他极难跟踪的错误的可能性(个人经验:如果每个函数调用都改变了引用计数,那么试着在200万行代码中找出谁不应该占用资源——你最终会认为做这种事情的人是m***ns)。最好传递一个原始指针或引用。当与惰性实例化相结合时,情况会更糟。我建议开发人员应该知道他们编写的对象的生命周期,并使用shared_ptr来控制它(RAII),但不要将shared_ptr的使用扩展到它之外。
- 使用CMake检测支持的C++标准
- 如何理解C++标准N3337中的expr.const.cast子句8
- "throw expression code" 1e7 >返回 d 是什么?投掷标准::overflow_error( "too big" ) : d;意味 着?
- 有一个打印语句的函数是一种糟糕的编程实践吗
- 编译标准库类型
- 标准是否使用多余的大括号(例如 T{{{10}}})定义列表初始化?
- 我是C++编程的新手,这些代码之间有什么区别,我应该使用哪一个
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 模板元编程:如何将参数包组合成新的参数包
- 铸造标准::有没有回到原来的类型
- Qt Q串行端口未编程设备未关闭
- 标准 N3337 5.2.10 第 7 条中的C++"类型"是什么意思?
- this_thread::sleep_for和计时时钟之间的关系是否由C++11标准指定
- 谁以编程语言(例如C )制定标准
- 我应该如何使用新的C++11标准来编程C++
- 大型嵌入式公司是否"forced"使用旧的编程标准/编译器?
- 这种模板编程技术的名称是什么,这是标准的c++
- 将智能指针用作编程标准
- 编程 Arduino 与标准 C 有何不同
- 以编程方式从父进程的子进程捕获打印,因此它们不会转到标准输出