Is it a good practice to always use smart pointers?

后端 未结 10 2003
眼角桃花
眼角桃花 2020-12-04 05:08

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 b

相关标签:
10条回答
  • 2020-12-04 05:44

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

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

    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.

    0 讨论(0)
  • 2020-12-04 05:45

    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.

    0 讨论(0)
  • 2020-12-04 05:49

    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.

    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?

    0 讨论(0)
  • 2020-12-04 05:52

    Smart pointers do perform 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.

    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.

    0 讨论(0)
  • 2020-12-04 05:53

    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.

    0 讨论(0)
  • 2020-12-04 05:59

    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 ;)
    
    0 讨论(0)
提交回复
热议问题