Cocos2dx <基础> C++的内存泄露(1)

<内存泄露>

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);
} 
f. 调用库存在内存泄露。在使用个人封装的库或者没有完全测试的库时,我们要检测次库对程序的性能有无影响。----->(我的实力不够,没有遇到)

---------------------------------感谢:

a. invisible_sky的博客

b. Lwbeyond的博客

大家看的不够详细的话,可以搜这两个大神的博客,谢谢这两位大神。

猜你喜欢

转载自blog.csdn.net/wue1206/article/details/80244434