Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Smart pointers & destructor

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.

like image 506
fex Avatar asked Apr 03 '12 19:04

fex


People also ask

What do smart pointers do?

In modern C++ programming, the Standard Library includes smart pointers, which are used to help ensure that programs are free of memory and resource leaks and are exception-safe.

What are the different types of smart pointers?

C++ libraries provide implementations of smart pointers in following types: auto_ptr.

Why should I consider using smart pointers?

Smart pointers try to prevent memory leaks by making the resource deallocation automatic: when the pointer to an object (or the last in a series of pointers) is destroyed, for example because it goes out of scope, the pointed object is destroyed too.

How are smart pointers implemented?

In C++, a smart pointer is implemented as a template class that mimics, by means of operator overloading, the behaviors of a traditional (raw) pointer, (e.g. dereferencing, assignment) while providing additional memory management features.


2 Answers

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.

like image 107
gymbrall Avatar answered Oct 26 '22 09:10

gymbrall


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

like image 22
Mooing Duck Avatar answered Oct 26 '22 10:10

Mooing Duck