总是使用智能指针是一个好习惯吗? [英] Is it a good practice to always use smart pointers?

查看:234
本文介绍了总是使用智能指针是一个好习惯吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



注意:尽管我来自Java背景,但我理解了智能指针的实现和RAII的概念。所以你可以在发布答案时从我这边得到这个知识。我使用静态分配几乎无处不在,只在必要时使用指针。我的问题只是:我可以使用智能指针代替原始指针吗

解决方案

鉴于多次编辑,我有一个印象,一个全面的总结将是有用的。



1。何时不



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



首先是完全相同的情况,你不应该使用 C ++ 实际上。 IE:DLL边界如果你不提供源代码给客户端。让我们说anecdotal。



第二个发生得更频繁:智能管理器意味着所有权。您可以使用指针指向现有资源,而不管理其生命周期,例如:

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

if(pointer)doSomething(* pointer);
}

此示例受到约束。但是指针在语义上不同于引用,因为它可能指向一个无效位置(空指针)。在这种情况下,不要使用智能指针代替它,这是完全正确的,因为你不想管理对象的生命周期。



2 。智能管理员



除非您正在编写智能管理员类,否则如果您使用关键字



这是一个有争议的观点,有缺陷的代码,我不再采取机会。所以,如果你写 new ,你需要一个智能管理器为新分配的内存。现在你需要它。



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



现在,真正的困难开始:哪个智能管理器?



3。智能指针



有各种智能指针,有各种特征。



跳过 std :: auto_ptr 你应该避免(它的副本语义被拧了)。




    < $ scoped_ptr :无开销,无法复制或移动。
  • unique_ptr

    $ c>:一些开销(引用计数),可以复制。



通常,尝试使用 scoped_ptr unique_ptr 。如果你需要几个业主试图改变设计。如果你不能改变设计,真的需要几个所有者,使用 shared_ptr ,但要小心引用循环,应该使用 weak_ptr 中间某处。



4。智能容器



许多智能指针并不意味着被复制,因此它们与STL容器的使用在一定程度上受到了影响。



不使用 shared_ptr 及其开销,请使用指针容器。它们模拟了经典STL容器的接口,但存储了他们自己的指针。



5。滚动您自己的



在某些情况下,您可能希望滚动自己的智能管理器。请检查您是否错过了您之前使用的库中的某些功能。



在存在异常的情况下编写智能管理器非常困难。你通常不能假定内存是可用的( new 可能会失败)或复制构造函数有<$ c $

在某种程度上,忽略 std :: bad_alloc 异常,并强加复制构造函数的一些帮助器不会失败...毕竟,这是 boost :: shared_ptr 用于其删除程序 D 模板参数。



但我不推荐,特别是对于初学者。这是一个棘手的问题,你现在不太可能注意到错误。



6。示例

  //为了简短的代码,避免使用真实的代码;)
using namespace促进;

//示例类
//是的,clone返回一个原始指针...
//它会给调用者带来如何包装的负担
//它遵循
中描述的`Cloneable'概念$ bool指针容器库链接
struct可克隆
{
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);
} //内存释放这里

//移动的语义的例子
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超出范围,释放内存

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()//有意义的语义,它被取消引用
);
} //当vec超出范围,它清除一切;)


I find smart pointers to be a lot more comfortable than raw pointers. So is it a good idea to always use 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. )

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???

解决方案

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

1. When not to

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.

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

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

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 new you need a smart manager for the newly allocated memory. And you need it right now.

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

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

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

  • 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.

Usually, try to use either scoped_ptr or 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_ptr somewhere in the midst.

4. Smart containers

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

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

5. Rolling your own

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 (new may fail) or that Copy Constructors have the no throw guarantee.

It may be acceptable, somewhat, to ignore the std::bad_alloc exception and impose that Copy Constructors of a number of helpers do not fail... after all, that's what boost::shared_ptr does for its deleter D template parameter.

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

// 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 ;)

这篇关于总是使用智能指针是一个好习惯吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆