<内存泄露>
a. 程序员在堆区分配的内存由于某种原因没有及时释放,造成系统内存的浪费,最后导致程序运行速度缓慢甚至程序奔溃。
<造成内存泄露的几种方式>----->我写的是正确的方式!!!
a. new之后,没有及时delete
int* func() { //a.栈区; b.堆区 int *p = new int(); return p; } int main() { auto q = func(); delete q; return 0; }
b. 释放对象数组时没有delete[]
int main() { int* arr = new int[5]; delete[] arr; return 0; }c. 释放二级指针时发生错误
---创建二级指针的时候,分配内存
----删除二级指针的时候,销毁内存
int **p = new int*[4]; for (int i=0;i<4;i++) { p[i] = new int(); } //释放的时候 for (int n=0;n<4;n++) { delete p[n]; } delete p;
d. 没有将基类的析构函数定义为虚函数。使用基类的指针指向子类的对象的时候, 释放内存空间的时候,没有释放完全。
--->一般创建子类对象:
class A { public: A() { cout << "创建父类对象" << endl; }; ~A() { cout << "释放父类空间" << endl; }; }; class B :public A { public: B() { cout << "创建子类对象" << endl; }; ~B() { cout << "释放子类空间" << endl; }; }; int main() { B* b = new B(); delete b; }输出结果:
这里的是完整的创建子类对象和删除子类对象的流程:
我们得知释放子类对象,首先释放子类的空间,然后释放父类的空间。
------类还是这两个类,A和B----->A的析构函数没有加virtual
int main() { A* a = new B(); delete a; //删除的还是子类B的对象, }运行结果:
释放子类对象的时候,内存空间没有释放完毕。
------我们对类A修改,加Virtual修饰符?
class A { public: A() { cout << "创建父类对象" << endl; }; virtual ~A() { cout << "释放父类空间" << endl; }; }; class B :public A { public: B() { cout << "创建子类对象" << endl; }; ~B() { cout << "释放子类空间" << endl; }; }; int main() { A* a = new B(); delete a; //删除的还是子类B的对象, }运行结果:
内存释放完毕。
注意: 我们使用父类的指针指向子类的对象的时候, 我们要在父类的析构函数加Virtual。
e. 缺少拷贝构造函数
(要不我这里给大家说说拷贝构造函数,你们不说话我就当默认了啊!!!--->简单介绍)
e1. 拷贝构造函数的定义:
普通对象的赋值:
int a =10; int b =a;
类对象的赋值:
class A { int x; public: A() { cout << "创建父类对象" << endl; }; virtual ~A() { cout << "释放父类空间" << endl; }; A(const A& a) //拷贝构造函数,我们不写的时候,系统会为我们提供一个默认的,就是这样的 { x = a.x; } }; int main() { A a ; A a1 = a; //类对象赋值的时候会调用拷贝构造函数 }拷贝构造函数: 是一种特殊的构造函数, 它其中必须的参数是类对象的引用。
e2. 拷贝构造函数的调用时机:
(1). 对象以值传递的方式作为形式参数
class A { int x; public: A(int a) { x = a; cout << "创建父类对象" << endl; }; virtual ~A() { cout << "释放父类空间" << endl; }; A(const A& a) //拷贝构造函数,我们不写的时候,系统会为我们提供一个默认的,就是这样的 { x = a.x; } int getA() { return x; }; }; void func(A b) { cout << b.getA() << endl; } int main() { A a(5); func(a); //调用拷贝构造函数 }(2). 类的对象以值的方式返回
class A { int x; public: A(int a) { x = a; cout << "创建父类对象" << endl; }; virtual ~A() { cout << "释放父类空间" << endl; }; A(const A& a) //拷贝构造函数,我们不写的时候,系统会为我们提供一个默认的,就是这样的 { x = a.x; } int getA() { return x; }; }; A func() { A c(5); return c; } int main() { func(); }(3). 类对象通过另一个类的对象初始化
class A { int x; public: A(int a) { x = a; cout << "创建父类对象" << endl; }; virtual ~A() { cout << "释放父类空间" << endl; }; A(const A& a) //拷贝构造函数,我们不写的时候,系统会为我们提供一个默认的,就是这样的 { x = a.x; } int getA() { return x; }; }; int main() { A a(5); A b(a); A c = a; }总结: 类的对象通过值的形式赋值的时候,会调用拷贝构造函数。
e3. 浅拷贝和深拷贝:
浅拷贝: 对数据成员进行简单的赋值,但是对指针变量不会重新分配内存。
深拷贝: 不仅对数据成员进行赋值,还可以对指针变量重新分配内存。
系统提供的默认的拷贝构造函数是浅拷贝。
对于类中成员变量有指针变量的时候,分析浅拷贝:
class A { int x; int* p; public: //p分配了一个内存 A(int a) { x = a; p = new int(); }; virtual ~A() { delete p; }; A(const A& a) //深拷贝构造函数 { x = a.x; p = a.p; } int getA() { return x; }; }; int main() { A a(5); A b(a); }上面的程序会报错,我们分析一下:
深拷贝:
class A { int x; int* p; public: //p分配了一个内存 A(int a) { x = a; p = new int(); }; virtual ~A() { delete p; }; A(const A& a) //深拷贝构造函数 { x = a.x; //重新分配内存空间 p = new int(); //赋值 *p = *(a.p); } int getA() { return x; }; }; int main() { A a(5); A b(a); }
---------------------------------感谢:
a. invisible_sky的博客
b. Lwbeyond的博客
大家看的不够详细的话,可以搜这两个大神的博客,谢谢这两位大神。