關(guān)鍵詞: mutable
处渣、new
/delete
與new[]
/delete[]
操作符重載
1. 問題:統(tǒng)計對象中某個成員變量的訪問次數(shù)栽惶。
知識點補充:
mutable
關(guān)鍵字
(1)mutable
是為了突破const
函數(shù)的限制而設(shè)計的
(2)mutable
成員變量永遠處于可改變的狀態(tài)
(3)mutable
在實際的項目開發(fā)中被嚴(yán)禁濫用
mutable
的深入分析
(1)mutable
成員變量破壞了只讀對象的內(nèi)部狀態(tài)
(2)const
成員函數(shù)保證只讀對象的狀態(tài)不變性
(3)mutable
成員變量的出現(xiàn)無法保證狀態(tài)不變性
編程說明:成員變量的訪問統(tǒng)計——使用mutable
#include <iostream>
#include <string>
using namespace std;
class Test
{
int m_value;
mutable int m_count;
public:
Test(int value = 0)
{
m_value = value;
m_count = 0;
}
int getValue() const
{
m_count++;
return m_value;
}
void setValue(int value)
{
m_count++;
m_value = value;
}
int getCount() const
{
return m_count;
}
};
int main()
{
Test t;
t.setValue(100);
cout << "t.m_value = " << t.getValue() << endl;
cout << "t.m_count = " << t.getCount() << endl;
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;
cout << "ct.m_count = " << ct.getCount() << endl;
return 0;
}
輸出結(jié)果:
t.m_value = 100
t.m_count = 2
ct.m_value = 200
ct.m_count = 1
在實際項目中禁止使用
mutable
關(guān)鍵字,因此可以不使用mutable
關(guān)鍵字來統(tǒng)計成員變量的訪問芍殖。
編程說明:成員變量的訪問統(tǒng)計——通過指針常量來統(tǒng)計
#include <iostream>
#include <string>
using namespace std;
class Test
{
int m_value;
int* const m_pCount; // 定義一個指針常量豪嗽,指針地址不會改變,但指針?biāo)赶虻闹悼梢愿淖?public:
Test(int value = 0) : m_pCount(new int(0)) // 初始化指針?biāo)赶虻闹禐?
{
m_value = value;
}
int getValue() const
{
*m_pCount = *m_pCount + 1;
return m_value;
}
void setValue(int value)
{
*m_pCount = *m_pCount + 1;
m_value = value;
}
int getCount() const
{
return *m_pCount;
}
~Test() // 釋放指針?biāo)赶虻目臻g
{
delete m_pCount;
}
};
int main()
{
Test t;
t.setValue(100);
cout << "t.m_value = " << t.getValue() << endl;
cout << "t.m_count = " << t.getCount() << endl;
const Test ct(200);
cout << "ct.m_value = " << ct.getValue() << endl;
cout << "ct.m_count = " << ct.getCount() << endl;
return 0;
}
輸出結(jié)果:
t.m_value = 100
t.m_count = 2
ct.m_value = 200
ct.m_count = 1
2. new
關(guān)鍵字創(chuàng)建出來的對象位于什么地方豌骏?
補充知識點:
(1)new/delete
的本質(zhì)是C++預(yù)定義的操作符——可以進行操作符重載
(2) C++對這兩個操作符做了嚴(yán)格的行為定義:
new:
1. 獲取足夠大的內(nèi)存空間(默認為堆空間)
2. 在獲取的空間中調(diào)用構(gòu)造函數(shù)創(chuàng)建對象
delete:
1. 調(diào)用析構(gòu)函數(shù)銷毀對象
2. 歸還對象所占用的空間(默認為堆空間)
-
在C++中能夠重載
new
/delete
操作符- 全局重載【不推薦】
- 局部重載(針對具體類進行重載)
重載
new
/delete
的意義:改變動態(tài)對象創(chuàng)建時的內(nèi)存分配方式new
和delete
的重載方式
new
和delete
的重載函數(shù)都是類的成員函數(shù)龟梦,這兩個函數(shù)默認為靜態(tài)成員函數(shù)static member function
,(函數(shù)聲明中static
關(guān)鍵字可以省略窃躲,都已經(jīng)默認為靜態(tài)成員函數(shù))
// static member function
void* operator new (unsigned int size) // size為指定內(nèi)存空間大小
{
void* ret = NULL;
// 1. ret指向內(nèi)存空間
// 2. 調(diào)用構(gòu)造函數(shù)计贰,創(chuàng)建對象
return ret;
}
// static member function
void operator delete(void* p)
{
// 1. 調(diào)用析構(gòu)函數(shù),銷毀對象
// 2. 歸還對象所占用的空間
}
編程說明:在通過new
在靜態(tài)存儲區(qū)創(chuàng)建對象,重載了new
和delete
操作符
#include <iostream>
using namespace std;
class Test
{
/* 在靜態(tài)存儲區(qū)里開發(fā)動態(tài)空間 */
static const unsigned int COUNT = 4; // 定義存儲對象的個數(shù)COUNT
static char c_buffer[]; // 定義靜態(tài)存儲區(qū)
static char c_map[]; // 定義標(biāo)記數(shù)組蒂窒,用于標(biāo)記自定義的區(qū)域哪些位置已經(jīng)創(chuàng)建對象
int m_value;
public:
void* operator new (unsigned int size)
{
void* ret = NULL;
for(int i=0; i<COUNT; ++i)
{
if( !c_map[i] )
{
c_map[i] = 1; // 將對應(yīng)區(qū)域改為已占用
ret = c_buffer + sizeof(Test) * i; // 將返回值指針指向可用區(qū)域
cout << "succeed to allocate memory: " << ret << endl;
break;
}
}
return ret;
}
void operator delete(void* p)
{
if( p != NULL )
{
char* mem = reinterpret_cast<char*>(p); // 將需要釋放的指針轉(zhuǎn)化為char類型躁倒,為下一步的指針運算做準(zhǔn)備
int index = (mem - c_buffer) / sizeof(Test); // 確定區(qū)域位置
int flag = (mem - c_buffer) % sizeof(Test); // 通過余數(shù)判斷此次傳來的指針是否合法,若flag==0代表指針合法洒琢,若flag!=0代表指針不合法
if( (flag == 0) && (index >= 0) && (index < COUNT) ) // 判斷指針是否合法秧秉,且是否在給定區(qū)域
{
c_map[index] = 0; // 將指定區(qū)域標(biāo)記為可用
cout << "succeed to free memory : " << p << endl;
}
}
}
};
char Test::c_buffer[sizeof(Test) * Test::COUNT] = {0}; // 在靜態(tài)存儲區(qū)分配所需空間,并將空間初始化為0
char Test::c_map[COUNT] = {0}; // 初始化標(biāo)記數(shù)組衰抑,0代表空間未使用象迎,1代表空間已使用
int main()
{
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 << "p[" << i << "] = " << pa[i] << endl;
}
for( int i=0; i<5; ++i)
{
cout << "delete " << pa[i] << endl;
delete pa[i];
}
return 0;
}
3. 面試題:在指定地址(棧空間/堆空間/靜態(tài)存儲區(qū))上創(chuàng)建c++對象?
解決方案:
- 在類中重載
new
/delete
操作符 - 在
new
的操作符重載函數(shù)中返回指定的地址 - 在
delete
操作符重載中標(biāo)記對應(yīng)的地址可用
#include <iostream>
#include <cstdlib>
using namespace std;
/* 動態(tài)指定在(靜態(tài)存儲區(qū)/棧/堆)空間中創(chuàng)建對象 */
class Test
{
static char* c_buffer; // 定義存儲區(qū)
static char* c_map; // 定義標(biāo)記數(shù)組呛踊,用于標(biāo)記自定義的區(qū)域哪些位置已經(jīng)創(chuàng)建對象
static unsigned int c_count; // 定義存儲對象的個數(shù)c_count
int m_value;
public:
static bool setMemorySource(char* memory, unsigned int size) // 動態(tài)指定具體內(nèi)存中創(chuàng)建對象
{
bool ret = false;
c_count = size / sizeof(Test); // 計算空間大小砾淌,c_count
ret = (c_count && (c_map = reinterpret_cast<char*>(calloc(c_count, sizeof(char))))); // 當(dāng)c_count>0時, 創(chuàng)建c_map
if( ret ) // 當(dāng) count>0 且 c_map 創(chuàng)建成功時
{
c_buffer = memory; // 將 c_buffer 指定到 memory 上
}
else // 否則谭网, 一切清零
{
free(c_map);
c_map = NULL;
c_buffer = NULL;
c_count = NULL;
}
return ret;
}
void* operator new (unsigned int size)
{
void* ret = NULL;
if( c_count > 0 ) // c_count>0:即指定的內(nèi)存空間標(biāo)記數(shù)組長度大于0時汪厨,可以在指定的內(nèi)存空間中創(chuàng)建對象
{
for(int i=0; i<c_count; ++i)
{
if( !c_map[i] )
{
c_map[i] = 1; // 將對應(yīng)區(qū)域改為已占用
ret = c_buffer + sizeof(Test) * i; // 將返回值指針指向可用區(qū)域
cout << "succeed to allocate memory: " << ret << endl;
break;
}
}
}
else
{
ret = malloc(size); // 在堆空間中創(chuàng)建對象
}
return ret;
}
void operator delete(void* p)
{
if( c_count > 0 ) // c_count>0:在指定的內(nèi)存空間中銷毀給定對象
{
if( p != NULL )
{
char* mem = reinterpret_cast<char*>(p); // 將需要釋放的指針轉(zhuǎn)化為char類型,為下一步的指針運算做準(zhǔn)備
int index = (mem - c_buffer) / sizeof(Test); // 確定區(qū)域位置
int flag = (mem - c_buffer) % sizeof(Test); // 通過余數(shù)判斷此次傳來的指針是否合法蜻底,若flag==0代表指針合法骄崩,若flag!=0代表指針不合法
if( (flag == 0) && (index >= 0) && (index < c_count) ) // 判斷指針是否合法聘鳞,且是否在給定區(qū)域
{
c_map[index] = 0; // 將指定區(qū)域標(biāo)記為可用
cout << "succeed to free memory : " << p << endl;
}
}
}
else
{
free(p); // 在堆空間中銷毀給定對象
}
}
};
unsigned int Test::c_count = 0; // 初始化指定內(nèi)存空間數(shù)組大小
char* Test::c_buffer = NULL; // 初始化指定內(nèi)存空間
char* Test::c_map = NULL; // 初始化標(biāo)記數(shù)組
/* ====== 測試部分 ====== */
/* 在給定堆空間創(chuàng)建對象 */
void test_create_object_in_heap()
{
cout << "========== test_create_object_in_heap ==========" << endl;
cout << "===== Test Single Object =====" << endl;
Test* pt = new Test;
cout << "pt" << pt << endl;
cout << "delete pt" << pt << endl;
delete pt;
cout << "===== Test Object Array =====" << endl;
Test* pa[5] = {0};
for( int i=0; i<5; ++i)
{
pa[i] = new Test;
cout << "p[" << i << "] = " << pa[i] << endl;
}
for( int i=0; i<5; ++i)
{
cout << "delete " << pa[i] << endl;
delete pa[i];
}
}
/* 在給定棧空間創(chuàng)建對象 */
void test_create_object_in_stack()
{
cout << "========== test_create_object_in_stack ==========" << endl;
char space[sizeof(Test) * 4]; // 在棧內(nèi)存中分配空間
Test::setMemorySource(space, sizeof(space));
cout << "===== Test Single Object =====" << endl;
Test* pt = new Test;
cout << "pt" << pt << endl;
cout << "delete pt" << pt << endl;
delete pt;
cout << "===== Test Object Array =====" << endl;
Test* pa[5] = {0};
for( int i=0; i<5; ++i)
{
pa[i] = new Test;
cout << "p[" << i << "] = " << pa[i] << endl;
}
for( int i=0; i<5; ++i)
{
cout << "delete " << pa[i] << endl;
delete pa[i];
}
}
/* 在給定靜態(tài)存儲區(qū)空間創(chuàng)建對象 */
void test_create_object_in_static_area()
{
cout << "========== test_create_object_in_static_area ==========" << endl;
static char space[sizeof(Test) * 4]; // 在靜態(tài)存儲區(qū)分配空間
Test::setMemorySource(space, sizeof(space));
cout << "===== Test Single Object =====" << endl;
Test* pt = new Test;
cout << "pt" << pt << endl;
cout << "delete pt" << pt << endl;
delete pt;
cout << "===== Test Object Array =====" << endl;
Test* pa[5] = {0};
for( int i=0; i<5; ++i)
{
pa[i] = new Test;
cout << "p[" << i << "] = " << pa[i] << endl;
}
for( int i=0; i<5; ++i)
{
cout << "delete " << pa[i] << endl;
}
}
int main()
{
test_create_object_in_heap();
test_create_object_in_stack();
test_create_object_in_static_area();
// Test的sizeof為4要拂,那么sizeof代表的值是非靜態(tài)成員變量的值抠璃;
// 在堆空間,椡讯瑁空間搏嗡,靜態(tài)區(qū)的內(nèi)存大小是一致的,只是存放的方式不一致
// cout << sizeof(Test) << endl; // 4
return 0;
}
輸出結(jié)果:
========== test_create_object_in_heap ==========
===== Test Single Object =====
pt0x9bd3008
delete pt0x9bd3008
===== Test Object Array =====
p[0] = 0x9bd3008
p[1] = 0x9bd3018
p[2] = 0x9bd3028
p[3] = 0x9bd3038
p[4] = 0x9bd3048
delete 0x9bd3008
delete 0x9bd3018
delete 0x9bd3028
delete 0x9bd3038
delete 0x9bd3048
========== test_create_object_in_stack ==========
===== Test Single Object =====
succeed to allocate memory: 0xbfd8a3ec
pt0xbfd8a3ec
delete pt0xbfd8a3ec
succeed to free memory : 0xbfd8a3ec
===== Test Object Array =====
succeed to allocate memory: 0xbfd8a3ec
p[0] = 0xbfd8a3ec
succeed to allocate memory: 0xbfd8a3f0
p[1] = 0xbfd8a3f0
succeed to allocate memory: 0xbfd8a3f4
p[2] = 0xbfd8a3f4
succeed to allocate memory: 0xbfd8a3f8
p[3] = 0xbfd8a3f8
p[4] = 0
delete 0xbfd8a3ec
succeed to free memory : 0xbfd8a3ec
delete 0xbfd8a3f0
succeed to free memory : 0xbfd8a3f0
delete 0xbfd8a3f4
succeed to free memory : 0xbfd8a3f4
delete 0xbfd8a3f8
succeed to free memory : 0xbfd8a3f8
delete 0
========== test_create_object_in_static_area ==========
===== Test Single Object =====
succeed to allocate memory: 0x804b101
pt0x804b101
delete pt0x804b101
succeed to free memory : 0x804b101
===== Test Object Array =====
succeed to allocate memory: 0x804b101
p[0] = 0x804b101
succeed to allocate memory: 0x804b105
p[1] = 0x804b105
succeed to allocate memory: 0x804b109
p[2] = 0x804b109
succeed to allocate memory: 0x804b10d
p[3] = 0x804b10d
p[4] = 0
delete 0x804b101
delete 0x804b105
delete 0x804b109
delete 0x804b10d
delete 0
4. new[]
和delete[]
-
動態(tài)對象數(shù)組創(chuàng)建通過
new[]
完成 -
動態(tài)對象數(shù)組銷毀通過
delete[]
完成 -
new[]
/delete[]
能夠被重載拉一,進而改變內(nèi)存管理方式
new[]
/delete[]
的重載方式
// static member function
void* operator new[] (unsigned int size)
{
return malloc(size);
}
// static member function
void operator delete(void* p)
{
free(p);
}
注意事項:
new[]
實際需要返回的內(nèi)存空間可能比期望的要多- 對象數(shù)組占用的內(nèi)存中需要保存數(shù)組信息
- 數(shù)組信息用于確定構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次數(shù)
編程說明:new/delete與new[]/delete[]調(diào)用
#include <iostream>
#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()
{
Test* pt = NULL;
pt = new Test;
delete pt;
pt = new Test[5];
delete[] pt;
return 0;
}
5. 小結(jié)
-
new
/delete
的本質(zhì)為操作符 - 可以通過全局函數(shù)重載
new
/delete
(不推薦使用) - 可以針對具體的類重載
new
/delete
-
new[]
/delete[]
與new
/delete
完全不同 -
new[]
/delete[]
可以進行操作符重載 -
new[]
返回的內(nèi)存空間可能比期望的要多(包含了數(shù)組的長度信息)