在C++中,智能指针是一种管理动态分配内存的有效方式,它通过自动释放不再使用的内存来防止内存泄漏。然而,智能指针通常比原始指针占用更多的内存空间。例如,std::shared_ptr
和boost::shared_array
在Win32系统中通常占用8个字节,而一个原始指针只占用4个字节。本文将介绍一种优化智能指针的方法,使其大小与原始指针相同,同时保持智能指针的便利性和安全性。
在C++中,指针的值可能会因为继承关系而改变,这会影响指针转换。例如,下面的代码展示了这种影响:
struct base {
};
struct der : base {
virtual void fff() {}
};
int main() {
der obj;
base* p = &obj
if (p == &obj) {
printf("pointer is equal\n");
}
if ((unsigned)p != (unsigned)&obj) {
printf("value is not equal\n");
}
}
程序输出为“pointer is equal”和“value is not equal”,这说明了在Win32系统中,sizeof(std::shared_ptr<>) == 8
的原因。由于继承,无法从对象指针值中检索引用计数位置,因此需要在内部存储一个额外的指针。
对于C++内置数据类型,不存在继承问题,这个限制为提供了优化智能指针的机会。内存分配如下:
直接使用重载的new运算符,在对象堆分配期间嵌入引用计数器。引用计数器和对象共享相同的堆,这将大大提高效率并减少资源消耗。
以下是实现这种优化智能指针的关键代码:
template<typename T, size_t align>
struct NativeCore {
NativeCore(T* p, size_t align): m_ptr(p), m_align(align) { }
T* m_ptr;
size_t m_align;
};
这段代码定义了一个模板结构体NativeCore
,它包含了一个指向对象的指针和一个对齐值。
以下是测试代码,展示了如何使用这种优化的智能指针来管理内置类型的引用计数,而不需要显式地释放或删除:
#include "stdafx.h"
int main() {
using namespace std;
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF|_CRTDBG_LEAK_CHECK_DF);
RefNative<int>::Normal n = NativeAlloc<int>(2); // two int
n[0] = 1;
n[1] = 2;
int* pn = n;
printf("Current ref-count before push_back = %d\n", n.GetRefCount());
std::vector<RefNative<int>::Normal> vec;
vec.push_back(n);
printf("Current ref-count after push_back = %d\n", n.GetRefCount());
printf("sizeof(RefNative::Normal) = %d\n", sizeof(RefNative<int>::Normal));
RefNative<double, 64>::Aligned na = NativeAllocAligned<double, 64>(1); // allocate one 64 bytes aligned double
double* pna = na; pna[0] = 3.5;
printf("sizeof(RefNative::Aligned) = %d\n", sizeof(RefNative<int>::Aligned));
if ((unsigned)(pna)%64 == 0) {
puts("it is properly 64 bytes aligned");
}
return 0;
}
这段测试代码展示了如何创建一个优化的智能指针,它管理两个整数的引用计数,并将其添加到一个向量中。此外,还展示了如何创建一个64字节对齐的双精度浮点数的优化智能指针。