C++内存管理的架构
C++ memory primitives
memory primitives 测试
#include <cstdlib>
#include <iostream>
#include "memory_primitives_test.h"
#include <vector>
#include <memory>
namespace memory_primitives_test {
void memory_primitives_test_function() {
std::cout << "malloc test start" << std::endl;
void *p1 = malloc(512); //512bytes
free(p1);
std::cout << "malloc test is over" << std::endl;
//2.new的测试
std::cout << "new test start" << std::endl;
std::vector<int>* p2 = new std::vector<int>;
delete p2;
std::cout << "new test is over" << std::endl;
std::cout << "operator new test start" << std::endl;
void *p3 = ::operator new (512); //直接执行全局作用域中的operator new
::operator delete(p3);
std::cout << "operator new test is over" << std::endl;
//测试allocators,其接口虽然有标准规格,但是不同的厂商实现了不同的接口
//一般不会通过分配器直接拿内存
#ifdef _MSC_VER
std::cout << "allocator test start" << std::endl;
int *p4 = std::allocator<int>().allocate(3, (int*)0); //分配3个ints
std::allocator<int>().deallocate(p4, 3);
std::cout << "allocator test is over" << std::endl;
#endif
#ifdef __BORLANDC__
int *p4 = allocator<int>().allocate(5);
allocator<int>().deallocate(p4, 5);
#endif
#ifdef __GNUC__
void *p4 = alloc::allocate(512);
alloc::deallocate(p4, 512);
#endif
}
}
new在编译器中的执行过程
Complex *pc = new Complex(1,2);
被编译器解释成以下代码:并且只有编译器才能做到如下所示的执行过程:
Complex *pc;
try {
void *mem = operator new(sizeof(Complex));
pc = static_cast<Complex*>(mem);
pc->Complex::Complex(1,2);
}
catch (std::bad_alloc) {
//若失败,就不执行构造函数了
}
delete在编译器中的执行过程
delete pc;
被编译器解释成以下代码:并且只有编译器才能做到如下所示的执行过程:
pc->~Complex(); //先析构
operator delete(pc); //再释放内存
其中operator delete又会调用free
void__cdecl opeartor delete(void *p) _THROW0() {
//free an allocated object
free(p);
}
array new在编译器中的执行过程
string *psa = new string[3];
delete []psa;
array new得到的是绿色的部分加上一个小的cookie(记录一些琐碎的东西,比如数组的长度等)
如上图所示,在 $ delete $时如果没有加上 $ [ ] $,回收还是正常回收的,但是析构函数只会被调用一次,即析构的可能是数组的第一个,也可能是数组的最后一个,这会造成内存泄露。
而如果是下面这个例子:
Complex* pca = new Complex[3]; //调用3次构造函数
delete [] pca; //调用三次析构函数
其中并没有指针指向析构函数,所以它的析构函数根本没有用,在析构时是可以不用加 $ [] $ 的,是不会造成内存泄露的,但是为了格式的统一,还是要加上[]
placement new在编译器中的执行过程
placement new允许我们将object建立于allocated memory中。
没有所谓的placement delete,因为placement new根本没有分配memory,
char *buf = new char[sizeof(Complex) * 3];
Complex *pc = new(buf)Complex(1,2);
delete [] buf;
//被编译器转为
Complex *pc;
try {
void *mem = operator new(sizeof(Complex), buf);
pc = static_cast<Complex*>(mem);
pc->Complex::Complex(1, 2);
}
catch (std::bad_alloc)
{
//若分配失败,则不执行constructor
}
所以 placemenet new
或指new(p)
或指::operator new(size,void*)
C++分配内存途径
C++ 应用程序分配内存的途径
C++ 容器分配内存的途径
重载new/class::operator new/::operator new
重载全局::operator new /::operator delete
//重载::operator new /::operator delete /::operator new[] /::operator delete[]
void* myAlloc(size_t size) {
return malloc(size); //返回一个指针指向malloc的内存
}
void myFree(void *ptr) {
return free(ptr);
}
inline void * operator new(size_t size) {
std::cout << "global new()
";
return myAlloc(size);
}
inline void operator delete(void* ptr) {
std::cout << "global delete()
";
return myFree(ptr);
}
重载全局/::operator new[] /::operator delete[]
inline void* operator new[](size_t size) {
std::cout << "global new()
";
return myAlloc(size);
}
inline void operator delete[](void *ptr) {
std::cout << "global delete []
";
myFree(ptr);
}
重载类内的operator new /operator delete
必须是static的
重载类内的operator new[] /operator delete[]
必须是static
定义类Foo
重载类的operator new和operator delete 以及operator new []和operator delete[],注意重载的operator new等为static的
//重载载类内的operator new和operator delete
class Foo {
public:
int _id;
long _data;
std::string _st;
public:
Foo() : _id(0) { std::cout << "default cotr. this = " << this << " id=" << _id << std::endl; }
Foo(int i):_id(i) { std::cout << "default cotr. this = " << this << " id=" << _id << std::endl; }
~Foo() { std::cout << "dtor. this= " << this << " id= " << _id << std::endl; }
static void *operator new(size_t size);
static void operator delete(void* pdead, size_t size);
static void *operator new[](size_t size);
static void operator delete[](void*pdead, size_t size);
};
operator new等重载实现
//Foo内的operator new
void *Foo::operator new(size_t size) {
Foo* p = (Foo*)malloc(size);
std::cout << "Foo operator new" << std::endl;;
return p;
}
//Foo内的operator delete
void Foo::operator delete(void* pdead, size_t size) {
std::cout << "Foo opeartor delete" << std::endl;
free(pdead);
}
//Foo内的operator new[]
void *Foo::operator new[](size_t size) {
Foo* p = (Foo*)malloc(size);
std::cout << "Foo operator new[]" << std::endl;;
return p;
}
//Foo内的operator delete[]
void Foo::operator delete[](void* pdead, size_t size) {
std::cout << "Foo opeartor delete[]" << std::endl;
free(pdead);
}
主函数测试
std::cout << "sizeof(Foo)" << sizeof(Foo) << std::endl;
//测试operator new和operator delete
Foo *p = new Foo(7);
delete p;
//测试operator new[] 和operator delete[]
Foo* pArray = new Foo[5];
delete[] pArray;
测试结果
sizeof(Foo)36
Foo operator new
default cotr. this = 0063EE50 id=7
dtor. this= 0063EE50 id= 7
Foo opeartor delete
Foo operator new[]
default cotr. this = 006412CC id=0
default cotr. this = 006412F0 id=0
default cotr. this = 00641314 id=0
default cotr. this = 00641338 id=0
default cotr. this = 0064135C id=0
dtor. this= 0064135C id= 0
dtor. this= 00641338 id= 0
dtor. this= 00641314 id= 0
dtor. this= 006412F0 id= 0
dtor. this= 006412CC id= 0
Foo opeartor delete[]
请按任意键继续. . .
测试全局的operator new和operator delete
std::cout << "sizeof(Foo)" << sizeof(Foo) << std::endl;
//测试operator new和operator delete
Foo *p = ::new Foo(7);
::delete p;
//测试operator new[] 和operator delete[]
Foo* pArray = ::new Foo[5];
::delete[] pArray;
全局的operator new和operator delete测试结果
sizeof(Foo)36
default cotr. this = 003EEE50 id=7
dtor. this= 003EEE50 id= 7
default cotr. this = 003F12CC id=0
default cotr. this = 003F12F0 id=0
default cotr. this = 003F1314 id=0
default cotr. this = 003F1338 id=0
default cotr. this = 003F135C id=0
dtor. this= 003F135C id= 0
dtor. this= 003F1338 id= 0
dtor. this= 003F1314 id= 0
dtor. this= 003F12F0 id= 0
dtor. this= 003F12CC id= 0
请按任意键继续. . .
发现并没有进入Foo类内的operator new和operator delete。
重载new()和delete()
我们可以重载class member operator new(),写出多个版本,前提是每个版本的声明都具有不同的参数列,且其中第一个参数必须是size_t,其余参数是以new所指定的placement arguments为初值,出现new (...)小括号内的便是所谓的placement arguments。
Foo *pf = new(300,'c')Foo;
我们也可以重载class member operator delete()
,写出多个版本,但它们绝对不会被delete调用,只有当new所调用的ctor抛出exception时,才会调用这些重载的operator delete().它只可能这样被调用,主要用于未能完全创建成功的object所占用的memory.
重载new()和delete()实现
//重载new()和delete()
void* Foo::operator new(size_t size, void *start) {
return start;
}
void* Foo::operator new(size_t size, long extra) {
return malloc(size + extra);
}
void* Foo::operator new(size_t size, long extra, char init) {
return malloc(size + extra);
}
//如果调用构造函数失败,才会调用如下的构造函数
void Foo::operator delete (void*, size_t) {
std::cout << "operator delete(void*,size_t)" << std::endl;
}
void Foo::operator delete(void*, void*) {
std::cout << "operator delete(void*,void*)" << std::endl;
}
void Foo::operator delete(void*, long) {
std::cout << "operator delete(void*,long)" << std::endl;
}
void Foo::operator delete(void*, long, char) {
std::cout << "operator delete(void*,long,char)" << std::endl;
}