一、new / delete探秘
1、new,delete是什么
sizeof关键字,不是一个函数。
new,delete关键字,不是函数。
malloc,free主要用于C语言,而new,delete用于C++编程,这两对都用于动态的在堆中分配和释放内存。
new,delete会调用类的构造与析构函数,malloc与free比new和delete更底层。new / delete具备对 堆上所分配内存进行初始化 / 释放 的能力,而这些能力是 malloc/free所不具备的。
#include <iostream>
#include <vector>
using namespace std;
class A
{
public:
A()
{
cout <<"A构造"<<endl;
}
~A()
{
cout <<"A的析构函数" << endl;
}
};
int main()
{
A *pa = new A();//构造函数被调用了
delete pa; //会调用析构函数
return 0;
}
2、operator new() 和 operator delete()函数:
int *pi =new int;
delete pi;
new干了两个事情:a、分配内存(通过operator new()来分配内存) b、调用构造函数来初始化内存
void *mypoint = operator new(100); //这种写法虽然不报错,但是很不常见
delete也干了两个事:a、调用析构函数 b、释放内存(调用 operator delete()来释放内存)
基本new如何记录分配的内存大小工delete使用
不同的编译器new内部有不同的使用方法
int *p = new int; //4个字节
delete p; //删除的时候,编译器怎么知道要回收的是4个字节:new内部有记录机制
#include <iostream>
#include <vector>
using namespace std;
class A
{
public:
A()
{
cout <<"A构造"<<endl;
}
~A()
{
cout <<"A的析构函数" << endl;
}
};
int main()
{
return 0;
}
main:
申请和释放一个组
int *p1 = new int(100); 分配4个字节,给int = 100;
int *p2 = new int [2] ; int数组,有两个元素,如果没delete泄露8个字节
delete p1;
delete []p2;
A a;
int ilen = sizeof(a) ;类对象肯定有地址,至少占一个字节的地址。‘
cout << ilen << endl; 结果为1
有构造函数和析构函数也是占一个字节。因为构造函数和析构函数(成员函数)不占用类对象的内存
加一个成员变量大小就会变成4 如: int m_b;
A *pa = new A(); 泄露一个字节
A *pa2 = new A[2](); 泄露6个字节,而不是2个字节。多出了4个字节
int *p = new int[2] ; int 数组有两个字节,泄露8个字节,没有多出4个字节
疑问:为什么动态给类项A分配内存对象数组的时候多出4个字节,而内置类型int动态分配内存对象时没有多出4个字节?
int *p2 = new int [2] ; int数组,有两个元素,如果没delete泄露8个字节
delete p2;使用这个来释放也可以,系统没报错
delete []p2; 合规是使用这种析构的方法。
A *pa2 = new A[2](); 泄露6个字节,而不是2个字节。多出了4个字节
这种对类对象数组使用 delete pa2;系统就会报错。
必须使用 delete [] pa2;
现在把类A的析构函数删掉:
A *pa2 = new A[2](); 现在泄露2个字节了
这种对类对象数组使用 delete pa2;系统不会报错。
也可以使用 delete [] pa2;
所以:多出来的4个字节,就是记录有几个A对象,来构造几次,释放几次。
保存数组的大小内置类型int数组,不需要析构函数。同理,如果一个类,无自定义的析构函数,也可以不用写[]
一定要配对使用,不配对使用,会出很对问题。 如果是mew A(); 然后 delete[] ,这样也会出错。
出错的主要原因,就是那4个字节的空间。
智能指针总述
智能指针:理解成对“裸指针”进行包装,给裸指针外边包了一层;包装后带来优点
最突出的优点:智能指针 能够“自动释放所指向的对象内存”
有四种智能指针:
auto_ptr(c++98)
c++11: unique_ptr shared_ptr weak_ptr
c++11反对使用auto_ptr ,这个不好用
shared_ptr 是共享式指针。多个指针指向同一个对象,最后一个指针被销毁时,这个对象释放
weak_ptr 是辅助shared_ptr工作的
unique_ptr :独占式指针,同一个时间内,只有一个指针能够指向该对象,当然,该对象的所有权还是可以移交出去的。
忘记delete的时候,智能指针 帮助你delete
shared_ptr基础
共享所有权,不是被一个shared_ptr拥有,而是被多个shared_ptr之间相互协作
有额外的开销
工作原理:引用计数,每个shared_ptr的拷贝都指向相同的内存,
所以,只有最后一个指向该内存的shared_ptr指针不需要再指向该对象时,就会析构。
释放的时机:a)这个shared_ptr被析构的时候 b)这个shared_ptr指向其他的对象时。
类模板,用到<> <>里,就是指针可以指向的类型,后边再跟智能指针名。
格式:shared_ptr<指向的类型名> 智能指针名;
shared_ptr<int> pitr;
指向int的智能指针,名字为pitr,但是目前为空,空指针,nullptr
常规的初始化:new 和 shared_ptr进行配合
shared_ptr <int> pi (new int(100)); 这是对的
shared_ptr <int> pi2 =new int (100); //不进行隐式类型转换,这种就是错误的
shared_ptr<int> newABlock(int value)
{
错误,无法把new得到的int*转换成shared_ptr
// return new int(vlaue);
return shared_ptr <int> (new int(value));
}
裸指针可以初始化shared_ptr,但不推荐,智能指针和裸指针不要穿插使用。
int *pi2 = new int;
shared_ptr <int> p1 (pi2);不推荐,容易出问题
make_shared函数
标准库里的函数模板,安全高效的分配和使用shared_ptr。
它能够在动态内存(堆)中,分配并初始化一个对象,然后返回指针指向对象的shared_ptr
#include <iostream>
#include <vector>
#include <memory> // shared_ptr
using namespace std;
int main()
{
shared_ptr <int> p = make_shared<int> (100);
//这个shared_ptr指向一个值为100的整型内存,有点类似于int *p = new int(100);
shared_ptr <string> pstr = make_shared<string> (5,'a');
//5个字符a生成的字符串 类似于string pstr (5,'a');
shared_ptr<int> p2 = make_shared<int> ();//p2指向一个int,int里面保存的值是0
p2 = make_shared<int> (100);//p2指向一个新int,int里保存的是100.
//p2首先释放刚才指向的值为0 的内存,然后指向新的分配的内存。
auto p5 = make_shared<int> (100);//使用auto更加方便。
//make_shared<> 也有一定的局限性,没有办法自定义删除器,以后会讲的。
return 0;
}