侵入式引用计数基类是将引用计数直接嵌入到被管理的对象内部,通过统一的基类提供计数增减接口,配合智能指针完成对象生命周期的自动管理,这种方式相比非侵入式方案减少了额外的内存分配开销。

侵入式引用计数基类设计
首先需要设计一个包含引用计数的基类,所有需要被侵入式智能指针管理的对象都需要继承这个基类。基类需要提供引用计数初始化、增加引用、减少引用以及获取当前引用计数的方法。
#include <iostream>
#include <cstddef>
// 侵入式引用计数基类
class RefCountBase {
private:
size_t ref_count; // 引用计数成员
protected:
// 构造函数初始化引用计数为0,由智能指针首次引用时增加
RefCountBase() : ref_count(0) {
std::cout << "RefCountBase 构造,初始引用计数0" << std::endl;
}
// 析构函数设为protected,避免直接删除基类指针
virtual ~RefCountBase() {
std::cout << "RefCountBase 析构" << std::endl;
}
public:
// 增加引用计数
void add_ref() {
++ref_count;
std::cout << "引用计数增加,当前计数: " << ref_count << std::endl;
}
// 减少引用计数,计数为0时删除对象
void release() {
--ref_count;
std::cout << "引用计数减少,当前计数: " << ref_count << std::endl;
if (ref_count == 0) {
delete this; // 计数归零删除自身
}
}
// 获取当前引用计数
size_t get_ref_count() const {
return ref_count;
}
};
侵入式智能指针实现
接下来实现配合基类使用的智能指针,智能指针在构造时增加对象的引用计数,析构时减少引用计数,通过重载指针相关操作符让使用体验和原生指针一致。
// 侵入式智能指针模板类
template <typename T>
class IntrusivePtr {
private:
T* ptr; // 管理的对象指针
// 增加引用计数的辅助函数
void add_ref_if_not_null() {
if (ptr != nullptr) {
ptr->add_ref();
}
}
// 减少引用计数的辅助函数
void release_if_not_null() {
if (ptr != nullptr) {
ptr->release();
}
}
public:
// 默认构造函数,管理空指针
IntrusivePtr() : ptr(nullptr) {}
// 从原生指针构造,假设指针指向的对象已经正确初始化
explicit IntrusivePtr(T* p) : ptr(p) {
add_ref_if_not_null();
}
// 拷贝构造函数
IntrusivePtr(const IntrusivePtr& other) : ptr(other.ptr) {
add_ref_if_not_null();
}
// 移动构造函数
IntrusivePtr(IntrusivePtr&& other) : ptr(other.ptr) {
other.ptr = nullptr;
}
// 析构函数
~IntrusivePtr() {
release_if_not_null();
}
// 拷贝赋值运算符
IntrusivePtr& operator=(const IntrusivePtr& other) {
if (this != &other) {
// 先减少当前对象的引用计数
release_if_not_null();
// 复制指针并增加新对象的引用计数
ptr = other.ptr;
add_ref_if_not_null();
}
return *this;
}
// 移动赋值运算符
IntrusivePtr& operator=(IntrusivePtr&& other) {
if (this != &other) {
release_if_not_null();
ptr = other.ptr;
other.ptr = nullptr;
}
return *this;
}
// 重载->操作符
T* operator->() const {
return ptr;
}
// 重载*操作符
T& operator*() const {
return *ptr;
}
// 获取原生指针
T* get() const {
return ptr;
}
// 判断是否管理有效对象
bool is_valid() const {
return ptr != nullptr;
}
};
使用示例与测试
我们定义一个继承RefCountBase的测试类,验证智能指针对对象生命周期的管理是否符合预期。
// 测试类,继承引用计数基类
class TestObject : public RefCountBase {
public:
int value;
TestObject(int v) : value(v) {
std::cout << "TestObject 构造,value=" << value << std::endl;
}
~TestObject() {
std::cout << "TestObject 析构,value=" << value << std::endl;
}
void print() const {
std::cout << "TestObject print, value=" << value << ", ref_count=" << get_ref_count() << std::endl;
}
};
int main() {
// 创建对象并用智能指针管理
IntrusivePtr<TestObject> ptr1(new TestObject(10));
ptr1->print();
{
// 拷贝智能指针,引用计数增加
IntrusivePtr<TestObject> ptr2 = ptr1;
ptr2->print();
std::cout << "离开内部作用域前" << std::endl;
}
// ptr2析构,引用计数减少,此时对象未被销毁
std::cout << "离开内部作用域后,当前引用计数: " << ptr1->get_ref_count() << std::endl;
// ptr1析构,引用计数归零,对象被销毁
return 0;
}
核心原理总结
侵入式引用计数的核心逻辑是计数与对象生命周期绑定:
- 对象构造时引用计数初始化为0,首次被智能指针管理时计数加1
- 每次智能指针拷贝都会让计数加1,智能指针析构会让计数减1
- 当计数减到0时,对象自动调用析构函数释放内存
这种方案的优势是无需额外分配引用计数的内存块,对象和控制块是同一个内存区域,缓存友好性更好,适合需要频繁创建销毁小对象的场景。缺点是要求被管理对象必须继承基类,侵入性较强,无法管理第三方不继承该基类的对象。