C++日记——Day29:new、delete探秘,智能指针概述、shared_ptr基础

一、new / delete探秘

1、new,delete是什么

sizeof关键字,不是一个函数。

new,delete关键字,不是函数。

malloc,free主要用于C语言,而new,delete用于C++编程,这两对都用于动态的在堆中分配和释放内存。

new,delete会调用类的构造与析构函数,malloc与free比new和delete更底层。new / delete具备对 堆上所分配内存进行初始化 / 释放 的能力,而这些能力是 malloc/free所不具备的。

 

2、operator new() 和 operator delete()函数:

new干了两个事情:a、分配内存(通过operator new()来分配内存) b、调用构造函数来初始化内存

void *mypoint = operator new(100); //这种写法虽然不报错,但是很不常见

delete也干了两个事:a、调用析构函数  b、释放内存(调用 operator delete()来释放内存)

 

3、基本new 符合记录分配内存大小供delete使用

int *p = new int(100);
delete p; // 删除的时候,编译器怎么知道要回收的是4字节;new内部有记录机制

 

class A {};

A a;
int len = sizeof(a);  //1字节 , 类对象肯定有地址并且至少占1个字节的空间



class A {
public:
    A() {}
    ~A() {}
};

A a;
int len = sizeof(a);  //1字节,成员函数属于类不占用类对象内存


A *pa = new A[2] (); //这里分配6个字节
int *pi = new int[2]; //这里分配8个字节

为什么动态给类型A分配内存对象时多出来4个字节,而给 内置类型int动态分配内存对象数组时并没有多出来4个字节?

多出来的4个字节实际上记录的是在类数组中存放的元素数量,delete时会根据这个数据去找到要析构的对象,而内置类型不存在析构。

int *pi = new int[3]; //这里分配了12字节,说明系统并没有多分配出4字节
delete pi; //没有用[]似乎也可以直接释放pi这个int数组
delete[] pi; //这种方式是规范的


A *pa = new A[2];
delete pa;   //异常错误,规范写法
delete[] pa;

 

为什么 new/delete、new[] / delete [] 要配对使用?

内置类型比如int类型不需要调用析构函数,所以new []的时候系统没有多分配出4字节;

对于int类型 new[]  ,delete p或者 delete [] p效果一样;

如果一个对象使用new[]来分配内存,却用单独的delete(而不是delete [])来释放内存,那么这个对象需要满足的条件:这个对象类型要么是内置类型或者是无自定义的析构函数类类型。

class A {
public:
    A() {}
};

A *pa = new A[2]; //这里分配了2个字节没有多分配4个字节

当类没有自定义析构函数时就不会为类数组多分配记录对象数量的存储空间。

 

为什么自己一提供析构函数,不用delete[] 来释放new[] 就会报异常。

a、第一次会正常调用析构函数而不是2次

b、调用operator delete(pa);释放内存

new的东西也不要用delete[] 来释放,否则会引起错乱;程序出错,自食恶果。

 

智能指针概述

裸指针:直接用new返回的指针。这种指针强大,灵活,但是开发者需要全程负责维护,一不小心就容易出错,一旦用错将造成重大问题。

智能指针:解决裸指针可能代码的各种问题。

智能指针就理解成“裸指针”进行包装,给裸指针外边包了一层;包装后的优点:

1、智能指针能够“自动释放所指向的对象”,大家不用再担心自己new出来的内存忘记释放了;

建议优先选择智能指针。使用智能指针的程序更容易编写和调试。

C++标准库有四种智能指针:std::

1、auto_ptr(C++98);  unique_ptr(C++11); shared_ptr(C++11);  weak_ptr(C++11);

帮助我们进行动态分配对象的声明周期管理。能够有效防止内存泄漏。

目前auto_ptr已经完全被 unique_ptr取代,C++11中反对使用auto_ptr(弃用);

这三种指针都是类模板。我们可以将new获得的地址赋给他们;

shared_ptr:共享式指针。多个指针指向同一个对象,最后一个指针被销毁时,这个对象会被释放。weak_ptr是辅助shared_ptr工作的。

unique_ptr:独占式指针;同一时间只有一个指针能够指向该对象。当然,该对象的所有权还是可以移交出去。

你忘记delete的时候,智能指针帮你delete,智能指针的本分就是帮你delete。

 

 

shared_ptr基础

共享所有权,不是被一个shared_ptr拥有,而是被多个shared_ptr之间相互协作。shared有额外开销。

工作原理:引用计数,每个shared_ptr的拷贝都指向相同的内存。所以只有最后一个指向该内存(对象)的shared_ptr指针不需要再指向该对象时,那么这个share_ptr才会析构所指向的对象。

最后一个指向该内存对象的shared_ptr在什么情况下,会释放该对象呢?

1、这个shared_ptr被析构的时候;

2、这个shared_ptr指向其他对象时;

类似于垃圾回收机制,我们不用再担心对象合适被delete

类模板,用到<>,<>里,就是指针可以指向的类型,后边再跟智能指针名;

shared_ptr<int> pi(new int(100)); //pi指向一个值为100的int型数据
shared_ptr<int> pi2 = new int(200); //不可以,智能指针是explicit,不可以进行隐式类型转换,必须直接初始化形式


shared_ptr<int> make(int value){
    return new int(value);   //不可以,无法把new得到的int *转换成shared_ptr
    return shared_ptr<int>(new int(value))
}

shared_ptr<int> pi3 = make(100);

裸指针可以初始化shared_ptr,但是不推荐,智能指针不要穿插用,否则会出问题。

 

make_shared函数:标准库里函数模板,安全,高效的分配和使用sahred_ptr;

它能够在动态内存中,分配并初始化一个对象,然后返回指向此对象的shared_ptr;

share_ptr<int> p2 = make_shared<int>(100);
share_ptr<string> p3 = make_shared<string>(5, 'a'); //类似于string mystr(5, 'a')

share_ptr<int> p4 = make_shared<int>();
p4 = make_shared<int>(300); //指向新int,p4首先释放指向值为0的内存,然后指向这个新的400的内存

auto p5 = make_shared<string>('I love you')

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章