C++ 内存泄露

一,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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值