C++ 总是使用智能指针是一个好习惯吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2454214/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:32:55  来源:igfitidea点击:

Is it a good practice to always use smart pointers?

c++

提问by Dony Borris

I find smart pointers to be a lot more comfortable than raw pointers. So is it a good idea to alwaysuse smart pointers? ( Please note that I am from Java background and hence don't much like the idea of explicit memory management. So unless there are some serious performance issues with smart pointers, I'd like to stick with them. )

我发现智能指针比原始指针舒服得多。那么总是使用智能指针是个好主意吗?(请注意,我来自 Java 背景,因此不太喜欢显式内存管理的想法。所以除非智能指针存在一些严重的性能问题,否则我想坚持使用它们。)

Note: Though I come from Java background, I understand the implementation of smart pointers and the concepts of RAII quite well. So you can take this knowledge for granted from my side when posting an answer. I use static allocation almost everywhere and use pointers only when necessary. My question is merely: Can I always use smart pointers in place of raw pointers???

注意:虽然我有Java背景,但我对智能指针的实现和RAII的概念非常了解。因此,您可以在发布答案时将这些知识视为理所当然。我几乎在所有地方都使用静态分配,并且仅在必要时使用指针。我的问题仅仅是:我可以总是使用智能指针代替原始指针吗???

采纳答案by Matthieu M.

Given the several edits, I have the impression that a comprehensive summary would be useful.

鉴于多次编辑,我的印象是综合总结会很有用。

1. When not to

1. 什么时候不

There are two situations where you should not use smart pointers.

有两种情况不应该使用智能指针。

The first is the exact same situation in which you should not use a C++class in fact. IE: DLL boundary if you do not offer the source code to the client. Let say anecdotal.

第一种情况与您C++实际上不应使用类的情况完全相同。IE:DLL 边界,如果您不向客户端提供源代码。说个轶事吧。

The second happens much more often: smart manager means ownership. You may use pointers to point at existing resources without managing their lifetime, for example:

第二种情况更常见:聪明的经理意味着所有权。您可以使用指针指向现有资源而无需管理其生命周期,例如:

void notowner(const std::string& name)
{
  Class* pointer(0);
  if (name == "cat")
    pointer = getCat();
  else if (name == "dog")
    pointer = getDog();

  if (pointer) doSomething(*pointer);
}

This example is constrained. But a pointer is semantically different from a reference in that it may point to an invalid location (the null pointer). In this case, it's perfectly fine not to use a smart pointer in its stead, because you don't want to manage the lifetime of the object.

这个例子是有约束的。但是指针在语义上与引用不同,因为它可能指向无效位置(空指针)。在这种情况下,最好不要使用智能指针代替它,因为您不想管理对象的生命周期。

2. Smart managers

2. 聪明的管理者

Unless you are writing a smart manager class, if you use the keyworddeleteyou are doing something wrong.

除非您正在编写智能管理器类,否则如果您使用关键字,delete您就做错了。

It is a controversial point of view, but after having reviewed so many example of flawed code, I don't take chances any longer. So, if you write newyou need a smart manager for the newly allocated memory. And you need it right now.

这是一个有争议的观点,但在了这么多有缺陷的代码示例之后,我不再冒险了。因此,如果您编写代码,则new需要一个智能管理器来管理新分配的内存。而你现在就需要它。

It does not mean you are less of a programmer! On the contrary, reusing code that has been proved to work instead of reinventing the wheel over and over is a key skill.

这并不意味着你不是一个程序员!相反,重用已被证明有效的代码而不是一遍又一遍地重新发明轮子是一项关键技能。

Now, the real difficulty start: which smart manager ?

现在,真正的困难开始了:哪个聪明的经理?

3. Smart pointers

3. 智能指针

There are various smart pointers out of there, with various characteristics.

那里有各种智能指针,具有各种特性。

Skipping std::auto_ptrwhich you should generally avoid (its copy semantic is screwed).

跳过std::auto_ptr你通常应该避免的(它的复制语义被搞砸了)。

  • scoped_ptr: no overhead, cannot be copied or moved.
  • unique_ptr: no overhead, cannot be copied, can be moved.
  • shared_ptr/ weak_ptr: some overhead (reference counting), can be copied.
  • scoped_ptr: 没有开销,无法复制或移动。
  • unique_ptr: 没有开销,不能复制,可以移动。
  • shared_ptr/ weak_ptr: 一些开销(引用计数),可以复制。

Usually, try to use either scoped_ptror unique_ptr. If you need several owners try to change the design. If you can't change the design and really need several owners, use a shared_ptr, but beware of references cycles that ought to be broken using a weak_ptrsomewhere in the midst.

通常,尝试使用scoped_ptrunique_ptr。如果您需要几个所有者尝试更改设计。如果您无法更改设计并且确实需要多个所有者,请使用shared_ptr,但要注意应该使用weak_ptr中间某个位置来打破的引用循环。

4. Smart containers

4. 智能容器

Many smart pointers are not meant to be copied, therefore their use with the STL containers are somewhat compromised.

许多智能指针并不意味着被复制,因此它们与 STL 容器的使用有些妥协。

Instead of resorting to shared_ptrand its overhead, use smart containers from the Boost Pointer Container. They emulate the interface of classic STL containers but store pointers they own.

不要求助于shared_ptr它的开销,而是使用来自Boost Pointer Container 的智能容器。它们模拟经典 STL 容器的接口,但存储它们拥有的指针。

5. Rolling your own

5. 滚动你自己的

There are situations when you may wish to roll your own smart manager. Do check that you did not just missed some feature in the libraries your are using beforehand.

在某些情况下,您可能希望推出自己的智能经理。请务必事先检查您是否只是错过了正在使用的库中的某些功能。

Writing a smart manager in the presence of exceptions is quite difficult. You usually cannot assume that memory is available (newmay fail) or that Copy Constructors have the no throwguarantee.

在出现异常的情况下编写一个聪明的管理器是相当困难的。您通常不能假设内存可用(new可能会失败)或Copy Constructorno throw保证。

It may be acceptable, somewhat, to ignore the std::bad_allocexception and impose that Copy Constructors of a number of helpers do not fail... after all, that's what boost::shared_ptrdoes for its deleter Dtemplate parameter.

在某种程度上,忽略std::bad_alloc异常并强加Copy Constructor许多助手的 s 不会失败可能是可以接受的......毕竟,这就是boost::shared_ptr它的删除器D模板参数所做的。

But I would not recommend it, especially for a beginner. It's a tricky issue, and you're not likely to notice the bugs right now.

但我不会推荐它,尤其是对于初学者。这是一个棘手的问题,您现在不太可能注意到这些错误。

6. Examples

6. 例子

// For the sake of short code, avoid in real code ;)
using namespace boost;

// Example classes
//   Yes, clone returns a raw pointer...
// it puts the burden on the caller as for how to wrap it
//   It is to obey the `Cloneable` concept as described in 
// the Boost Pointer Container library linked above
struct Cloneable
{
  virtual ~Cloneable() {}
  virtual Cloneable* clone() const = 0;
};

struct Derived: Cloneable
{
  virtual Derived* clone() const { new Derived(*this); }
};

void scoped()
{
  scoped_ptr<Cloneable> c(new Derived);
} // memory freed here

// illustration of the moved semantics
unique_ptr<Cloneable> unique()
{
  return unique_ptr<Cloneable>(new Derived);
}

void shared()
{
  shared_ptr<Cloneable> n1(new Derived);
  weak_ptr<Cloneable> w = n1;

  {
    shared_ptr<Cloneable> n2 = n1;          // copy

    n1.reset();

    assert(n1.get() == 0);
    assert(n2.get() != 0);
    assert(!w.expired() && w.get() != 0);
  } // n2 goes out of scope, the memory is released

  assert(w.expired()); // no object any longer
}

void container()
{
  ptr_vector<Cloneable> vec;
  vec.push_back(new Derived);
  vec.push_back(new Derived);

  vec.push_back(
    vec.front().clone()         // Interesting semantic, it is dereferenced!
  );
} // when vec goes out of scope, it clears up everything ;)

回答by Matthieu M.

Smart pointers doperform explicit memory management, and if you don't understand how they are doing it, you are in for a world of trouble when programming with C++. And remember that memory isn't the only resource that they manage.

智能指针确实执行显式内存管理,如果您不了解它们是如何做到的,那么在使用 C++ 编程时就会陷入困境。请记住,内存并不是他们管理的唯一资源。

But to answer your question you should prefer smart-pointers as a first approximation to a solution, but possibly be prepared to ditch them when necessary. You should never use pointers (or any sort) or dynamic allocation when it can be avoided. For example:

但是要回答您的问题,您应该更喜欢智能指针作为解决方案的第一个近似值,但可能准备在必要时放弃它们。在可以避免的情况下,您永远不应该使用指针(或任何类型)或动态分配。例如:

string * s1 = new string( "foo" );      // bad
string s2( "bar" );    // good

Edit:To answer your suplementary question "Can I always use smart pointers in place of raw pointers??? Then, no you can't. If (for example) you need to implement your own version of operator new, you would have to make it return a pointer, not a smart pointer.

编辑:要回答您的补充问题“我可以始终使用智能指针代替原始指针吗???那么,不,您不能。如果(例如)您需要实现自己的 operator new 版本,则必须让它返回一个指针,而不是一个智能指针。

回答by sth

Usually you shouldn't use pointers (smart or otherwise) if you don't need them. Better make local variables, class members, vector elements and similar items normal objects instead of pointers to objects. (Since you come from Java you're probably tempted allocate everything with new, which is not recommended.)

如果您不需要它们,通常您不应该使用指针(智能或其他方式)。更好地使局部变量、类成员、向量元素和类似项成为普通对象,而不是指向对象的指针。(由于您来自 Java,您可能很想用 分配所有内容new,但不推荐这样做。)

This approach ("RAII") saves you from worrying about pointers most of the time.

这种方法(“ RAII”)使您无需在大多数时间担心指针。

When you have to use pointers it depends on the situation and why exactly you need pointers, but usually smart pointers can be used. It might not be always(in bold) be the best option, but this depends on the specific situation.

当您必须使用指针时,这取决于具体情况以及您需要指针的确切原因,但通常可以使用智能指针。它可能并不总是(以粗体显示)是最佳选择,但这取决于具体情况。

回答by Steve Jessop

A good time notto use smart pointers, is at the interface boundary of a DLL. You don't know whether other executables will be built with the same compiler/libraries. Your system's DLL calling convention won't specify what standard or TR1 classes look like, smart pointers included.

使用智能指针的好时机是在 DLL 的接口边界处。您不知道是否会使用相同的编译器/库构建其他可执行文件。您系统的 DLL 调用约定不会指定标准或 TR1 类的外观,包括智能指针。

Within an executable or library, if you want to represent ownership of the pointee, then smart pointers are on average the best way to do it. So it's fine to want to always use them in preference to raw. Whether you actually can always use them is another matter.

在可执行文件或库中,如果您想表示指针对象的所有权,那么智能指针通常是最好的方法。因此,希望始终优先使用它们而不是原始数据是可以的。你是否真的可以一直使用它们是另一回事。

For a concrete example when not to - suppose you are writing a representation of a generic graph, with vertices represented by objects and edges represented by pointers between the objects. The usual smart pointers will not help you: graphs can be cyclic, and no particular node can be held responsible for the memory management of other nodes, so shared and weak pointers are insufficient. You might for example put everything in a vector and use indices instead of pointers, or put everything in a deque and use raw pointers. You could use shared_ptrif you wanted, but it won't add anything except overhead. Or you could look for mark-sweep GC.

对于什么时候不这样做的具体示例 - 假设您正在编写通用图的表示,其中顶点由对象表示,边由对象之间的指针表示。通常的智能指针不会帮助您:图可以是循环的,并且没有特定节点可以负责其他节点的内存管理,因此共享和弱指针是不够的。例如,您可以将所有内容放入向量中并使用索引而不是指针,或者将所有内容放入双端队列并使用原始指针。您可以shared_ptr根据需要使用,但除了开销外不会增加任何内容。或者你可以寻找标记清除 GC。

A more marginal case: I prefer to see functions take a parameter by pointer or reference, and promise not to retain a pointer or reference to it, rather than take a shared_ptrand leave you wondering whether maybe they retain a reference after they return, maybe if you modify the referand ever again you'll break something, etc. Not retaining references is something that often isn't documented explicitly, it just goes without saying. Maybe it shouldn't, but it does. Smart pointers imply something about ownership, and falsely implying that can be confusing. So if your function takes a shared_ptr, be sure to document whether it can retain a reference or not.

一个更边缘的情况:我更喜欢看到函数通过指针或引用获取参数,并承诺不保留指针或对它的引用,而不是使用 ashared_ptr并让您想知道它们是否在返回后保留引用,也许如果你再次修改引用,你会破坏一些东西,等等。不保留引用是通常没有明确记录的东西,这是不言而喻的。也许不应该,但确实如此。智能指针暗示关于所有权的某些事情,错误地暗示这可能会令人困惑。因此,如果您的函数采用shared_ptr,请务必记录它是否可以保留引用。

回答by Mark Wilkins

In many situations, I believe they are definitely the way to go (less messy cleanup code, reduced risk of leaks, etc.). However there is some very slight extra expense. If I were writing some code that had to be as fast as possible (say a tight loop that had to do some allocation and a free), I would probably not use a smart pointer in hopes of eking out a bit more speed. But I doubt that it would make any measurable difference in most situations.

在许多情况下,我相信它们绝对是要走的路(更少的清理代码,减少泄漏的风险等)。然而,有一些非常轻微的额外费用。如果我正在编写一些必须尽可能快的代码(比如一个必须进行一些分配和释放的紧密循环),我可能不会使用智能指针,希望能提高一点速度。但我怀疑它在大多数情况下是否会产生任何可衡量的差异。

回答by jopa

In general, no you cannot use smart pointers always. For example, when you use other frameworks that don't use smart pointer (like Qt), you have to use raw pointers too.

一般来说,不,你不能总是使用智能指针。例如,当您使用其他不使用智能指针的框架(如 Qt)时,您也必须使用原始指针。

回答by David Rodríguez - dribeas

If you are handling a resource, you should always use RAII techniques, with in the case of memory means using some form or another of a smart pointer (note: smart, not shared_ptr, choose the smart pointer that is most appropriate for your specific use case). It is the only way to avoid leaks in the presence of exceptions.

如果您正在处理资源,则应始终使用 RAII 技术,在内存的情况下意味着使用某种形式的智能指针(注意:smart,not shared_ptr,选择最适合您的特定用例的智能指针)。这是在出现异常时避免泄漏的唯一方法。

There are still cases where raw pointers are necessary, when resource management is not handled through the pointer. In particular they are the only way of having a resettable reference. Think of keeping a reference into an object whose lifetime cannot be explicitly handled (member attribute, object in the stack). But that is a very specific case that I have only seen once in real code. In most cases, using a shared_ptris a better approach to sharing an object.

当资源管理不是通过指针处理时,仍然存在需要原始指针的情况。特别是它们是具有可重置参考的唯一方法。考虑保留对生命周期无法显式处理的对象的引用(成员属性,堆栈中的对象)。但这是一个非常特殊的案例,我在实际代码中只见过一次。在大多数情况下,使用 ashared_ptr是共享对象的更好方法。

回答by RyanWilcox

My take on smart pointers: GREAT when it's hard to know when deallocation could happen (say inside an try/catch block, or inside a function that calls a function (or even a constructor!) that could throw you out of your current function), or adding better memory management to a function that has returns everywhere in the code. Or putting pointers in containers.

我对智能指针的看法:当很难知道何时可能发生解除分配时(比如在 try/catch 块内,或在调用函数(甚至是构造函数!)的函数内,这可能会使您退出当前函数)。 ,或者为在代码中随处都返回的函数添加更好的内存管理。或者将指针放在容器中。

Smart pointers, however, have a cost that you might not want to pay all over your program. If memory management is easy to do by hand ("Hmm, I know that when this function ends I need to delete these three pointers, and I know that this function will run to completion"), then why waste the cycles having the computer do it?

然而,智能指针的成本是您可能不想在整个程序中支付的。如果内存管理很容易手工完成(“嗯,我知道当这个函数结束时我需要删除这三个指针,我知道这个函数会运行到完成”),那为什么要浪费计算机来做的周期它?

回答by RyanWilcox

Yes BUT i have gone several projects without the use of a smart pointer or any pointers. Its good practice to use containers such as deque, list, map etc. Alternatively i use references when possible. Instead of passing in a pointer i pass a reference or const reference and its almost always illogical to delete/free a reference so i never have issue there (typically i create them on the stack by writing { Class class; func(class, ref2, ref3); }

是的,但我在没有使用智能指针或任何指针的情况下进行了几个项目。使用容器(如 deque、list、map 等)的好习惯。或者,我尽可能使用引用。我没有传递指针,而是传递引用或常量引用,删除/释放引用几乎总是不合逻辑的,所以我从来没有在那里遇到问题(通常我通过写在堆栈上创建它们{ Class class; func(class, ref2, ref3); }

回答by Trombe

It is. Smart pointer is one of the cornerstones of the old Cocoa (Touch) ecosystem. I believe it keeps impacting the new.

这是。智能指针是旧 Cocoa (Touch) 生态系统的基石之一。我相信它会不断影响新事物。