第六十九课、自定义内存管理
一、统计对象中某个成员变量的访问次数
解决方法一:
#include <iostream> using namespace std; class Test { private: int i; mutable int m_count; public: Test(int v) { i = v; m_count = 0; } void setI(int v) { i = v; m_count++; } int getI() const //方便const对象调用 { m_count++; return i; } int getCount() const //方便const对象调用 { return m_count; } }; int main() { const Test t(2);//只读对象 cout << t.getI() << endl;//2 cout << t.getCount() << endl;//1 return 0; }
1.遗失的关键字:mutable
(1)、mutable关键字是为了突破const函数的限制设计的
(2)、mutable关键字将永远处于可改变的状态
(3)、mutable关键字在实际项目中被严禁使用
2、mutab的深入分析
(1)、mutable破坏了只读对象内部的状态
(2)、const成员函数保证只读对象内部的状态不变性
(3)、mutable成员变量的出现无法保证状态不变性
解决方法二:
#include <iostream> using namespace std; class Test { private: int i; int * const m_count;//左数右指,const在*左边表示指针指向的地址中的数据是常量,在右说明指针本身是常量 public: Test(int v = 0) : m_count(new int(0))//const变量要在初始化列表中进行初始化 { i = v; } void setI(int v) { i = v; *m_count = *m_count + 1; } int getI() const //方便const对象调用 { *m_count = *m_count + 1;//巧妙!!!只读成员函数里并没有改变成员变量的值 return i; } int getCount() const //方便const对象调用 { return *m_count; } ~Test() { delete m_count; } }; int main() { const Test t(2);//只读对象 cout << t.getI() << endl;//2 cout << t.getCount() << endl;//1 return 0; }
二、new/delete的本质是c++预定义的操作符
1、c++对这两个操作符做了严格的行为定义
new:
(1)、获取足够大的内存空间(默认为堆空间)
(2)、在获取的空间中调用构造函数创建对象
delete:
(1)、调用析构函数销毁对象
(2)、归还对象所占用的空间(默认为堆空间)
2、c++中能够重载new/delete操作符(意义在于改变动态对象创建时的内存分配方式)
(1)、全局重载(不推荐)
(2)、局部重载(针对具体类进行重载)
3、new/delete的重载方式(都是static的)
#include <iostream> using namespace std; class Test { private: static const unsigned int COUNT = 4;//最多分配4个Test大小 static char c_buffer[];//分配空间 static char c_map[];//用来标记哪个空间可以用 int m_value; public: Test() { } void * operator new(unsigned int size) { void* ret = NULL; for(int i=0; i<COUNT; i++) { if( !c_map[i] ) { ret = c_buffer + i*sizeof(Test); c_map[i] = 1; cout << "succed to new a object:" << ret << endl; break; } } return ret; } void operator delete(void* p) { if(p != NULL) { char* mem = reinterpret_cast<char*>(p); int index = (mem - c_buffer) / sizeof(Test); int flag = (mem - c_buffer) % sizeof(Test);//要保证位置合法 if( (flag == 0) && (0 <= index) && (index < COUNT) ) { c_map[index] = 0; cout << "succed to delete a object: " << p << endl; } } } }; char Test::c_buffer[sizeof(Test)*Test::COUNT] = {0}; char Test::c_map[COUNT] = {0}; int main() { Test* pa[5] = {0}; for(int i=0; i<5; i++) { pa[i] = new Test; cout << "new " << "pa[" << i << "]=" << pa[i] << endl; } for(int i=0; i<5; i++) { cout << "delete " << "pa[" << i << "]=" << pa[i] << endl; delete pa[i]; } return 0; } //输出结果 /* succed to new a object:0x804a0d4 new pa[0]=0x804a0d4 succed to new a object:0x804a0d8 new pa[1]=0x804a0d8 succed to new a object:0x804a0dc new pa[2]=0x804a0dc succed to new a object:0x804a0e0 new pa[3]=0x804a0e0 new pa[4]=0 delete pa[0]=0x804a0d4 succed to delete a object: 0x804a0d4 delete pa[1]=0x804a0d8 succed to delete a object: 0x804a0d8 delete pa[2]=0x804a0dc succed to delete a object: 0x804a0dc delete pa[3]=0x804a0e0 succed to delete a object: 0x804a0e0 delete pa[4]=0 */
三、在指定地址上创建c++对象
1、重载new/delete
(1)、在类中重载new/delete操作符
(2)、在new操作符重载函数中返回指定地址
(3)、在delete操作符重载函数中标记对应的地址可用
#include <iostream> #include <string> #include <cstdlib> using namespace std; class Test { static unsigned int c_count; static char* c_buffer; static char* c_map; int m_value; public: static bool SetMemorySource(char* memory, unsigned int size) { bool ret = false; c_count = size / sizeof(Test); ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char))))); if( ret ) { c_buffer = memory; } else { free(c_map); c_map = NULL; c_buffer = NULL; c_count = 0; } return ret; } void* operator new (unsigned int size) { void* ret = NULL; if( c_count > 0 ) { for(int i=0; i<c_count; i++) { if( !c_map[i] ) { c_map[i] = 1; ret = c_buffer + i * sizeof(Test); cout << "succeed to allocate memory: " << ret << endl; break; } } } else { ret = malloc(size); } return ret; } void operator delete (void* p) { if( p != NULL ) { if( c_count > 0 ) { char* mem = reinterpret_cast<char*>(p); int index = (mem - c_buffer) / sizeof(Test); int flag = (mem - c_buffer) % sizeof(Test); if( (flag == 0) && (0 <= index) && (index < c_count) ) { c_map[index] = 0; cout << "succeed to free memory: " << p << endl; } } else { free(p); } } } }; unsigned int Test::c_count = 0; char* Test::c_buffer = NULL; char* Test::c_map = NULL; int main(int argc, char *argv[]) { char buffer[12] = {0}; Test::SetMemorySource(buffer, sizeof(buffer)); cout << "===== Test Single Object =====" << endl; Test* pt = new Test; delete pt; cout << "===== Test Object Array =====" << endl; Test* pa[5] = {0}; for(int i=0; i<5; i++) { pa[i] = new Test; cout << "pa[" << i << "] = " << pa[i] << endl; } for(int i=0; i<5; i++) { cout << "delete " << pa[i] << endl; delete pa[i]; } return 0; } //输出结果 /* ===== Test Single Object ===== succeed to allocate memory: 0xbfbc38f0 succeed to free memory: 0xbfbc38f0 ===== Test Object Array ===== succeed to allocate memory: 0xbfbc38f0 pa[0] = 0xbfbc38f0 succeed to allocate memory: 0xbfbc38f4 pa[1] = 0xbfbc38f4 succeed to allocate memory: 0xbfbc38f8 pa[2] = 0xbfbc38f8 pa[3] = 0 pa[4] = 0 delete 0xbfbc38f0 succeed to free memory: 0xbfbc38f0 delete 0xbfbc38f4 succeed to free memory: 0xbfbc38f4 delete 0xbfbc38f8 succeed to free memory: 0xbfbc38f8 delete 0 delete 0 */
2、new[]/delete[]和new/delete 完全不同
(1)、动态对象数组的创建通过new[]完成
(2)、动态对象数组销毁通过delete[]完成
(3)、new[]/delete[]可以被重载,进而改变内存管理方式
注意事项:
(1)、new[]返回的地址空间可能比期望的多(原因如2、3)
(2)、对象数组中占用的内存中需要保存数组信息(不然怎么知道调用多少次构造和析构函数?)
(3)、数组信息用于确定构造函数和析构函数的调用次数
#include <iostream> #include <string> #include <cstdlib> using namespace std; class Test { int m_value; public: Test() { m_value = 0; } ~Test() { } void* operator new (unsigned int size) { cout << "operator new: " << size << endl; return malloc(size); } void operator delete (void* p) { cout << "operator delete: " << p << endl; free(p); } void* operator new[] (unsigned int size) { cout << "operator new[]: " << size << endl; return malloc(size); } void operator delete[] (void* p) { cout << "operator delete[]: " << p << endl; free(p); } }; int main(int argc, char *argv[]) { Test* pt = NULL; pt = new Test; delete pt; pt = new Test[5]; delete[] pt; return 0; } //输出结果 /* operator new: 4 operator delete: 0x8463008 operator new[]: 24 operator delete[]: 0x8463018 */
四、小结
(1)、new/delete本质为操作符
(2)、可以通过全局函数重载new/delete(不推荐)
(3)、可以针对具体类重载new/delete
(4)、new[]/delete[]与new/delete 完全不同
(5)、new[]/delete[]也是可以被重载的操作符
(6)、new[]返回的地址空间可能比期望的多