【C++标准库】模拟实现string类
模拟实现string类
- 一.命名空间与类成员变量
- 二.构造函数
- 1.无参(默认)构造
- 2.有参构造
- 3.兼容无参和有参构造
- 4.拷贝构造
- 1.传统写法
- 2.现代写法
- 三.析构函数
- 四.string类对象的容量操作
- 1.size
- 2.capacity
- 3.clear
- 4.empty
- 5.reserve
- 6.resize
- 五.string类对象的访问及遍历操作
- 1.operator[]
- 2.实现迭代器:begin+end
- 六.string类对象的增删查改操作
- 1.operator=
- 1.传统写法
- 2.现代写法
- 2.push_back
- 3.append
- 4.operator+=
- 5.insert
- 6.erase
- 7.find
- 8.substr
- 9.c_str
- 10.swap
- 七.非成员函数
- 1.string比较函数
- 2.流插入与流提取
- 3.getline
一.命名空间与类成员变量
根据string的结构,显然可知string实质就是字符数组,但有一点区别就是,string可以扩容,再类比动态顺序表,就不难得出string的成员变量。在模拟实现string时,为了与C++标准库中的string作区分,可以给定命名空间。
成员变量:
- char* str:指向string第一个字符的指针。
- size_t size:string中有效数据的个数。
- size_t capacity:string可以存放有效数据的容量。
- static const size_t npos:静态成员。
大体结构如下:
namespace xzy
{class string{private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;static const size_t npos; //静态成员类内声明};const size_t string::npos = -1; //类外初始化
}
二.构造函数
class string
{
public:string():_str(nullptr),_size(0),_capacity(0){}string(const char* str):_size(strlen(str)), _capacity(_size),_str(new char[_capacity + 1]){}const char* c_str() const{return _str;}private:char* _str;size_t _size;size_t _capacity;
};


- 第一种由于将_str初始化为nullptr,通过C语言中的返回_str直到遇到’\0’停止打印字符串的方法,而_str为nullptr,打印nullptr导致程序崩溃。
- 第二种看似程序正常但真的是正确的吗?其实:初始化列表出现的顺序,并不是初始化的顺序,而是按照成员变量声明的顺序初始化成员变量,先初始化_str,而_capacity是随机值,导致开辟的空间不确定,导致出现错误。
正确的方法如下:
1.无参(默认)构造
由于string默认含有’\0’,可以提前开辟一个’\0’,而’\0’不是有效的数据,也不算入容量之中。
string():_str(new char[1]{'\0'}), _size(0), _capacity(0)
{}
2.有参构造
注意:容量中不包含’\0’,而string中有包含’\0’,所以在开辟空间时要加上一个’\0’的空间。
string(const char* str)
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);
}
3.兼容无参和有参构造
string(const char* str = "")
{_size = strlen(str);_capacity = _size;_str = new char[_capacity + 1];strcpy(_str, str);
}
- 不传参时:用缺省值,str为空的常量字符串,strlen(str)为0,且sizeof(str)为1,含有一个隐藏的’\0’,刚好满足无参构造。
- 传参时:就用实参,满足有参构造。
4.拷贝构造
string(const string& str)
{_str = str._str;_size = str._size;_capacity = str._capacity;
}
int main()
{xzy::string s1;xzy::string s2(s1);return 0;
}

分析:当我们未提供拷贝构造时,编译器会提供拷贝构造,进行简单的值拷贝(浅拷贝),正如以上代码。但是存在很大的漏洞,s1的_str与s2的_str指向堆区同一块空间,程序结束时分别调用各自的析构函数,从而对同一块空间释放两次,这是未定义行为,导致程序崩溃。
1.传统写法
思路:先开空间,再利用strcpy拷贝,最后修改有效数据大小与容量。
string(const string& str)
{_str = new char[str._capacity + 1];strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;
}
2.现代写法
构造一个临时对象,进行交换。
void swap(string& str)
{std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);
}string(const string& str)
{string tmp(str._str);swap(tmp);
}

注意:由于没有初始化列表,不确定s2_str被初始化为nullptr,取决于编译器,可以在类成员变量声明时加上缺省值,确保s2.str为nullptr,而避免s2._str为随机值,交换给tmp变成野指针,函数结束时tmp调用析构函数释放不合法的空间导致程序崩溃。
class string
{
private:char* _str = nullptr;size_t _size = 0;size_t _capacity = 0;
};
三.析构函数
_str是在堆区开辟的空间,要用delete[]释放空间,否则造成内存泄漏。
~string()
{delete[] _str;_str = nullptr;_size = _capacity = 0;
}
四.string类对象的容量操作
1.size
size_t size() const
{return _size;
}
2.capacity
size_t capacity() const
{return _capacity;
}
3.clear
void clear()
{_str[0] = '\0';_size = 0;
}
4.empty
5.reserve
扩容时:先开辟新空间,千万记得多开一个空间保存’\0’,再将旧空间拷贝到空间,释放旧空间,修改_str指向新空间,最后修改容量。学了C++,new就取代realloc了。
void reserve(size_t n)
{if (n > _capacity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capacity = n;}
}
6.resize
修改有效数据的个数时:先比较修改后的有效数据与原有数据的大小,若小于则修改_size,若大于再比较容量与修改后的有效数据的大小,判断是否扩容,利用memset函数初始化。
void string::resize(size_t n, char c)
{if (n > _size){// 如果newSize大于底层空间大小,则需要重新开辟空间if (n > _capacity){reserve(n);}memset(_str + _size, c, n - _size);}_size = n;_str[n] = '\0';
}
五.string类对象的访问及遍历操作
1.operator[]
char& operator[](int pos)
{assert(pos >= 0 && pos < _size);return _str[pos];
}const char& operator[](int pos) const
{assert(pos >= 0 && pos < _size);return _str[pos];
}
-
提供两个版本的operator[]:普通重载[]与const修饰的重载[]。若初始化一个常量字符串时:const string s(“123”); 由于存在权放大问题,就无法调用普通重载[],而const修饰的重载[]就可以使用。
-
重载operator[],本质就是函数重载,而函数的返回值是不支持函数重载条件的,为了让两个operator[]满足函数重载的条件,可以const随便修饰一个成员函数。隐藏了this指针,实际const修饰的是this所指的对象。
-
第一个函数的参数列表的第一个位置隐藏了string* const this;第二个函数的参数列表的第一个位置隐藏了const string* const this;函数的参数不同就满足了函数重载的条件,可以共存。
2.实现迭代器:begin+end
typedef char* iterator;
typedef const char* const_iterator;iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}const_iterator begin() const
{return _str;
}
const_iterator end() const
{return _str + _size;
}
为了与标准库里的类似,重定义char* 为iterator。同理提供两个版本的迭代器iterator与const_iterator。
六.string类对象的增删查改操作
1.operator=
注意:operator=只能写成成员函数,不能写成成员函数。
1.传统写法
与传统写法的拷贝构造类似。
string& operator=(const string& str)
{if (this != &str){delete[] _str;_str = new char[str._capacity + 1];strcpy(_str, str._str);_size = str._size;_capacity = str._capacity;}return *this;
}
注意:如果没写 if (this != &str) 自己给自己赋值时,delete[] _str 后_str为野指针,自己给自己拷贝程序崩溃。
2.现代写法
与现代写法的拷贝构造类似。
string& operator=(const string& str)
{if (this != &str){//string tmp(str.c_str()); //调用构造string tmp(str); //调用拷贝构造swap(tmp); //刚好函数结束时,tmp将赋值前的空间释放,相当的完美}return *this;
}//更完美的方法:一行搞定
string& operator=(string tmp)
{swap(tmp);return *this;
}
2.push_back
尾插时:先检查容量,再进行尾插。注意:最后要补上'\0'。
void push_back(char ch)
{if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}_str[_size] = ch;++_size;_str[_size] = '\0';
}
3.append
追加时:先要判断容量是否大于有效数据+所追加的字符串大小。若小于则无需扩容;若大于两倍则需要多少就扩容多少;小于两倍就按照两倍扩容。最后拷贝字符串即可。
void append(const char* str)
{size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}strcpy(_str + _size, str);_size += len;
}
4.operator+=
- +=一个字符:直接调用push_back即可。
string& operator+=(char ch)
{push_back(ch);return *this;
}
- +=一个字符串:直接调用append即可。
string& operator+=(const char* str)
{append(str);return *this;
}
5.insert
- 插入一个字符:先检查容量,再整体往后挪动一位,最后插入即可。
但是存在一些坑如下:

- 当在pos=0位置插入字符时:end=0时进入循环,- -end,由于end类型为无符号整形size_t,则end不是-1而是一个非常大的值,进入死循环。
- 就算将end修改为int ,循环条件end>=pos时,两边类型不同会进行算数转换,int转换成size_t,end转换成size_t类型,依旧进入死循环。
正确写法:
void insert(size_t pos, char ch)
{assert(pos >= 0 && pos <= _size);if (_size == _capacity){reserve(_capacity == 0 ? 4 : 2 * _capacity);}//第一种:强转size_t为int//int end = _size;//while (end >= (int)pos)//{// _str[end + 1] = _str[end];// --end;//}//_str[pos] = ch;//++_size;//推荐这种:end始终大于0size_t end = _size + 1;while (end > pos){_str[end] = _str[end - 1];--end;}_str[pos] = ch;++_size;
}
- 插入一个字符串:先检查容量,再整体往后挪动为插入的字符串预留空间,最后插入字符串即可。
void insert(size_t pos, const char* str)
{assert(pos >= 0 && pos <= _size);size_t len = strlen(str);if (_size + len > _capacity){reserve(_size + len > 2 * _capacity ? _size + len : 2 * _capacity);}//整体后移//memmove(_str + len, _str, sizeof(char) * len);size_t end = _size + len;while (end > pos + len - 1){_str[end] = _str[end - len];--end;}//插入字符串for (size_t i = 0; i < len; i++){_str[pos + i] = str[i];}_size += len;
}
6.erase
删除时:比较要删除的子串长度与pos及其以后字符串的的大小,判断是否pos及其以后得字符全删除。
void erase(size_t pos, size_t len = npos
)
{assert(pos >= 0 && pos < _size);if (len >= _size - pos){_str[pos] = '\0';_size = pos;}else{//memmove(_str + pos, _str + pos + len, sizeof(char) * (_size - pos - len + 1));for (size_t i = pos; i <= _size - len; i++){_str[i] = _str[i + len];}_size -= len;}
}
7.find
- 查找字符:找到返回下标,未找到返回npos。
size_t find(char ch, size_t pos = 0)
{assert(pos >= 0 && pos < _size);for (size_t i = 0; i < _size; i++){if (_str[i] == ch){return i;}}return npos;
}
- 查找字符串:利用C语言接口strstr查找子串函数,找到返回下标,未找到返回npos。
size_t find(const char* str, size_t pos = 0)
{assert(pos >= 0 && pos < _size);const char* ptr = strstr(_str + pos, str);if (ptr == nullptr){return npos;}else{return ptr - _str;}
}
8.substr
返回子串:比较要返回子串长度与pos及其以后字符串的的大小,判断是否pos及其以后得字符全返回。
注意:深浅拷贝问题;由于是返回局部string,而局部string出函数被销毁。此时会拷贝构造一个临时string作为返回,而默认的拷贝构造是浅拷贝(简单的值拷贝),局部string销毁时,临时变量string中的_str变成野指针,外面又拷贝构造接收该临时string,本身就是无效的string,程序结束前调用析构函数释放空间,重复的delete导致程序崩溃。解决方法:自己写一个深拷贝构造。
string substr(size_t pos = 0, size_t len)
{assert(pos >= 0 && pos < _size);if (len > _size - pos){len = _size - pos;}string sub;sub.reserve(len);for (size_t i = 0; i < len; i++){sub += _str[pos + i];}return sub;
}
9.c_str
返回字符串首字符的地址:用于调用C语言接口,例如strcpy,memmove等。
const char* c_str() const
{return _str;
}
10.swap
调用std::swap进行对象(值)交换。
void swap(string& str)
{std::swap(_str, str._str);std::swap(_size, str._size);std::swap(_capacity, str._capacity);
}
七.非成员函数
1.string比较函数
只需要利用strcmp函数比较,实现两个函数,就可以调用实现多个函数。
bool operator<(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) < 0;
}bool operator>(const string& s1, const string& s2)
{return !(s1 <= s2);
}bool operator==(const string& s1, const string& s2)
{return strcmp(s1.c_str(), s2.c_str()) == 0;
}bool operator<=(const string& s1, const string& s2)
{return s1 < s2 || s1 == s2;
}bool operator>=(const string& s1, const string& s2)
{return !(s1 < s2);
}bool operator!=(const string& s1, const string& s2)
{return !(s1 == s2);
}
2.流插入与流提取
在C++中,屏幕和键盘分别通过标准输出流(std::cout)和标准输入流(std::cin)来实现数据的流插入(输出)和流提取(输入)。以下是针对屏幕(输出)和键盘(输入)的流插入与流提取的详细介绍:
- 屏幕(输出)与流插入(operator<<):流插入(operator<<)用于将数据发送到输出流中,在C++中,标准输出流std::cout是与屏幕(通常是控制台或命令行界面)相关联的。当你使用<<操作符将数据发送到std::cout时,数据会被格式化(如果需要的话)并显示在屏幕上。
- 键盘(输入)与流提取(operator>>):流提取(operator>>)用于从输入流中读取数据,在C++中,标准输入流std::cin是与键盘(或任何标准输入设备)相关联的。当你使用>>操作符从std::cin中读取数据时,它会从键盘获取输入,并根据需要将其存储在提供的变量中。
注意:
- 流插入与流提取不推荐写成成员函数,例如ostream& operator<<(ostream& out); 因为<<左边是类对象,调用时要写成s<<out,非常别扭。
- 不需要写成友元函数,可以做到不用访问类内的私有成员,完成流插入与流提取。
ostream& operator<<(ostream& out, const string& str)
{/*string::const_iterator it = str.begin();while (it != str.end()){cout << *it;++it;}*/for (auto ch : str){out << ch;}return out;
}istream& operator>>(istream& in, string& str)
{str.clear();char ch;//in >> ch; //错误,ch不会提取空白字符,陷入死循环ch = in.get();while (ch != ' ' && ch != '\n'){str += ch;//in >> ch;ch = in.get();}return in;
}
注意:流提取cin默认跳过空白字符(不会读取空白字符),例如:空格、换行,可以用cin.get()函数从键盘获得空白字符,类似C语言中的getc()函数。
优化方法:减少扩容,临时存放到字符数组中,等到满了时,再+=到其中。
istream& operator>>(istream& in, string& str)
{str.clear();const int N = 256;char buff[N];int i = 0;char ch;ch = in.get();while (ch != ' ' && ch != '\n'){buff[i++] = ch;if (i == N - 1){buff[i] = '\0';str += buff;i = 0;}ch = in.get();}if (i > 0){buff[i] = '\0';str += buff;}return in;
}
3.getline
getline函数:可以读取含有空格的字符串,将’\n’作为分隔符。
istream& getline(istream& in, string& str)
{str.clear();char ch;ch = in.get();while (ch != '\n'){str += ch;ch = in.get();}return in;
}
相关文章:
【C++标准库】模拟实现string类
模拟实现string类 一.命名空间与类成员变量二.构造函数1.无参(默认)构造2.有参构造3.兼容无参和有参构造4.拷贝构造1.传统写法2.现代写法 三.析构函数四.string类对象的容量操作1.size2.capacity3.clear4.empty5.reserve6.resize 五.string类对象的访问及…...
ArcGIS for js 标记(vue代码)
一、引入依赖 import Graphic from "arcgis/core/Graphic"; import GraphicsLayer from "arcgis/core/layers/GraphicsLayer"; import Color from "arcgis/core/Color"; import TextSymbol from "arcgis/core/symbols/TextSymbol.js"…...
全网最全最新100道C++面试题:40-60
前述:本文初衷是为了总结本人在各大平台看到的C面经,我会在本文持续更新我所遇到的一些C面试问题,如有错误请一定指正我。新建立了一个收集问答的仓库,欢迎各位小伙伴来更新鸭interview_experience: 本仓库初衷是想为大家提供一个…...
RAG+内容推荐,应该如何实践?
最近业务有需求:结合RAG内容推荐,针对实践部分,做一点探究。 话不多说,直接开冲! 背景 首先回顾一下 RAG 技术定义,它可以结合信息检索和生成模型的混合。简单来说,RAG 预训练的语言模型 信…...
SFTTrainer loss多少合适
在机器学习和深度学习中,“loss”(损失函数)的合理值并没有一个固定的标准,因为它依赖于多种因素,包括模型的类型、任务的性质、数据的规模和特性等。然而,我们可以从一些通用的原则和经验值来讨论损失函数…...
HTTP协议详解(一)
协议 为了使数据在网络上从源头到达目的,网络通信的参与方必须遵循相同的规则,这套规则称为协议,它最终体现为在网络上传输的数据包的格式。 一、HTTP 协议介绍 HTTP(Hyper Text Transfer Protocol): 全…...
RK3568平台(触摸篇)串口触摸屏
一.什么是串口屏 串口屏,可组态方式二次开发的智能串口控制显示屏,是指带有串口通信的TFT彩色液晶屏显示控制模组。利用显示屏显示相关数据,通过触摸屏、按键、鼠标等输入单元写入参数或者输入操作指令,进而实现用户与机器进行信…...
MySQL数据库-事务
一、什么是事务 1.概念 事务(Transaction):一个最小的不可再分的工作单元,一个事务对应一个完整的业务,一个完整的业务需要批量的DML(insert、update、delete)语句共同联合完成,事务只针对DML语句。 数据…...
qt事件类型列表
t提供了一系列丰富的事件类型,这些事件允许应用程序响应各种用户输入、系统通知以及其他类型的交互。以下是一些常见的Qt事件类型及其用途概述: QEvent::None (0): 无事件,用于初始化或作为默认值。 QEvent::Timer (1): 定时器事件ÿ…...
ElasticSearch父子索引实战
关于父子索引 ES底层是Lucene,由于Lucene实际上是不支持嵌套类型的,所有文档都是以扁平的结构存储在Lucene中,ES对父子文档的支持,实际上也是采取了一种投机取巧的方式实现的. 父子文档均以独立的文档存入,然后添加关联关系,且父子文档必须在同一分片,由于父子类型文档并没有…...
二百四十九、Linux——在Linux中创建新用户、赋予新用户root权限并对文件夹赋予新用户的权限
一、目的 安装国产化数据库OceanBase的时候,需要创建新用户、赋予新用户root权限并对文件夹赋予新用户的权限 二、创建新用户 #创建账户 oceanadmin [roothurys22 ~]#useradd -U oceanadmin -d /home/oceanadmin -s /bin/bash [roothurys22 ~]#mkdir -p /home/oc…...
com.mysql.cj.jdbc.Driver 爆红
出现这样的问题就是pom.xml文件中没有添加数据库依赖坐标 添加上这个依赖即可,添加完后重新加载一下Maven即可。 如果感觉对你有用就点个赞!!!...
传神论文中心|第19期人工智能领域论文推荐
在人工智能领域的快速发展中,我们不断看到令人振奋的技术进步和创新。近期,开放传神(OpenCSG)社区发现了一些值得关注的成就。传神社区本周也为对AI和大模型感兴趣的读者们提供了一些值得一读的研究工作的简要概述以及它们各自的论…...
案例分享-国外轻松感UI设计赏析
国外UI设计倾向于采用简洁的布局、清晰的排版和直观的交互方式,减少用户的认知负担,从而营造出轻松的使用体验。这种设计风格让用户能够快速找到所需信息,降低操作难度,提升整体满意度。 在注重美观的同时,更加重视用户…...
操作系统(4)——文件系统
目录 小程一言文件系统管理基础概念&功能基本概念文件的结构和属性文件的操作文件的安全性和权限控制文件系统的实现和分配方式 问题&解答1、文件系统在操作系统中起到什么作用?2、文件的逻辑结构和物理结构有何区别?3、如何理解文件权限控制在操…...
C# 调用Webservice接口接受数据测试
1.http://t.csdnimg.cn/96m2g 此链接提供测试代码; 2.http://t.csdnimg.cn/64iCC 此链接提供测试接口; 关于Webservice的基础部分不做赘述,下面贴上我的测试代码(属于动态调用Webservice): 1ÿ…...
工作流流程引擎框架推荐来了
近期有不少粉丝客户朋友都在询问工作流流程引擎框架推荐。随着行业竞争激烈化,实现流程化办公已经成为当务之急。低代码技术平台及工作流流程引擎拥有够灵活、更可靠、可视化界面等诸多个优势特点,在推动企业实现数字化转型的过程中深受行业信赖与喜爱。…...
从技术博客到个人 IP 矩阵:全面攻略与实战示例
文章目录 摘要引言创建博客选择平台设计和布局 内容规划明确目标受众设定内容方向制定发布计划 SEO 优化关键词研究内链和外链元标签优化 社交媒体推广选择社交平台制定推广策略 可运行的 Demo 代码模块QA 环节问:如何增加博客的曝光度?问:如…...
SOFAJRaft 简介
SOFAJRaft 简介 SOFAJRaft是一个基于Raft一致性算法的生产级高性能Java实现,由蚂蚁金服自主研发。以下是关于SOFAJRaft的详细介绍: 来源与背景: SOFAJRaft是从百度的braft移植而来,并在其基础上进行了一系列的优化和改进。它作为…...
c#中Oracle.DataAccess.dll连接数据库的报错处理
通过DataAccess.dll连接Oracle数据库时,报如下错误 The provider is not compatible with the version of Oracle client 最终原因: dll 文件复制不全(4个文件必须) oracle.dataaccess.dll oci.dll oraociei11.dll oraops11w.dll...
在鸿蒙HarmonyOS 5中实现抖音风格的点赞功能
下面我将详细介绍如何使用HarmonyOS SDK在HarmonyOS 5中实现类似抖音的点赞功能,包括动画效果、数据同步和交互优化。 1. 基础点赞功能实现 1.1 创建数据模型 // VideoModel.ets export class VideoModel {id: string "";title: string ""…...
AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...
AtCoder 第409场初级竞赛 A~E题解
A Conflict 【题目链接】 原题链接:A - Conflict 【考点】 枚举 【题目大意】 找到是否有两人都想要的物品。 【解析】 遍历两端字符串,只有在同时为 o 时输出 Yes 并结束程序,否则输出 No。 【难度】 GESP三级 【代码参考】 #i…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...
蓝桥杯 冶炼金属
原题目链接 🔧 冶炼金属转换率推测题解 📜 原题描述 小蓝有一个神奇的炉子用于将普通金属 O O O 冶炼成为一种特殊金属 X X X。这个炉子有一个属性叫转换率 V V V,是一个正整数,表示每 V V V 个普通金属 O O O 可以冶炼出 …...
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
QT开发技术【ffmpeg + QAudioOutput】音乐播放器
一、 介绍 使用ffmpeg 4.2.2 在数字化浪潮席卷全球的当下,音视频内容犹如璀璨繁星,点亮了人们的生活与工作。从短视频平台上令人捧腹的搞笑视频,到在线课堂中知识渊博的专家授课,再到影视平台上扣人心弦的高清大片,音…...
