一,C++内存泄露
下面列出的是有可能导致内存泄露的几种情况
1,在类的构造函数与析构函数中没有匹配调用new与delete运算符
这种情况需要注意两个问题
a,在构造函数中使用new分配内存,但是在析构函数中没有释放掉内存。
b,在构造函数中使用new []分配内存,但是在析构函数中使用delete释放内存,没有使用delete []来释放内存。
2,指向对象的指针数组不等于对象数组
对象数组是指,数组中存放的是对象,只需要delete [] p,即可调用对象数组中的每个对象的析构函数释放空间。指向对象的指针数组是指,数组中存放的是指向对象的指针,不仅要释放每个对象的空间,还要释放每个指针的指向的内存空间,delete [] p只是释放了每个指针,但是并没有释放对象的空间,正确的做法,是通过一个循环,将每个对象释放,然后再把指针释放。
3,浅复制(拷贝构造函数与赋值运算符)
类copy constructor与assign operation进行的都是浅复制,进行逐个成员的复制。如果类的一个成员变量是一个指针,在类的构造函数中,使用new分配一块内存空间,初始化上面的指针,让这个指针指向前面分配的内存空间。
class String{
private:
char *p;
public:
String(char *str){
int len = strlen(str);
p = new char[len + 1];
strcpy(p, str);
}
friend ostream & operator<<(ostream &os, String &obj){
os<<obj.p<<endl;
}
~String(){
delete[] p;
}
};
int main(){
String A("hello");
{
String B("world");
A = B;
}
cout<<A;
return 0;
}
输出结果
t
Process returned 0 (0x0) execution time : 0.016 s
Press any key to continue.
程序分析
输出的结果为乱码,并不是我们所期待的输出"word"。上面的代码首先在一个局部的作用域定义一个String B("world"),之后把B对象的值赋值给A对象,此时,使用的是默认的赋值运算符,进行的是浅复制,会把B.p指向的内存地址复制一份给A.p。当离开这个局部作用域时,B的生命周期结束,它的析构函数会被调用,释放掉B.p指向的内存,此时的A.p指向的就是前面被释放掉的内存,因此输出A对象时,出现乱码。自定义assign operation进行深复制解决上面的问题
String & String::operator=(const String &s){
if(s == *this){
return *this;
}
delete [] p;
int len = strlen(s.p);
p = new char[len + 1];
strcpy(p, s.p);
return *this;
}
4,在一个类的继承体系中,没有把基类的析构函数声明为虚函数
下面是一个类的继承结构
class Base{
public:
~Base(){
cout<<"delete base."<<endl;
};
};
class Derived : public Base{
public:
~Derived(){
cout<<"delete Derived."<<endl;
};
};
int main(){
Base *p;
p = new Derived;
delete p;
return 0;
}
输出结果
delete base.
Process returned 0 (0x0) execution time : 0.006 s
Press any key to continue.
程序分析
基类的析构函数不是虚函数,定义一个基类指针,实际指向的是一个派生类对象,在派生类的生命周期结束时,我们期待的是调用派生类的析构函数,但是实际调用的是基类的虚构函数,此时会导致一个派生类对象的基类对象部分被释放掉,但是派生类有拥有的成员不会被释放掉。
二,空指针与野指针
1,空指针
空指针不指向任何实际的对象或函数,得到空指针最直接的办法是使用预定义的NULL,如果要初始化一个空指针可以这样做
int *p = NULL;
C/C++中预定义的NULL,值为0
// Define NULL pointer value
#ifndef NULL
# ifdef __cplusplus
# define NULL 0
# else
# define NULL ((void *)0)
# endif
#endif // NULL
2,野指针
野指针不是空指针,是指向垃圾内存的指针,下面是造成野指针的常见原因
(1),指针变量没有被初始化
任何指针刚刚创建时不会被初始化为NULL,它的缺省值是随机的。所以,指针变量在创建的同时应该进行初始化,要么将指针设置为NULL,要么指向一个有效的内存空间。
int *p = NULL;
char *ch = new char;
(2),指针被free或delete后,没有设置为NULL,让人误任务这是一个合法的指针
free或delete只是把指针指向的内存给释放掉,此时的指针依然指向原来的位置,此时这个指针指向的内存就是垃圾内存。此时的指针不是NULL,也不指向有效的地址内存,在做如下指针检验时,会逃过检验
int main(){
char *p = new char;
delete p;
if(p == NULL){
cout<<"p == NULL"<<endl;
} else {
cout<<"p != NULL"<<endl;
}
return 0;
}
输出结果
p != NULL
Process returned 0 (0x0) execution time : 0.017 s
Press any key to continue.
(3),指针指向一个被释放的对象
指针指向一个临时变量
class People{
public:
void say(){
cout<<"hello"<<endl;
}
};
int main(){
People *p;
{
People obj;
p = &obj;
}
p->say();
return 0;
}