zoukankan      html  css  js  c++  java
  • 1.控制内存分配的new和delete等

    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;
    	}
    
    
  • 相关阅读:
    Delphi DataSnap入门操作,动起来
    Delphi 记录Record和字符串String相互赋值
    转载:JAVA每天学习
    转载:IntelliJ IDEA 的使用方法总结
    合并多个txt
    如何用vosviewer进行时间线分析——结合pajek
    链路预测(一)
    【js】百分比保留两位小数
    【基础】float保留两位小数
    【js】鼠标悬停显示信息
  • 原文地址:https://www.cnblogs.com/ccpang/p/12185998.html
Copyright © 2011-2022 走看看