Probably you should start by reading the C++ FAQ on memory management
Since I'm supposed to avoid naked new and delete, is there a different way (such as using unique_ptr, not sure if it's an
applicable concept here) or not so error prone to create this QWidget
and still have the object reference to manipulate it outside the my
createGroupScreen member function?
Yes, if modern C++, you are supposed to use std::shared_ptr or std::unique_ptr or std::weak_ptr. All of them take care of the destruction of the memory they refer to. Each of them has different characteristics regarding ownership, sharing, etc.
I'd try to use them, as they simplify memory management. If not possible (e.g. your compiler is not up to C++11 and are not using boost... there might be many reasons), you can use new/delete... just be extra careful.
if I create this object inside the member function, without a global
pointer and also not using naked new, this object will remain in the
memory as long as it's parent is alive or in the end of the member
function it will be deleted?
Not sure what you mean.
if you're creating a variable, method/function local, object attribute... etc.. The variable will be destroyed when the scope it is defined in goes away.
Say you have:
void myfunc() {
int a=0;
}
when you enter the scope the memory for the integer a is created and when you go out of the scope the memory for the integer a is destroyed. In this case it will be memory on the stack (vs. memory on the heap)
If you have an attribute in an class, when the object is created, the attribute will be created (constructor). When the object is destroyed, the attribute will be destroyed (destructor).
When you are allocating with new, memory is allocated from the heap. If you store a reference to that memory in a (plain) pointer variable, when the pointer variable is destroyed, the memory referenced by the pointer is not destroyed (and you have a leak).
Same thing can be said when you have memory referenced by a pointer and you assign a different memory reference to that pointer. If the memory is not referenced elsewhere, it is lost to your program.
if it remains alive with its parent, how can I acquire the object
reference outside the member function so I can call ~QWidget()?
if you allocated with new, then you must either destroy it when going out of scope or save the memory reference to a pointer variable in some other scope from where you can delete it what the time comes.
See the C++ FAQ:Why isn’t the destructor called at the end of scope?
Something you might want to read about is RAII as the smart pointer I listed in the first question use this idiom.
If I destroy groupScreen by calling groupScreen->~QWidget() this will
only destroy my QWidget object and my pointer will remain there as a
memory leak waiting for me to delete groupScreen?
See the C++ FAQ:What are the two steps that happen when I say delete p?
Calling delete groupScreen will cause the ~QWidget()
to be executed and the destructors of all the attributes (non-pointer/reference) and the destructor of the parent classes (if any) and the memory for the object deallocated.
When you are dealing with a hierarchy of classes, it is important to consider if the object might be deleted through a pointer to the parent class(es). If that's the case, the destructor method must be declared virtual, so that actual object class method is executed (instead of executing the destructor of the class pointed to by the pointer) (which is yet another form of leak).
When I call my KHUB constructor, that doesn't mean all global pointers
that I have in my .h file are being allocated right? That only happens
when they are initialized, correct?
When you call the constructor, before executing your custom constuctor, the attributes will be allocated. But a pointer will not be allocated memory an assigned to it, just memory for the pointer itself (and it is important that you take care of initializing it to some sane value, e.g. null_ptr)
variables you declare outside of a class or static attributes of a class, should not be initialized in a *.h, as they will cause symbols to appear multiple times (as the header is included in different compilation units)
Initialize them in the *.cpp. However be careful as it might be a cause of problems (see C++ FAQ)
If I really need to create my custom destructor, I should delete in it
every global pointer declared in my .h file? I'm wondering if that
wouldn't be risk in case I didn't pass through the initialization of
one of those pointers.
You should create a custom destructor if
- You want to do anything on object destruction
- If your object contains dynamic memory (naked) pointers that it owns (i.e. that will not be taken care of by someone else)
Is there a way to loop the pointers acquirement and delete? i.e.
for(auto p : globalPtrs) delete p;
not entirely sure what you mean by globalPtrs... if it is some magical variable that contains all the pointers in an object, there's not such a thing.
You must take care of the deletion of every pointer (you owned)... that's why the smart pointers are much less error-prone, as they will do it automatically.
Perhaps an example will help:
#include <iostream>
class B {
public:
B() {
std::cout << "B()" << std::endl;
}
~B() {
std::cout << "~B()" << std::endl;
}
};
class A {
public:
A() {
std::cout << "A()" << std::endl;
}
~A() {
std::cout << "~A()" << std::endl;
}
B b;
};
int main() {
std::cout << "A as variable" << std::endl;
A a;
std::cout << "A as pointer" << std::endl;
A *a_ptr=0;
std::cout << "running new" << std::endl;
a_ptr=new A();
std::cout << "running delete" << std::endl;
delete a_ptr;
}
This produces the following output (with some annotations)
A as variable // We are declaring a stack variable of type A
B() // since A contains a B attribute object it creates it
A() // ... and runs the constructor
A as pointer // We are declaring a stack variable pointer to A
// ... nothing happens
running new // but when we run new, we get
B() // the creation of the attribute (and its constructor)
A() // the execution of the constructor
running delete // now we call delete
~A() // that calls A's destructor
~B() // and then proceeds to destroy the attributes.
~A() // these are the destructor for the first variable
~B() // which is now going out of scope
let's try something different. Same classes definition. Our main is now:
int main() {
std::cout << "A as pointer" << std::endl;
A *a_ptr=0;
std::cout << "running new" << std::endl;
a_ptr=new A();
}
We get
A as pointer
running new
B()
A()
No destructor, we've leaked the A object.