16.1 C++智能指针-new/delete探秘

16.1 C++智能指针-new/delete探秘
16.2 C++智能指针-shared_ptr
16.3 C++智能指针-weak_ptr
16.4 C++智能指针-shared_ptr使用场景、陷阱、性能分析与使用建议
16.5 C++智能指针-unique_ptr

    智能指针的引入正是为了防止无意之间写出有内存泄漏的程序。内存释放的工作将交给智能指针来完成,在很大程度上能够避免程序代码中的内存泄漏。

1.new/delete探秘

  • new/delete是什么
        new/delete和malloc/free最明显的区别之一就是使用new生成一个类对象时系统会调用该类的构造函数,使用delete删除一个类对象时系统会调用该类的析构函数(释放函数)。既然有调用构造函数和析构函数的能力,这就意味着new和delete具备针对堆所分配的内存进行初始化(把初始化代码放在类的构造函数中)和释放(把释放相关的代码放在类的析构函数中)的能力,而这些能力是malloc和free所不具备的。
  • operator new()和operator delete()
        new运算符做了两件事:①分配内存;②调用构造函数初始化该内存。
        delete运算符也做了两件事:①调用析构函数;②释放内存。
  • new如何记录分配的内存大小供delete使用
        不同编译器的new内部都有不同的实现方式
  • 申请和释放一个数组
        为数组动态分配内存时,往往需要用到[],如new[…],而释放数组时,往往也要用到[],如delete[…],这意味着,往往new[]和delete[]要配套使用。
  • 为什么new/delete、new[]/delete[]要配对使用
        如果一个对象,是用new[]分配内存,而却用delete(而不是delete[])来释放内存,那么这个对象满足的条件是:对象的类型是内置类型(如int类型)或者是无自定义析构函数的类类型。
A * pA = new A[2];    //这里不再分配6字节,而是2字节
delete pA;            //这里不再出现内存泄漏,也不再报异常

    那事情反过来看,如果类A书写了自己的析构函数,则用new[]为对象数组分配内存,而用单独的delete来释放内存,就会报异常。

A * pA = new A[2];    //这里分配6字节
delete pA;            //这里报异常 为什么异常?

    报异常的原因是,代码行“delete pA;”做了两件事:
    (1)调用一次类A的析构函数。new的时候创建的是两个对象,调用的是两次构造函数,而释放的时候调用的是一次析构函数,虽然不致命,但也存在后遗症(如类A的构造函数中如果分配了内存,指望在析构函数中释放内存,那么如果少执行一次析构函数,就会直接导致内存的泄漏)
    (2)调用“operator delete(pA);”来释放内存。系统所报的异常,其实就是执行这行代码的调用导致的。就是因为多分配这4字节的问题导致释放的内存空间错乱。例如,明明应该释放一个0x00000012作为开始地址的内存,因为内存空间错乱,导致释放了0x00000016作为开始地址的内存,从而导致出现异常。
    所以,new/delete、new []/delete[]要配对使用,否则程序运行出错。

#include <iostream>
#include <vector>
using namespace std;   //后面再使用诸如std::cout时就可以简写成cout了;
class A
{
public:
    A()
    {
        cout << "A" << endl;
    }
    int m_i;
};
int main()
{
    {
        int* pointi = new int; //pointi指向一个int对象
        string* mystr = new string;                
    }
    {
        int* pointi = new int(100); //跟踪调试,指针指向的值变成了100
        string* mystr2 = new string(5, 'a'); //生成5个a的字符串,调用的是符合给进去的参数的string构造函数来构造出合适的字符串内容
        vector<int>* pointv = new vector<int>{ 1,2,3,4,5 }; //一个容器对象,里面有5个元素,分别是1,2,3,4,5
    }
    {
        string* mystr2 = new string(); //“值初始化”,感觉和string *mystr = new string;效果一样,总之最终字符串内容为空("")
        int* pointi3 = new int(); //值被初始化为0, 这个()加与不加确实不一样,只有加了()值才会被初始化为0    
    }
    {
        A* pa1 = new A;
        A* pa2 = new A();
    }
    {
        string* mystr2 = new string(5, 'a');
        const char* p = mystr2->c_str();
        auto mystr3 = new auto(mystr2); //注意这种写法,mystr3会被推断成string *类型
        //string** mystr3 = new (string *)(mystr2);        
        delete mystr2;
        delete mystr3;                
    }
    {
        const int* pointci = new const int(200); //new后面这个const可以不写,似乎都差不多;当然const对象不能修改其值
        //*pointci = 300;  //不合法        
        cout << "断点放到这里方便观察" << endl;
        delete pointci; //new的内存不能忘记释放
    }
    {
        char* p = nullptr;
        delete p;
        delete p;
    }
    {
        int i;
        int* p = &i;
        //delete p; //不是new出来的不能delete,否则编译不报错,但执行时会出现异常
    }
    {
        int* p = new int();
        int* p2 = p;
        delete p2; //没问题
        //delete p; //异常,因为p和p2指向同一块内存,该内存已经通过delete p2释放了,所以两个指针指向同一块内存这种,也比较麻烦,释放了p就不能再释放p2,释放了p2就不能再释放p,换句话说,如果释放了p2,也就不能再使用p
    }
    {
        const int* pci = new const int(300);
        delete pci; //可以delete const对象
    }
    {
        int* pci = new  int(300);
        delete pci; //可以delete const对象
        *pci = 900;
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值