当我们使用C++进行项目开发时,总是避免不了使用new
来申请空间构建目标对象,但是频繁的申请和释放会导致系统性能的浪费和开销。
当我们设计一个类时,我们应该像以下方式这样设计:
C++class Airplane
{
private:
struct AirplaneRep
{
unsigned long miles{ 10 }; // 8
char type{ 'A' }; // 1 16
};
union
{
AirplaneRep rep{}; // 16
Airplane* next; // 8
}; // 16
public:
unsigned long getMiles()
{
return rep.miles;
}
char getType() { return rep.type; }
void set(unsigned long m, char t)
{
rep.miles = m;
rep.type = t;
}
static void* operator new(size_t size);
static void operator delete(void* ptr);
~Airplane() { std::cout << "Airplane::~Airplane()" << std::endl; }
private:
static const int BLOCK_SIZE;
static Airplane* headOfFreeList;
};
Airplane* Airplane::headOfFreeList;
const int Airplane::BLOCK_SIZE = 512;
对于new和delete需要我们重新设计和实现:
C++void* Airplane::operator new(size_t size) {
// if (size != sizeof(Airplane))
// {
// return ::operator new(size);
// }
Airplane* p = headOfFreeList;
if (p) {
headOfFreeList = p->next;
}
else {
Airplane* newBlock = static_cast<Airplane*>(::operator new(BLOCK_SIZE * sizeof(Airplane)));
for (int i = 1; i < BLOCK_SIZE - 1; ++i) {
newBlock[i].next = &newBlock[i + 1];
}
newBlock[BLOCK_SIZE - 1].next = 0;
p = newBlock;
headOfFreeList = &newBlock[1];
}
return p;
}
void Airplane::operator delete(void* ptr) {
if (ptr == 0) {
return;
}
// if (size != sizeof(Airplane)) {
// ::operator delete(ptr);
// return;
// }
Airplane* deleteMe = static_cast<Airplane*>(ptr);
deleteMe->next = headOfFreeList;
headOfFreeList = deleteMe;
}
调用时不会有任何不适:
C++int main() {
Airplane* p3 = new Airplane();
std::cout << p3 << std::endl;
// p3->set(1000, 'A');
Airplane* p4 = new Airplane();
std::cout << p4 << std::endl;
// p4->set(5000, 'A');
Airplane* p5 = new Airplane();
std::cout << p5 << std::endl;
delete p3;
delete p5;
delete p4;
free(p3);
// std::allocator<int> alloc;
// alloc.allocate(10);
return 0;
}
我们重写了operator new,至于重写的原因,就是因为malloc与free
以及new与delete
本身的缺陷所导致。
malloc在分配内存时会多分配一点内存至少用来存储具体分配的大小,因此,malloc操作是有代价的
,每一次malloc都会浪费一点空间,所以分配空间时malloc的次数越少越好。
因此,如上方代码所示我们重写了new。看重写后的new分配空间时的内存分布如下:
当我们第一次调用operator new时,为其在栈上分配栈帧
,首先创建Airplane类型的指针p
,指向headOfFreeList
。headOfFreeList
是存在静态区的静态变量。这时若p即headOfFreeList为空,我们就调用::operator new
一次分配足够多的栈空间,同时创建一个Airplane类型的指针newBlock
指向堆空间,并且堆空间内的分区进行连接以便遍历。将newBlock 所指向的0下标的空间的地址 给 p
,并将下标为1的空间的地址给headOfFreeList
。将p指向的空间给对象
。
当我们第二次调用operator new时,为其在栈上分配栈帧
,首先创建Airplane类型的指针p
,指向headOfFreeList
。p不为空,将p指向的空间给对象
。同时使headOfFreeList指向下一部分空间
。
因此。虽然我们多次调用了operator new,但相较于原本的new会多次浪费资源,重写的new在一定次数的调用中只会浪费一次资源。
这也是一次开辟,多次使用的典型操作。
本文作者:流浪的将军
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!