访问空指针是非常危险的,因为它可能使您的程序崩溃。始终要确保您不是 在访问空指针。
没有躲过的坑--指针(内存泄露)
C++被人骂娘最多的就是指针。
夜深人静的时候,拿出几个使用指针容易出现的坑儿。可能我的语言描述有些让人费劲,尽量用代码说话。
通过指向类的NULL指针调用类的成员函数
试图用一个null指针调用类的成员函数,导致崩溃:#include using namespace std;class A{int value;public:void dumb() const{cout << 'dumb()\n';}void set(int x){cout << 'set()\n'; value=x;}int get() const{cout << 'get()\n'; return value;}};int main(){A *pA1 = new A;A *pA2 = NULL;pA1->dumb();pA1->set(10);pA1->get();pA2->dumb();pA2->set(20);//崩溃pA2->get();return 0;}
为什么会这样?
通过非法指针调用函数,就相当于给函数传递了一个指向函数的非法指针!
但是为什么pA2->dumb()会成功呢?
因为导致崩溃的是访问了成员变量!!
使用已经释放的指针struct X{int data;};int foo(){struct X *pX;pX = (struct X *) malloc(sizeof (struct X));pX->data = 10;free(pX);...return pX->data;}
使用未初始化的指针
如果你这样写,编译器会提示你使用了未初始化的变量p。void fooA(){int *p;*p = 100;}
那么如果我释放一个初始化的指针呢?void fooB(){int *p;free(p);}
结果是一样的!!
释放已经释放的指针
直接看看代码:void fooA(){char *p;p = (char *)malloc(100);cout << 'free(p)\n';free(p);cout << 'free(p)\n';free(p);}
这样的问题也许不会立即使你的程序崩溃,那样后果更加严重!!
没有调用子类的析构函数
之前的博客讲过,父类的析构函数最好声明为虚!!ParentClass *pObj = new ChildClass;...delete pObj;
上述代码会造成崩溃,如果父类的析构函数不声明为虚,那么不会调用继承类的析构函数,造成内存泄露。
内存溢出
当我们拷贝字符串的时候,我们常常会用到 memcpy函数。这里特别需要注意的就是字符串结尾的null字符:char *p = (char *)malloc(strlen(str));strcpy(p, str);
为了躲过这个坑,只需要把 strlen(str) 改为 strlen(str)+1。
记一次指针使用不当造成的内存泄露
刚写完一段代码,由于将很运行在移动设备上, 我决定先测试一下内存的使用量, 结果发现了很严重的内存泄漏, 在前前后后翻看了new 和delete并确认没有漏写的情况下, 泄露依然存在!调试后最终确认了问题是因为union的不当使用造成的, 下面开始还原现场:typedef struct DATA_{DATA_(int size = 10){pVoid = new char[nSize];this->size = size;}virtual ~DATA_(){if (pVoid){delete pVoid;}}int nSize;void *pVoid;}DATA, *LPDATA;struct STRUCT1{STRUCT1(int count){pVoid = new DATA[count];this->count = count;}virtual ~STRUCT1(){if (pVoid){delete pVoid;}}int count;union{void *pVoid;LPDATA pData;}}int main(int argc, char* argv[]){for( int i = 0; i < 1000; i++){void *p = new STRUCT1( 10 );delete p;}return 0;}
在上面这段代码中,STRUCT1中包含了若干个DATA结构,DATA结构又申请了默认为10byte大小的内存,并且内存在对象析构的时候会用delete回收。乍一看这个代码貌似不会泄露内存,其实不然,待我分析:
在C++中构造函数与析构函数的调用是由编译器完成的,其中构造函数的调用一般是在对象空间开辟完以后(栈对象或者堆对象都是一样的),将参数压栈,this指针(对象的起始地址)放入eax寄存器(不同的编译器做法可能不同),然后跳到构造函数去执行。而析构函数的调用则是当对象内存被回收的时候被调用(栈对象是当代码执行到变量可见域外之前调用, 堆对象是在delete语句的位置进行调用)。
然而编译器并是那么智能的可以理解coder的意图,析构函数的调用是根据当前delete的指针类型来确定的,而(下面)这段代码却没有提供类型, 这导致了DATA_的析构函数将不会被调用,内存泄漏就在所难免了。virtual ~STRUCT1(){if (pVoid){delete pVoid;//问题在这, 应该使用delete pData;}}