You need to define a destructor if default destruction is not enough. Of course, this just asks the question: what does the default destructor do? Well, he calls the destructors of each of the member variables and what he is. If this is enough for you, you are well off. If this is not the case, you need to write a destructor.
The most common example is when a pointer is placed with a new one. A pointer (for any type) is primitive, and the destructor simply forces the pointer to leave without touching the pointer to memory. Thus, the default pointer destructor does not have the correct behavior for us (this will lead to a memory leak), so we need a delete call in the destructor. Imagine now that we change the pointer to a smart pointer. When a smart pointer is destroyed, it also calls the destructor of what it points to, and then frees memory. Thus, a smart pointer destructor is sufficient.
Understanding the root cause of the most common case, you can talk about less common cases. It is true that very often, if you use smart pointers and std library containers, their destructors do the right thing, and you don’t need to write a destructor at all. But there are still exceptions.
Suppose you have a Logger class. This logger class is intelligent, but it buffers a bunch of messages in the log, and then writes them to a file only when the buffer reaches a certain size (it "flushes" the buffer). This may be more effective than immediately dumping everything into a file. When the Logger is destroyed, you need to flush everything from the buffer, regardless of whether it is full, so you probably want to write a destructor for it, although it is quite easy to implement Logger in terms of std :: vector and std :: string, so nothing flows when it is destroyed.
Edit: I have not seen question 2. The answer to question 2 is that you should not call delete if it is a non-owner pointer. In other words, if any other class or area of responsibility is solely responsible for cleaning up after this object, and you have a pointer to “just look,” then do not call delete. The reason is that if you call delete and someone else owns it, the pointer deletes it twice:
struct A { A(SomeObj * obj) : m_obj(obj){}; SomeObj * m_obj; ~A(){delete m_obj;}; } SomeObj * obj = new SomeObj(); A a(obj); delete obj;
In fact, perhaps the manual in C ++ 11 should NEVER call delete on a pointer. What for? Well, if you call delete on a pointer, that means you own it. And if you own it, there is no reason not to use a smart pointer, in particular unique_ptr - almost the same speed and does it automatically, and is much more likely to be thread safe.
Also, besides (excuse me that I'm really getting into it now), it's usually a bad idea to make unexplored representations of objects (source pointers or links) members of other objects. What for? Since an object with a raw pointer may not have to worry about destroying another object, since it does not own it, but it does not know when it will be destroyed. The specified object can be destroyed while the object with the pointer is still alive:
struct A { SomeObj * m_obj; void func(){m_obj->doStuff();}; } A a; if(blah) { SomeObj b; a.m_obj = &b; } a.func()
Please note that this applies only to the fields of object objects. Passing the representation of an object into a function (member or not) is safe because the function is called in the scope of the object itself, so this is not a problem.
The harsh conclusion to all of this is that if you don’t know what you are doing, you simply should not have the original pointers or references as fields of object objects.
Edit 2: I think the general conclusion (which is really good!) Is that in general your classes should be written in such a way that they do not need destructors unless the destructors do something semantically meaningful. In my example, Logger Logger must be reset, something important must happen before it is destroyed. You should not write (usually) classes that must perform a trivial cleanup after their members, member variables must clean up after themselves.