使用全局指针和自定义析构函数 - 如何避免内存泄漏?
Using global pointers and custom destructor - How to avoid memory leak?
我一直在通过 Bjarne Stroustrup 的书 [C++ 11] 学习 C++ 来完成我的论文。即便如此,我仍然没有得到有关在 C++ 中处理内存泄漏的信息。他在书中说了很多避免使用 new
、delete
以及自定义析构函数。
我想说我正在使用 QT 构建我的 UI,现在请考虑这个例子来解决我的前三个问题:
class KHUB : public QMainWindow
{
Q_OBJECT
public:
KHUB(QWidget *parent = 0)
{
createGroupScreen();
};
~KHUB();
private:
QWidget *groupScreen;
void createGroupScreen()
{
groupScreen = new QWidget();
};
}
这是恢复我的应用程序。我有一个名为 KHUB 的构造函数,它创建一个对话框 (QWidget
),提供一些选项来放置名称、类别等,以创建一个新组。
因为我应该避免裸new
和delete
,有没有不同的方法(比如使用unique_ptr,不知道是不是这里适用的概念)或不太容易出错创建这个 QWidget
并且仍然有对象引用在我的 createGroupScreen
成员函数之外操作它?
如果我在成员函数中创建这个对象,没有全局指针,也不使用裸new
,只要它的父对象还活着,这个对象就会保留在内存中,或者成员函数最后会被删除吗?
如果它和它的父对象一起存活,我如何在成员函数之外获取对象引用以便调用~QWidget()
?
我的第四个问题是关于同一个对象的破坏。
- 如果我通过调用
groupScreen->~QWidget()
销毁 groupScreen
这只会销毁我的 QWidget
对象并且我的指针将保留在那里作为内存泄漏等待我 delete groupScreen
?
考虑这是我的第五个、第六个也是最后一个问题的另一段代码:
class KHUB : public QMainWindow
{
Q_OBJECT
public:
KHUB(QWidget *parent = 0)
~KHUB()
{
delete groupScreen;
delete buttonOne;
delete buttonTwo;
delete buttonThree;
};
private:
QWidget *groupScreen;
QPushButton *buttonOne, *buttonTwo, *buttonThree;
/* Components Setup */
void btSetup(QPushButton** button, const QString name, int posX, int posY, int width, int height);
}
我正在尝试使用具有设置按钮名称、位置和大小功能的按钮来重用一些代码,这样代码就不会因为对每个按钮重复输入相同的参数而变得如此庞大 -但我也担心程序的性能。
当我调用我的 KHUB 构造函数时,这并不意味着我的 .h 文件中的所有全局指针都被分配了,对吗?只有在初始化时才会发生这种情况,对吗?
如果我真的需要创建我的自定义析构函数,我应该在其中删除我的 .h 文件中声明的每个全局指针吗?我想知道如果我没有通过其中一个指针的初始化,那是否不会有风险。
有没有办法循环获取和删除指针?即 for(auto p : globalPtrs) delete p;
我试图在堆栈中找到有关此主题的一些问题,但 none 对我很有启发:
- C++ destructor memory leak
- Avoiding memory leak
- Avoiding memory leaks
也许您应该先阅读 memory management
上的 C++ 常见问题解答
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?
是的,如果是现代 C++,您应该使用 std::shared_ptr or std::unique_ptr or std::weak_ptr。他们都负责销毁他们所指的记忆。他们每个人在所有权、共享等方面都有不同的特征。
我会尝试使用它们,因为它们简化了内存管理。如果不可能(例如,您的编译器不符合 C++11 并且没有使用 boost...可能有很多原因),您可以使用 new/delete...只是要格外小心。
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?
不确定你的意思。
如果您正在创建一个变量,method/function 本地、对象属性...等。当定义它的范围消失时,该变量将被销毁。
假设你有:
void myfunc() {
int a=0;
}
当你进入范围时,整数 a 的内存被创建,当你离开范围时,整数 a 的内存被销毁。在这种情况下,它将是堆栈上的内存(相对于堆上的内存)
如果您在 class 中有一个属性,当创建对象时,将创建该属性(构造函数)。当对象被销毁时,属性也会被销毁(析构函数)。
当您使用 new 进行分配时,内存是从堆中分配的。如果您将对该内存的引用存储在一个(普通)指针变量中,当指针变量被销毁时,指针 引用的内存不会 被销毁(并且您有泄漏)。
当你有一个指针引用的内存并且你为该指针分配一个不同的内存引用时,可以说同样的事情。如果该内存未在别处引用,则它会丢失到您的程序中。
if it remains alive with its parent, how can I acquire the object
reference outside the member function so I can call ~QWidget()?
如果你用 new 分配,那么你必须在超出范围时销毁它,或者将内存引用保存到某个其他范围中的指针变量,你可以从那里随时删除它。
请参阅 C++ 常见问题解答:Why isn’t the destructor called at the end of scope?
您可能想要阅读的内容是 RAII 作为 智能指针 我在第一个问题中列出的使用这个成语。
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?
请参阅 C++ 常见问题解答:What are the two steps that happen when I say delete p?
调用 delete groupScreen 将导致执行 ~QWidget()
以及所有属性 (non-pointer/reference) 的析构函数和父 classes 的析构函数(如果有)和释放对象的内存。
当您处理 classes 的层次结构时,重要的是要考虑是否可以通过指向父 class(es) 的指针删除对象。如果是这种情况,析构函数方法必须声明为虚拟的,以便执行实际对象 class 方法(而不是执行指针指向的 class 的析构函数)(这是另一种形式泄漏)。
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?
当您调用构造函数时,在执行自定义构造函数之前,将分配属性。但是不会为指针分配内存,而是为指针本身分配内存(重要的是你要注意将它初始化为一些合理的值,例如null_ptr)
您在 class 的 外部 声明的变量或 class 的静态属性不应在 *.h 中初始化,因为它们会导致符号出现多次(因为头文件包含在不同的编译单元中)
在*.cpp 中初始化它们。但是要小心,因为它可能会导致问题(参见 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.
如果
,您应该创建自定义析构函数
- 您想对对象销毁做任何事情
- 如果您的对象包含它拥有的动态内存(裸)指针(即不会被其他人处理)
Is there a way to loop the pointers acquirement and delete? i.e.
for(auto p : globalPtrs) delete p;
不完全确定你所说的 globalPtrs 是什么意思...如果它是一些包含对象中所有指针的神奇变量,那么就没有这样的东西。
您必须注意删除每个指针(您拥有的)...这就是为什么智能指针不太容易出错,因为它们会自动执行。
也许一个例子会有所帮助:
#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;
}
这会产生以下输出(带有一些注释)
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
让我们尝试一些不同的东西。相同的 classes 定义。我们现在的主要是:
int main() {
std::cout << "A as pointer" << std::endl;
A *a_ptr=0;
std::cout << "running new" << std::endl;
a_ptr=new A();
}
我们得到
A as pointer
running new
B()
A()
没有析构函数,我们已经泄露了 A 对象。
我一直在通过 Bjarne Stroustrup 的书 [C++ 11] 学习 C++ 来完成我的论文。即便如此,我仍然没有得到有关在 C++ 中处理内存泄漏的信息。他在书中说了很多避免使用 new
、delete
以及自定义析构函数。
我想说我正在使用 QT 构建我的 UI,现在请考虑这个例子来解决我的前三个问题:
class KHUB : public QMainWindow
{
Q_OBJECT
public:
KHUB(QWidget *parent = 0)
{
createGroupScreen();
};
~KHUB();
private:
QWidget *groupScreen;
void createGroupScreen()
{
groupScreen = new QWidget();
};
}
这是恢复我的应用程序。我有一个名为 KHUB 的构造函数,它创建一个对话框 (QWidget
),提供一些选项来放置名称、类别等,以创建一个新组。
因为我应该避免裸
new
和delete
,有没有不同的方法(比如使用unique_ptr,不知道是不是这里适用的概念)或不太容易出错创建这个QWidget
并且仍然有对象引用在我的createGroupScreen
成员函数之外操作它?如果我在成员函数中创建这个对象,没有全局指针,也不使用裸
new
,只要它的父对象还活着,这个对象就会保留在内存中,或者成员函数最后会被删除吗?如果它和它的父对象一起存活,我如何在成员函数之外获取对象引用以便调用
~QWidget()
?
我的第四个问题是关于同一个对象的破坏。
- 如果我通过调用
groupScreen->~QWidget()
销毁groupScreen
这只会销毁我的QWidget
对象并且我的指针将保留在那里作为内存泄漏等待我delete groupScreen
?
考虑这是我的第五个、第六个也是最后一个问题的另一段代码:
class KHUB : public QMainWindow
{
Q_OBJECT
public:
KHUB(QWidget *parent = 0)
~KHUB()
{
delete groupScreen;
delete buttonOne;
delete buttonTwo;
delete buttonThree;
};
private:
QWidget *groupScreen;
QPushButton *buttonOne, *buttonTwo, *buttonThree;
/* Components Setup */
void btSetup(QPushButton** button, const QString name, int posX, int posY, int width, int height);
}
我正在尝试使用具有设置按钮名称、位置和大小功能的按钮来重用一些代码,这样代码就不会因为对每个按钮重复输入相同的参数而变得如此庞大 -但我也担心程序的性能。
当我调用我的 KHUB 构造函数时,这并不意味着我的 .h 文件中的所有全局指针都被分配了,对吗?只有在初始化时才会发生这种情况,对吗?
如果我真的需要创建我的自定义析构函数,我应该在其中删除我的 .h 文件中声明的每个全局指针吗?我想知道如果我没有通过其中一个指针的初始化,那是否不会有风险。
有没有办法循环获取和删除指针?即
for(auto p : globalPtrs) delete p;
我试图在堆栈中找到有关此主题的一些问题,但 none 对我很有启发:
- C++ destructor memory leak
- Avoiding memory leak
- Avoiding memory leaks
也许您应该先阅读 memory management
上的 C++ 常见问题解答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?
是的,如果是现代 C++,您应该使用 std::shared_ptr or std::unique_ptr or std::weak_ptr。他们都负责销毁他们所指的记忆。他们每个人在所有权、共享等方面都有不同的特征。
我会尝试使用它们,因为它们简化了内存管理。如果不可能(例如,您的编译器不符合 C++11 并且没有使用 boost...可能有很多原因),您可以使用 new/delete...只是要格外小心。
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?
不确定你的意思。 如果您正在创建一个变量,method/function 本地、对象属性...等。当定义它的范围消失时,该变量将被销毁。
假设你有:
void myfunc() {
int a=0;
}
当你进入范围时,整数 a 的内存被创建,当你离开范围时,整数 a 的内存被销毁。在这种情况下,它将是堆栈上的内存(相对于堆上的内存)
如果您在 class 中有一个属性,当创建对象时,将创建该属性(构造函数)。当对象被销毁时,属性也会被销毁(析构函数)。
当您使用 new 进行分配时,内存是从堆中分配的。如果您将对该内存的引用存储在一个(普通)指针变量中,当指针变量被销毁时,指针 引用的内存不会 被销毁(并且您有泄漏)。
当你有一个指针引用的内存并且你为该指针分配一个不同的内存引用时,可以说同样的事情。如果该内存未在别处引用,则它会丢失到您的程序中。
if it remains alive with its parent, how can I acquire the object reference outside the member function so I can call ~QWidget()?
如果你用 new 分配,那么你必须在超出范围时销毁它,或者将内存引用保存到某个其他范围中的指针变量,你可以从那里随时删除它。
请参阅 C++ 常见问题解答:Why isn’t the destructor called at the end of scope?
您可能想要阅读的内容是 RAII 作为 智能指针 我在第一个问题中列出的使用这个成语。
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?
请参阅 C++ 常见问题解答:What are the two steps that happen when I say delete p?
调用 delete groupScreen 将导致执行 ~QWidget()
以及所有属性 (non-pointer/reference) 的析构函数和父 classes 的析构函数(如果有)和释放对象的内存。
当您处理 classes 的层次结构时,重要的是要考虑是否可以通过指向父 class(es) 的指针删除对象。如果是这种情况,析构函数方法必须声明为虚拟的,以便执行实际对象 class 方法(而不是执行指针指向的 class 的析构函数)(这是另一种形式泄漏)。
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?
当您调用构造函数时,在执行自定义构造函数之前,将分配属性。但是不会为指针分配内存,而是为指针本身分配内存(重要的是你要注意将它初始化为一些合理的值,例如null_ptr)
您在 class 的 外部 声明的变量或 class 的静态属性不应在 *.h 中初始化,因为它们会导致符号出现多次(因为头文件包含在不同的编译单元中)
在*.cpp 中初始化它们。但是要小心,因为它可能会导致问题(参见 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.
如果
,您应该创建自定义析构函数- 您想对对象销毁做任何事情
- 如果您的对象包含它拥有的动态内存(裸)指针(即不会被其他人处理)
Is there a way to loop the pointers acquirement and delete? i.e. for(auto p : globalPtrs) delete p;
不完全确定你所说的 globalPtrs 是什么意思...如果它是一些包含对象中所有指针的神奇变量,那么就没有这样的东西。
您必须注意删除每个指针(您拥有的)...这就是为什么智能指针不太容易出错,因为它们会自动执行。
也许一个例子会有所帮助:
#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;
}
这会产生以下输出(带有一些注释)
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
让我们尝试一些不同的东西。相同的 classes 定义。我们现在的主要是:
int main() {
std::cout << "A as pointer" << std::endl;
A *a_ptr=0;
std::cout << "running new" << std::endl;
a_ptr=new A();
}
我们得到
A as pointer
running new
B()
A()
没有析构函数,我们已经泄露了 A 对象。