Smart pointers & destructor

Go To StackoverFlow.com

7

I'd like to know do I need to write destructor in classes when I don't use raw pointers anymore? Just boost smart pointers.

2012-04-03 19:57
by fex
Maybe @simchona knows something I don't, but smart pointers don't alleviate the need for cleanup, they just change how (when) it happens - NoName 2012-04-03 20:01
I'm reading this question as, do boost smart pointers in and of themselves remove the need for writing destructors in pretty much all cases? Is that correct - gymbrall 2012-04-03 20:01
@Yeah, if you have a deep_pointer (or whatever it's called), then you don't need assignment/move/destructor/constructors. (Though constructors can still sometimes be handy - Mooing Duck 2012-04-03 20:14


11

Boost smart pointers by themselves don't have anything to do with the need for a destructor. All they do is remove the need for you to call delete on the allocated memory that they are effectively managing. So having said that, if before you started using smart pointers all you had in your destructors were calls to delete and delete[] freeing the memory of dynamically allocated class members and you have now switched all those regular pointers over to smart pointers, you could probably just switch to an empty destructor as they will now clean up for themselves when they go out of scope.

However, if for whatever reason, you have a class that needs to do cleanup (file cleanup, sockets, other resources etc) you will still need to provide a destructor to do that.

Let me know if that helps.

2012-04-03 20:07
by gymbrall
All those things can be cleaned up by wrapping them in an RAII class, like a smart_ptr. With C++11, class destructors should be rare - Mooing Duck 2012-04-03 20:16


4

Each resource type should have a RAII class to manage that resource. If you also have a smart pointer with deep copy semantics (pretty easy to do), that's all you need to manage your resources 99.9% of the time. I don't know why unique_ptr doesn't do deep copies, nor any boost smart pointer, but if you have those two things, you do not need to write copy constructors, move constructors, assignment operators, move assignment operators, nor destructors. You may or may not have to provide other constructors (including the default constructor), but that's five less places to make mistakes.

#include <memory>

template<class Type, class Del = std::default_delete<Type> >
class deep_ptr : public std::unique_ptr<Type, Del> {
public: 
     typedef std::unique_ptr<Type, Del> base;
     typedef typename base::element_type element_type;
     typedef typename base::deleter_type deleter_type;
     typedef typename base::pointer pointer;

     deep_ptr() : base() {}
     //deep_ptr(std::nullptr_t p) : base(p) {}  //GCC no has nullptr_t?
     explicit deep_ptr(pointer p) : base() {}
     deep_ptr(pointer p, const typename std::remove_reference<Del>::type &d) : base(p, d) {} //I faked this, it isn't quite right
    deep_ptr(pointer p, typename std::remove_reference<Del>::type&& d): base(p, d) {}
    deep_ptr(const deep_ptr& rhs) : base(new Type(*rhs)) {}
    template<class Type2, class Del2>
    deep_ptr(const deep_ptr<Type2, Del2>& rhs) : base(new Type(*rhs)) {}
    deep_ptr(deep_ptr&& rhs) : base(std::move(rhs)) {}
    template<class Type2, class Del2>
    deep_ptr(deep_ptr<Type2, Del2>&& rhs) : base(std::move(rhs)) {}

    deep_ptr& operator=(const deep_ptr& rhs) {base::reset(new Type(*rhs)); return *this;}
    template<class Type2, class Del2>
    deep_ptr& operator=(const deep_ptr<Type2, Del2>& rhs) {base::reset(new Type(*rhs)); return *this;}
    deep_ptr& operator=(deep_ptr&& rhs) {base::reset(rhs.release()); return *this;}
    template<class Type2, class Del2>
    deep_ptr& operator=(deep_ptr<Type2, Del2>&& rhs) {base::reset(rhs.release()); return *this;}
    void swap(deep_ptr& rhs) {base::swap(rhs.ptr);}
    friend void swap(deep_ptr& lhs, deep_ptr& rhs) {lhs.swap(rhs.ptr);}
};

With this class (or one similar), you don't need much at all!

struct dog {
   deep_ptr<std::string> name;
};

int main() {
    dog first; //default construct a dog
    first.name.reset(new std::string("Fred"));
    dog second(first); //copy construct a dog
    std::cout << *first.name << ' ' << *second.name << '\n';
    second.name->at(3) = 'o';
    std::cout << *first.name << ' ' << *second.name << '\n';
    second = first; //assign a dog
    std::cout << *first.name << ' ' << *second.name << '\n';
}

As demonstrated at http://ideone.com/Kdhj8

2012-04-03 20:21
by Mooing Duck


-2

You should always consider providing a destructor. You would use that to free any resources your class is holding. Often my smart_ptr class destructors are empty but not always. File streams, database connections, etc all need proper cleanup.

2012-04-03 20:02
by StAlphonzo
All those things can be cleaned up by wrapping them in an RAII class, like a smart_ptr. With C++11, class destructors should be rare - Mooing Duck 2012-04-03 20:15
@Mooing No kidding. Smart pointers/RAII reduce my code by a lot and my destructors are generally empty. They take a while to get used to, but they are worth learning for sure - Aura 2012-04-03 20:20
Ads