当前位置:网站首页>手把手教你实现buffer(二)——内存管理及移动语义
手把手教你实现buffer(二)——内存管理及移动语义
2022-08-04 05:25:00 【mo4776】
在webrtc中有一个
Buffer类,它是一个非常典型的基础buffer封装,我们来通过分析它的实现,来学习要如何实现一个buffer。这篇文件介绍 Buffer的构造函数及移动语义。 webrtc中的Buffer类
在前面的文章中,提到了buffer的几个基本功能:
- 内存动态分配
- 自动管理内存
- 自动扩容
- 提供使用方便的接口
我们从这4个方面来分析Buffer类的实现
class Buffer:public noncopyable {
public:
Buffer():_size(0),_capacity(0),_data(nullptr){
}
Buffer(size_t size,size_t capacity):
_size(size),
_capacity(std::max(size,capacity)),
_data(_capacity>0?new uint8_t[_capacity]:nullptr) {
}
Buffer(size_t size):Buffer(size,size) {
}
Buffer(const uint8_t* data, size_t size, size_t capacity):Buffer(size,capacity) {
std::memcpy(_data.get(),data, size);
}
Buffer(const uint8_t* data,size_t size):Buffer(data,size,size){
}
Buffer(Buffer&& buf):
_size(buf.size()),
_capacity(buf.capacity()),
_data(std::move(buf._data)) {
assert(IsConsistent());
buf._size = 0;
buf._capacity = 0;
}
Buffer& operator=(Buffer&& buf) {
assert(IsConsistent());
_size = buf._size;
_capacity = buf._capacity;
std::swap(_data,buf._data);
buf._data.reset();
buf._size = 0;
buf._capacity = 0;
return *this;
}
bool empty() const {
return _size == 0;
}
uint8_t* data() {
return _data.get();
}
uint8_t* data() const {
return _data.get();
}
size_t size() const {
return _size;
}
size_t capacity() const {
return _capacity;
}
uint8_t& operator[](size_t index) {
return data()[index];
}
uint8_t operator[](size_t index) const {
return data()[index];
}
void SetData(const uint8_t* data,size_t size);
void AppendData(const uint8_t* data,size_t size);
void SetSize(size_t size);
private:
void ZeroTrailingData(size_t count);
void EnsureCapacityWithHeadroom(size_t capacity, bool extra_headroom);
bool IsConsistent() const;
private:
size_t _size;
size_t _capacity;
std::unique_ptr<uint8_t[]> _data;
};
}
我做了点小的改良,Buffer类原本是个模版类,这里我将的类型固化为uint8_t便于分析。
管理内存
- 在
Buffer内部通过unique_ptr来管理内存空间,这里是uint8_t[],因为是管理的一段内存。
std::unique_ptr<uint8_t[]> _data;
- 在构造函数中分配内存。
//传入size和capacit来分配一段内存
Buffer(size_t size,size_t capacity):
_size(size),
_capacity(std::max(size,capacity)),
_data(_capacity>0?new uint8_t[_capacity]:nullptr) {
}
//只传入一个size参数时,size合capacity大小一直
Buffer(size_t size):Buffer(size,size) {
}
也可以通过一段内存构造Buffer
//分配制定size和capacity的内存并将data的内存copy到buffer中
Buffer(const uint8_t* data, size_t size, size_t capacity):Buffer(size,capacity) {
std::memcpy(_data.get(),data, size);
}
Buffer(const uint8_t* data,size_t size):Buffer(data,size,size){
}
- 在析构函数中释放内存
因为使用的是unique_ptr,所以直接使用默认析构函数即可,在析构_data时,内存会自动释放。
给Buffer赋予语意
在C++中的对象有三种语意:值语义,引用语义,移动语义
- 值语义,支持拷贝,指对象的拷贝与原对象无关,就像内置类型一样,将一个int型变量赋值给另外一个变量,两者间是无关联的。
- 引用语义,支持拷贝,指对象的拷贝与原对象相关联,比如在内部有指针的对象,如果对象赋值时对指针是浅拷贝,这里两个对象是相互关联的,因为两个对象的指针都指向同一块内存。
- 移动语义,不支持拷贝,两个对象间不能相互赋值,只能将内部资源移动到另外一个对象,原对象则变为无效对象。
Buffer在C++中最直观的语义就是引用语义,因为它代表一块内存空间。多个裸指针可以指向同一块内存,理论上Buffer也应该支持引用。
但是这种引用语义,很难区分所有权,即是谁需要对这块内存负责(负责分配,负责释放),这种语义的Buffer多引用几次,引用关系就会混乱了,很容易造成问题,比如内存无法释放;内存在释放后还会被访问造成段错误等。
移动语意刚刚与引用语义相反,它不支持拷贝,不能相互引用,只存在移动操作。对**Buffer**来说的移动语义,它所代表的资源就是内部的内存空间,将一个buffer1对象移动到另外一个buffer2对象,就是代表在buffer1对象不再拥有这段内存空间,而是将其转移给了buffer2对象。
这样严格区分所有权,可以转让出所有权,但是不能共享所有权。这样就能保证内存空间能被正常使用和安全的释放。
禁止拷贝
禁止拷贝,通过是构造函数,复制构造函数,赋值操作符号来实现。要禁用复制语义,需要即不定义这些函数也不能让编译器自动生成。如下是Buffer类的实现方法,继承noncopyable,
class Buffer:public noncopyable
nocopyable的实现
class noncopyable {
public:
noncopyable(const noncopyable&) = delete;
void operator = (const noncopyable&) = delete;
protected:
noncopyable() = default;
~noncopyable() = default;
};
nocopyable将拷贝构造函数和赋值函数声明为delete,构造函数和析构函数声明为private,就无法产生**nocoyable**对象。而**Buffer**继承**nocopyable**也就禁用了拷贝,这是实现禁止拷贝的通用手法。
实现移动语义
C++11中自动类型具有移动语义,需要实现移动构造函数和移动赋值操作符,通过它们来定义移动的行为。
移动构造函数
//移动构造函数
Buffer(Buffer&& buf):
_size(buf.size()),
_capacity(buf.capacity()),
_data(std::move(buf._data)) {
assert(IsConsistent());
buf._size = 0;
buf._capacity = 0;
}
将形参的_data指向的内存空间通过std::move给到了所构造的对象,而原对象的_data被置为无效,_size和_capacity也被置为0。
移动赋值操作符
//移动赋值运算符
Buffer& operator=(Buffer&& buf) {
assert(IsConsistent());
_size = buf._size;
_capacity = buf._capacity;
std::swap(_data,buf._data);
buf._data.reset();
buf._size = 0;
buf._capacity = 0;
return *this;
}
移动赋值运算符的逻辑跟移动构造函数的相同,将资源转移,将原对象置为无效。
使用Buffer
结合构造函数及移动语义,那么Buffer就可以这样用
Buffer tmpF() {
uint8_t tmp[5] = {
1,2,3,4,5};
Buffer tmpB(tmp,5);
return tmpB;
}
int main() {
Buffer buffer1;//1
uint8_t b[3] = {
1,2,3};
Buffer buffer2(b,3);
std::cout<<"buffer1 size "<<buffer1.size()<<",buffer2 size "<<buffer2.size()<<std::endl;
Buffer buffer3(std::move(buffer2));//2
std::cout<<"buffer3 size "<<buffer3.size()<<",buffer2 size "<<buffer2.size()<<std::endl;
Buffer buffer4 = tmpF();
std::cout<<"buffer4 size "<<buffer4.size()<<std::endl;
}
- 首先构造了两个
Buffer对象buffer1和buffer2,buffer1的size为0,buffer2的size为3。 - 通过移动构造函数使用buffer2构造了对象buffer3,此时buffer2的资源转移至buffer3,buffer3的size为3,buffer2的size为0。
- 函数
tmpF()返回一个临时对象通过移动构造函数来构造buffer4,临时对象的资源被转移到buffer4,buffer4的size为5。
边栏推荐
- string类简介
- Performance testing with Loadrunner
- warning C4251: “std::vector&lt;_Ty&gt;”需要有 dll 接口由 class“Test”的客户端使用错误
- MySql data recovery method personal summary
- OpenSSF 安全计划:SBOM 将驱动软件供应链安全
- 去重的几种方式
- 少年成就黑客,需要这些技能
- MySQL数据库面试题总结(2022最新版)
- What is the salary of a software testing student?
- 嵌入式系统驱动初级【4】——字符设备驱动基础下_并发控制
猜你喜欢

7.15 Day21---MySQL----索引

入坑软件测试的经验与建议

【问题解决】同一机器上Flask部署TensorRT报错记录

Unity Visual Effect Graph入门与实践

MySQL log articles, binlog log of MySQL log, detailed explanation of binlog log

程序员也应了解的Unity粒子系统

少年成就黑客,需要这些技能

Teenage Achievement Hackers Need These Skills

MySQL数据库面试题总结(2022最新版)

What is the salary of a software testing student?
随机推荐
Deploy LVS-DR cluster [experimental]
The idea setting recognizes the .sql file type and other file types
JS basics - forced type conversion (error-prone, self-use)
8.03 Day34---BaseMapper query statement usage
OpenSSF 安全计划:SBOM 将驱动软件供应链安全
文献管理工具 | Zotero
少年成就黑客,需要这些技能
Programming hodgepodge (4)
3面头条,花7天整理了面试题和学习笔记,已正式入职半个月
解决安装nbextensions后使用Jupyter Notebook时出现template_paths相关错误的问题
如何将 DevSecOps 引入企业?
How to view sql execution plan offline collection
力扣:96.不同的二叉搜索树
canal实现mysql数据同步
Unity DOTS学习教程汇总
Unity Visual Effect Graph入门与实践
【JS】js给对象动态添加、设置、删除属性名和属性值
力扣:746. 使用最小花费爬楼梯
Can 't connect to MySQL server on' localhost3306 '(10061) simple solutions
4.3 Annotation-based declarative transactions and XML-based declarative transactions