写在前面
buffer的概念很庞大,根据不同场景,有各种各样功能的buffer,比如双缓冲buffer,jitterbuffer。这篇文章说的buffer,是指一段用于存取数据的内存空间,这是最基础的buffer。然而就是这种最基础的buffer,在C++中标准库也没有提供很通用,完善的buffer类(-_-!)。
这一系列文章,将参照webrtc中的CopyOnWriteBuffer ,一步步介绍如何实现这样的buffer。
buffer的实现
静态分配
在我们平常的开发中,当需要一个buffer时,最简单的方式就时直接定义个数组,如下代码:
uint8_t buffer[128]
这种方式叫静态数组,空间分配在栈上,产生了一个固定128字节长度的buffer。长度值是预估,并不是根据程序运行时分配空间,而是编译器就确定了,所以会造成空间浪费。
动态分配
通过new 或malloc 动态分配一个buffer,这种方式的好处在程序运行时分配,不会造成空间浪费,坏处也很明显,就是需要手动管理buffer,不小心就会造成内存泄露。
通过智能指针管理buffer
到了C++11,有了unique_ptr 后,可以用它来代替裸指针管理动态分配的内存,达到自动释放内存的目的
std::unique_ptr <uint8_t[]> buffer(new uint8_t[128]);
但是这种buffer还是太原始(本质就是通过指针管理buffer):
- 容量固定,虽然是动态分配,但是再扩容就比较麻烦
- 缺少必要的接口,比如判断是buffer否为空,获取buffer中的数据长度等
vector代替指针
标准库中的vector 可以作为buffer使用,它可以自动管理内存,自动扩容,提供了必要接口。
#include <vector>
#include <iostream>
int main() {
std::vector<uint8_t> buffer;
uint8_t data1[3] = {1,2,3};
buffer.assign(data1,data1+3);
std::cout<<"size:"<<buffer.size()<<",capacity:"<<buffer.capacity()<<std::endl;
uint8_t data2[5] = {1,2,3,4,5};
buffer.assign(data2,data2+5);
std::cout<<"size:"<<buffer.size()<<",capacity:"<<buffer.capacity()<<std::endl;
for (auto i:buffer) {
std::cout<<(uint32_t)i<<" ";
}
}
上面的代码中在将data2 放入buffer 时,buffer会自动扩容。vector 也提供了size() ,capaciy() 等基本接口。通过vector 的data() 方法可以获取内部buffer的指针。
vector作为buffer使用的局限
vector 满足了作为一个buffer的大部分要求,但是它的接口还是有局限,比如vector 没提供添加一段数据方法,尽管有assgin 方法,但它是往vector 中copy一段数据,而非添加。
实现自己的buffer
上面描述了buffer的需求,概括起来有如下几点:
- 内存动态分配
- 自动管理内存
- 自动扩容
- 提供使用方便的接口
vector 满足了前三个需求。但是第4个最重要,毕竟简单,好用是最核心的需求。
|