#include <iostream>
using namespace std;
class Point{
public:
Point(int x, int y, int z):x(x),y(y),z(z){
cout<<"ctor"<<endl;
}
~Point(){ cout<<"dtor"<<endl;}
virtual void print(){
cout<<x<<" "<<y<<" "<<z<<endl;
}
int x;
int y;
int z;
};
int main()
{
cout<<sizeof(Point)<<endl;
void* memory = std::malloc(sizeof(Point));
long *p=(long *)memory;
std::cout<<*p<<std::endl;//第一个位置放的是虚函数表指针
p++;
int* p1=(int*)p;
for(int i=0;i<3;i++)
{
cout<<*p1<<" ";
p1++;
}
//在指定内存位置构建对象Point
Point* myObject = ::new (memory) Point[{100,200,300}];
myObject->print();
long* p=(long*)memory;
cout<<*p<<endl;
p++;
int* pi=reinterpret_cast<int*>(p);
cout<<*pi++<<endl;
cout<<*pi++<<endl;
cout<<*pi<<endl;
myObject->~Point();
std::free(memory);
// int data=100;
// double dvalue=234.323;
//int value =(int)dvalue C风格转换,不推荐了
// static_cast:相关类型转型可能造成内存位的丢失
// int value=static_cast<int>(dvalue);
// static_cast 子类指针可以直接赋值给父类指针是兼容的,但是
// 也可以使用static_cast 来强调子类转型到了父类
// //dynamic_cast: 多态转型 , 基类必须有虚函数(虚析构函数)
// Sub s1;
// Base& base=s1;
// Sub& s=dynamic_cast<Sub&> (base);
// //reinterpret_cast: 二进制一致性转型
// //const_cast: 去常量性
//标准库针对placement_new 做了更好用的封装如下
void* memory = std::malloc(3*sizeof(Point));
Point* myObject = reinterpret_cast<Point*>(memory);
std::uninitialized_fill_n(myObject, 3, Point{100,200,300});
//3表示写几次,3次那么就要预先申请3倍的内存空间
std::destroy_at(myObject);
std::free(memory);
//void* 转到其他类型的指针也可以使用static_cast 也可以 reinterpret_cast
//建议统一使用reinterpret_cast
}
//placement_new 常使用的场景,大量小对象的频繁开辟销毁,造成
//内存碎片
关于operator new operator delete重载
类内部operator_new operator_delete
#include <iostream>
using namespace std;
//void* T::operator new ( std::size_t count );
//void* T::operator new[]( std::size_t count );
class MyClass{
public:
MyClass(const string& s):_s(s){}
MyClass(){ cout<<"ctor"<<endl;}
~MyClass(){ cout<<"dtor"<<endl;}
void process(){
cout<<_s<<endl;
}
void* operator new(size_t size) {
cout<<"class new "<<endl;
data++;
//data+=size;
return ::operator new(size); 调用全局new
}
void operator delete(void* p) {
cout<<"class delete "<<endl;
::operator delete(p);
data--;
}
static int data;
private:
string _s;
};
int MyClass::data=0;
int main()
{
MyClass* myObject = new MyClass{"Software"};
myObject->process();
delete myObject;
cout<<"-----------"<<endl;
MyClass* p = ::new MyClass{}; //绕过自定义类内部的operator new 使用全局的 operator new
::delete p;
}
全局operator_new operator_delete
#include <iostream>
#include <vector>
using namespace std;
int alloc_times = 0;
int dealloc_times = 0;
int allocated = 0;
class MyClass{
private:
double x,y,z;
};
void* operator new(size_t size) {
void* p = std::malloc(size);
//std::cout << "allocated " << size << " byte(s)\n";
allocated+=size;
alloc_times++;
return p;
}
void operator delete(void* p) noexcept {
//std::cout << "deleted memory\n";
dealloc_times++;
return std::free(p);
}
void* operator new[](size_t size) {
void* p = std::malloc(size);
allocated+=size;
alloc_times++;
std::cout << "allocated " << size << " byte(s) with new[]\n";
return p;
}
void operator delete[](void* p) noexcept {
std::cout << "deleted memory with delete[]\n";
dealloc_times++;
return std::free(p);
}
int main()
{
{
MyClass* v=new MyClass[10];//24*10=240
cout<<allocated<<endl;
for(int i=0;i<10;i++)
{
MyClass* pc=new MyClass();//10
delete pc;
}
delete[] v;
}
//MyClass* p = ::new MyClass{};
//::delete p;
cout<<allocated<<endl;
cout<<alloc_times<<endl;
cout<<dealloc_times<<endl;
}
全局operator_new operator_delete 结合share_ptr weak_ptr一些有意思的代码 1.1
#include <iostream>
#include <memory>
using namespace std;
class MyClass{
public:
MyClass(){ cout<<"MyClass()构造函数"<<endl;}
~MyClass(){ cout<<"~MyClass()析构函数"<<endl;}
private:
double x,y,z;
};
int alloc_times = 0;
int dealloc_times = 0;
int allocated = 0;
void* operator new(size_t size) {
void* p = std::malloc(size);
cout << "operator new " << size << " byte(s)"<<endl;
allocated+=size;
alloc_times++;
return p;
}
void operator delete(void* p) noexcept {
cout << "operator deleted memory"<<endl;
dealloc_times++;
return std::free(p);
}
int main(){
//weak_ptr<MyClass> w;
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
{
shared_ptr<MyClass> s{new MyClass()};
//shared_ptr<MyClass> s = std::make_shared<MyClass>();
//w=s;
}
//1. s析构、释放原始对象、但不释放控制块
cout<<"block end--------"<<endl;
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
//w.reset(); //2. w析构、释放控制块
//cout<<"分配尺寸:"<<allocated<<endl;
//cout<<"分配次数"<<alloc_times<<endl;
//cout<<"释放次数"<<dealloc_times<<endl;
}
//运行结果如下
/*
分配尺寸:0
分配次数0
释放次数0
operator new 24 byte(s)
MyClass()构造函数
operator new 24 byte(s)
~MyClass()析构函数
operator deleted memory
operator deleted memory
block end--------
分配尺寸:48
分配次数2
释放次数2
//我们来对上面运行结果进行分析
智能指针创建方式: shared_ptr<MyClass> s{new MyClass()};
运行结果中第一个 operator new 24 byte(s) 这24个字节是创建 new MyClass()的时候发生空间开辟动作, 然后紧接着调用MyClass的构造函数
第二个 operator new 24 byte(s) 是给谁的??
我们再上一篇内容 <三>智能指针之__sharePtr 第一张图揭示了 sharePtr的内存结构
sharePtr 中有两条指针, 第一条指向MyClass()对象,这个对象中有三个double成员,所以消费了24个字节空间
第二条指针指向了控制块,那控制块对象有哪些成员呢?控制块对象又需要多少个字节存储空间呢?首先运行结果是24个字节空间
我们下面通过跟踪源码 看一下 控制块的的内部结构,如下图 , 看完下图就知道这24个字节其中一个是指向原对象的指针,
另外两个是long 类型的计数器
当 shared_ptr<MyClass> s{new MyClass()}; 出{}作用域后 析构了两次,一个是MyClass的,还有一个是控制块的
接下来我们将上面代码中的//都去掉
主要关注点
1: //weak_ptr<MyClass> w;
2: //w=s;
3:
//w.reset(); w.reset()开和不开 两种不同结果,看看不开的情况 //2. w析构、释放控制块
//cout<<"分配尺寸:"<<allocated<<endl;
//cout<<"分配次数"<<alloc_times<<endl;
//cout<<"释放次数"<<dealloc_times<<endl;
见下面代码 1.2
*/
全局operator_new operator_delete 结合share_ptr weak_ptr一些有意思的代码 1.2
#include <iostream>
#include <memory>
using namespace std;
class MyClass{
public:
MyClass(){ cout<<"MyClass()构造函数"<<endl;}
~MyClass(){ cout<<"~MyClass()析构函数"<<endl;}
private:
double x,y,z;
};
int alloc_times = 0;
int dealloc_times = 0;
int allocated = 0;
void* operator new(size_t size) {
void* p = std::malloc(size);
cout << "operator new " << size << " byte(s)"<<endl;
allocated+=size;
alloc_times++;
return p;
}
void operator delete(void* p) noexcept {
cout << "operator deleted memory"<<endl;
dealloc_times++;
return std::free(p);
}
int main(){
weak_ptr<MyClass> w;
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
{
shared_ptr<MyClass> s{new MyClass()};
//shared_ptr<MyClass> s = std::make_shared<MyClass>();
w=s;
}
//1. s析构、释放原始对象、但不释放控制块
cout<<"block end--------"<<endl;
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
//w.reset(); //2. w析构、释放控制块
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
}
//运行结果如下
/*
分配尺寸:0
分配次数0
释放次数0
operator new 24 byte(s)
MyClass()构造函数
operator new 24 byte(s)
~MyClass()析构函数
operator deleted memory
block end--------
分配尺寸:48
分配次数2
释放次数1
分配尺寸:48
分配次数2
释放次数1
operator deleted memory
//我们来对上面运行结果进行分析
智能指针创建方式: shared_ptr<MyClass> s{new MyClass()};
运行结果中第一个 operator new 24 byte(s) 这24个字节是创建 new MyClass()的时候发生空间开辟动作, 然后紧接着调用MyClass的构造函数
第二个 operator new 24 byte(s) 是给控制块的,这个我们再代码1.1已经学习到了
我们再没有打开 //w.reset(); 这个代码的情况下,居然只析operator_delete了一次,这一次就是给MyClass的内存释放用的
为什么控制块的堆内存没能得到正常的operter_delete? 这个不是内存泄漏吗?确实属于内存泄漏,原因在于
weak_ptr<MyClass> w;
..
..
w=s;
我们用一个弱引用挂住了share_ptr
w=s; 会使得弱引用计数 +1 见下面第一张图
我们现在通过跟踪源码看看为什么operator_delete一次,见下面第二张图
通过看完下面第二张图,我知道了出来share_ptr作用域 对象应用计数==0所以对象delete
但是还有一个w=s挂着弱引用,使得弱引用计数不为0,到时弱引用计数指针指向的堆无法被释放
即内存泄漏!!
*/
接下来我们看代码1.3 我们激活 w.reset(); //2. w析构、释放控制块
第一张图
第二张图
全局operator_new operator_delete 结合share_ptr weak_ptr一些有意思的代码 1.3
#include <iostream>
#include <memory>
using namespace std;
class MyClass{
public:
MyClass(){ cout<<"MyClass()构造函数"<<endl;}
~MyClass(){ cout<<"~MyClass()析构函数"<<endl;}
private:
double x,y,z;
};
int alloc_times = 0;
int dealloc_times = 0;
int allocated = 0;
void* operator new(size_t size) {
void* p = std::malloc(size);
cout << "operator new " << size << " byte(s)"<<endl;
allocated+=size;
alloc_times++;
return p;
}
void operator delete(void* p) noexcept {
cout << "operator deleted memory"<<endl;
dealloc_times++;
return std::free(p);
}
int main(){
weak_ptr<MyClass> w;
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
{
shared_ptr<MyClass> s{new MyClass()};
//shared_ptr<MyClass> s = std::make_shared<MyClass>();
w=s;
}
//1. s析构、释放原始对象、但不释放控制块
cout<<"block end--------"<<endl;
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
w.reset(); //2. w析构、释放控制块
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
}
//运行结果如下
分配尺寸:0
分配次数0
释放次数0
operator new 24 byte(s)
MyClass()构造函数
operator new 16 byte(s)
~MyClass()析构函数
operator deleted memory
block end--------
分配尺寸:40
分配次数2
释放次数1
operator deleted memory
分配尺寸:40
分配次数2
释放次数2
在w.reset()之前释放次数为1,在w.reset()之后dealloc_times又加了1,这样释放次数就为2 了,
所以在w.reset()之前,原对象已经正常释放了,在w.reset()之后控制块又正常释放了
------------------------
接下来 我们 将代码中的
shared_ptr<MyClass> s{new MyClass()};
换成 shared_ptr<MyClass> s = std::make_shared<MyClass>(); 看看 见代码1.4
全局operator_new operator_delete 结合share_ptr weak_ptr一些有意思的代码 1.4
#include <iostream>
#include <memory>
using namespace std;
class MyClass{
public:
MyClass(){ cout<<"MyClass()构造函数"<<endl;}
~MyClass(){ cout<<"~MyClass()析构函数"<<endl;}
private:
double x,y,z;
};
int alloc_times = 0;
int dealloc_times = 0;
int allocated = 0;
void* operator new(size_t size) {
void* p = std::malloc(size);
cout << "operator new " << size << " byte(s)"<<endl;
allocated+=size;
alloc_times++;
return p;
}
void operator delete(void* p) noexcept {
cout << "operator deleted memory"<<endl;
dealloc_times++;
return std::free(p);
}
int main(){
weak_ptr<MyClass> w;
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
{
//shared_ptr<MyClass> s{new MyClass()};
shared_ptr<MyClass> s = std::make_shared<MyClass>();
w=s;
}
//1. s析构、释放原始对象、但不释放控制块
cout<<"block end--------"<<endl;
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
w.reset(); //2. w析构、释放控制块
cout<<"分配尺寸:"<<allocated<<endl;
cout<<"分配次数"<<alloc_times<<endl;
cout<<"释放次数"<<dealloc_times<<endl;
}
-----运行结果
分配尺寸:0
分配次数0
释放次数0
operator new 40 byte(s)
MyClass()构造函数
~MyClass()析构函数
block end--------
分配尺寸:40
分配次数1
释放次数0
operator deleted memory
分配尺寸:40
分配次数1
释放次数1
//上面的结果我有以下几个点需要关注
1:operator new()只发生了一次,即内存只开辟了一次(相比shared_ptr<MyClass> s{new MyClass()};这种方式)
2:在w.reset();之前 哪怕shared_ptr<MyClass> s = std::make_shared<MyClass>(); 出了作用域, 基础对象
都没有operator_delete, 与shared_ptr<MyClass> s{new MyClass()} 不一样,
3:w.reset()之后 operator_delete发生了一次
针对上面的关注点,我们来分析一下
1:shared_ptr<MyClass> s = std::make_shared<MyClass>(); 与shared_ptr<MyClass> s{new MyClass()};
智能指针的构建过程和方式不一样
a:shared_ptr<MyClass> s{new MyClass()} 先 new MyClass() 第二步再在堆上创建控制块对象,所以operator_new 了两次
b:shared_ptr<MyClass> s = std::make_shared<MyClass>(); 直接将MyClass对象所需内存大小+控制块内存所需大小 一次分配了,
所以只调用了一次operator_new ,大小40字节,(这种一次开辟的内存控制是无法区分MyClass和控制块分别delete的,只能一次性回收所有40个字节)
2:正是由于上面b:中描述的原因这一块大内存区域还有weak_ptr还在占用,导致share_ptr出了作用域也无法释放空间,但是w.reset()之后由于没有弱引用再使用内存了,那么整块内存就可以回收了
3:所有如果没有w.rest() 那么这个40个字节一直无法释放,相当于内存泄漏了.
哪些情况场景下,我们会用到内存分配自定义?