一、带着问题,找思路
有这样一个需求,有3个算法类,我们分别称为TestAlgo1、TestAlgo2、TestAlgo3,每个算法可以设置使用buffer类型是共享或私有。
补充:
共享是指在同类型算法的多个实例中使用同一块buffer,比如若算法TestAlgo1指定为共享buffer,则TestAlgo1的三个实例xx,yy,zz,均使用一块buffer进行读写操作。
私有是指每个算法实例,均有自己的buffer。
我们应该怎么实现呢?
首先我们定义一个内存块buffer类:
class MemoryBlock
{
public:
MemoryBlock(size_t size)
{
_ptr = new char[size];
_size = size;
std::cout << "alloc memory : " << this << std::endl;
}
~MemoryBlock()
{
delete[] (char*)_ptr;
_ptr = nullptr;
std::cout << "free memory : " << this << std::endl;
}
void* ptr()
{
return _ptr;
}
size_t size()
{
return _size;
}
private:
void* _ptr;
size_t _size;
};
然后定义TestAlgo1算法类,通过构造函数传入shared,设置其缓存方式。其他算法如法炮制。
class TestAlgo1
{
public:
TestAlgo1(bool shared)
: privateBuffer(nullptr)
{
refCount++;
if (shared)
{
if (sharedBuffer == nullptr)
{
sharedBuffer = new MemoryBlock(20);
}
}
else
{
privateBuffer = new MemoryBlock(20);
}
}
~TestAlgo1()
{
refCount--;
if (sharedBuffer && refCount == 0)
{// 其实最好应该是所有TestAlgo1实例释放后,
// 再删除共享buffer
delete sharedBuffer;
sharedBuffer = nullptr;
}
if (privateBuffer)
{
delete privateBuffer;
privateBuffer = nullptr;
}
}
virtual void run()
{
MemoryBlock* buffer = nullptr;
if (sharedBuffer)
buffer = sharedBuffer;
else
buffer = privateBuffer;
// 读写操作buffer
void* ptr = buffer->ptr();
size_t size = buffer->size();
// ...
}
private:
static MemoryBlock* sharedBuffer;
static int refCount;
MemoryBlock* privateBuffer;
};
MemoryBlock* TestAlgo1::sharedBuffer = nullptr;
int TestAlgo1::refCount = 0;
这代码。。。,此时只能用一个表情代替内心的独白。
不行,要做一个有追求(代码实在是太丑了)的程序猿,一定有其他办法。
是不是可以在创建算法实例的上一层创建好buffer,然后分配给每个算法,这样是不是就可以让算法共享一个buffer了。试一试:
class Controller
{
public:
Controller()
{
// 读取算法配置
// 假设TestAlgo1、TestAlgo2为共享,
// TestAlgo3私有
TestAlgo1* algo1 = new TestAlgo1();
algo1->setBuffer();
TestAlgo2* algo2 = new TestAlgo2();
algo2->setBuffer();
TestAlgo1* algo3 = new TestAlgo3();
algo3->setBuffer();
}
private:
static MemoryBlock* sharedBuffer1;
static MemoryBlock* sharedBuffer2;
MemoryBlock* privateBuffer3;
};
写到此处2个static,就明白了,压根没必要写下去了,上一把在各个算法中如法炮制,现在变成在Controller里面继续如法炮制了。。。
随着算法的数量增加,内存的管理如果放在算法的上一层次来做的话,管理成本很高。再次回到需要解决的问题上来,我们需要解决的是buffer的管理问题,而私有buffer属于每个算法实例,自然不用再说,共享buffer属于某个算法的所有实例,嗯?这个属性是不是和static成员变量很像,所有我们应该将buffer的管理限制在算法类内部实现,刚才试过上升了,那么我还可以试试buffer管理下降至算法类的成员变量、或者基类试试。基类很明显不行,因为放在基类中的话,那么所有算法都共享同一个buffer了,我们只希望buffer在一类算法中共享。那么只剩一条路了。读完上述的代码,是否已经发现存在大量类似代码了。
二、干货时间到SmartBuffer
我们可以使用模板将这部分代码封装起来,定义为智能缓存类SmartBuffer:
template <class VisitorT, class BufferT>
class SmartBuffer
{
public:
template <typename... Args>
SmartBuffer(bool shared, Args&&... args)
: privateObj(nullptr)
{
if (shared)
{
refCount++;
if (sharedObj == nullptr) // 若共享,则仅在第一次实例化时创建BufferT
{
sharedObj = new BufferT(std::forward<Args>(args)...);
}
}
else
{
privateObj = new BufferT(std::forward<Args>(args)...);
}
}
~SmartBuffer()
{
if (privateObj)
{
delete privateObj;
privateObj = nullptr;
}
else
{
refCount--;
if (sharedObj && refCount == 0) // 若共享,则仅在最后一次析构时释放BufferT
{
delete sharedObj;
sharedObj = nullptr;
}
}
}
BufferT* buffer()
{
if (privateObj)
{
return privateObj;
}
else
{
return sharedObj;
}
}
private:
SmartBuffer(const SmartBuffer&) = delete;
SmartBuffer& operator=(const SmartBuffer&) = delete;
private:
static int refCount; ///<实例化引用计数,用于释放sharedObj
static BufferT* sharedObj; ///<每个VisitorT实例均共享sharedObj
BufferT* privateObj; ///<私有BufferT
};
template <class VisitorT, class BufferT>
int SmartBuffer<VisitorT, BufferT>::refCount = 0; ///<必须放在头文件,否则编译报错
template <class VisitorT, class BufferT>
BufferT* SmartBuffer<VisitorT, BufferT>::sharedObj = nullptr; ///<必须放在头文件,否则编译报错
原理什么的就不说了,大家自己看,我已经测试过了,就当是个轮子吧。后面会放出代码地址,有需要的自取。
下面说一下轮子的功能与使用:
1. 构造函数SmartBuffer(bool shared, Args&&… args)
shared - true表示使用共享buffer,反之私有。
若共享buffer,则SmartBuffer第一次实例化时,会自动创建缓存,后续不会再创建。
args - 可变长度参数,用于创建内部缓存时,传递给构造函数MemoryBlock(size_t size),故args需要填一个参数。
2. 析构函数~SmartBuffer()
若共享buffer,则最后一个SmartBuffer实例被销毁时,会自动释放共享缓存。
3.BufferT* buffer()
获取缓存块地址,可以对缓存进行读写操作。
三、编写测试代码
下面看SmartBuffer类的使用。
TestAlgo1类:
class TestAlgo1
{
public:
TestAlgo1(bool shared)
{
std::cout << "TestAlgo1()" << std::endl;
buffer = new SmartBufferT(shared, 20);
}
virtual ~TestAlgo1()
{
std::cout << "~TestAlgo1()" << std::endl;
delete buffer;
buffer = nullptr;
}
private:
typedef SmartBuffer<TestAlgo1, MemoryBlock> SmartBufferT;
SmartBufferT* buffer;
};
TestAlgo2类:
class TestAlgo2
{
public:
TestAlgo2(bool shared)
{
std::cout << "TestAlgo2()" << std::endl;
buffer = new SmartBufferT(shared, 20);
}
virtual ~TestAlgo2()
{
std::cout << "~TestAlgo2()" << std::endl;
delete buffer;
buffer = nullptr;
}
private:
typedef SmartBuffer<TestAlgo2, MemoryBlock> SmartBufferT;
SmartBufferT* buffer;
};
TestAlgo3类:
class TestAlgo3
{
public:
TestAlgo3(bool shared)
{
std::cout << "TestAlgo3()" << std::endl;
buffer = new SmartBufferT(shared, 20);
}
virtual ~TestAlgo3()
{
std::cout << "~TestAlgo3()" << std::endl;
delete buffer;
buffer = nullptr;
}
private:
typedef SmartBuffer<TestAlgo3, MemoryBlock> SmartBufferT;
SmartBufferT* buffer;
};
三个类基本就是改了个编号。
怎么样,现在代码是否已经变得犹如清新可爱的小姐姐一般
main.cpp中:
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
TestAlgo1* algo1_1 = new TestAlgo1(true);
TestAlgo1* algo1_2 = new TestAlgo1(true);
TestAlgo2* algo2_1 = new TestAlgo2(false);
TestAlgo2* algo2_2 = new TestAlgo2(false);
TestAlgo3* algo3_1 = new TestAlgo3(true);
TestAlgo3* algo3_2 = new TestAlgo3(false);
TestAlgo3* algo3_3 = new TestAlgo3(true);
delete algo1_1;
delete algo1_2;
delete algo2_1;
delete algo2_2;
delete algo3_1;
delete algo3_2;
delete algo3_3;
return a.exec();
}
运行结果:
若对你有帮助,欢迎点赞、收藏、评论,你的支持就是我的最大动力!!!
同时,阿超为大家准备了丰富的学习资料,欢迎关注公众号“超哥学编程”,即可领取。
本文涉及工程代码,公众号回复:07SmartBuffer,即可下载。