问题提出
我们在解析JSON时,需要多次调用堆栈来解析对象,这时主要产生了两个问题:
1)多次调用需要大量的申请一块类似的内存空间,然后又释放掉,耗费时间。如果当前系统中有大量的内存碎片,并且我们申请的空间很大,甚至有可能失败。
2)压栈对象的类型不一,而在c++中定义的模板类std::stack中,却只能存储一种类型。
对此我们需要一个可以存放任意类型的混合堆栈,并且可以通过重复使用一块我们提前准备好的内存避免问题 1)。
解决办法
-
placement new(定位放置new)
它与普通的new不同,一般申请空间语句为:A* p=new A;而placement new则为:A* p=new (ptr)A;(ptr为指定的内存首地址)。
顾名思义,定位放置即在用户指定的内存位置上构建新的对象,这个构建过程不需要额外分配内存,只需要调用对象的构造函数即可。
placement new在已分配好的内存上进行对象的构建,速度大大提升;
同时已分配好的内存可以反复利用,有效的避免了内存碎片问题。
一般new和delete成对出现,然而placement new其实是相当于在一块已经分配好的内存上通过调用构造函数进行操作,所以结束后需要调用析构函数并释放内存。 -
混合类型堆栈
我们通过定义以下来实现:
class Stack {
Stack(Allocator* allocator, size_t stackCapacity);
~Stack();
void Clear();
void ShrinkToFit();
template<typename T> T* Push(size_t count = 1);
template<typename T> T* Pop(size_t count);
template<typename T> T* Top();
template<typename T> T* Bottom();
Allocator& GetAllocator();
bool Empty() const;
size_t GetSize();
size_t GetCapacity();
};
此时我们可以使用模板成员函数,压入或弹出不同类型的对象。而且为了完全防止拷贝构造函数调用的可能性,这些函数都是返回指针。
同时这也带来的新的问题:不同的数据类型带来的对齐问题。
对此我们可以使用:std::memcpy来解决:
int i = 123;
std::memcpy(s.Push<int>(), &i, sizeof(i));
int j;
std::memcpy(&j, s.Pop<int>(1), sizeof(j));
混合类型堆栈同时也可以当做一个动态缩放的缓冲区来使用,如:从DOM生成JSON的字符串便可以借助它来实现。