现代C++与STL
现代C++与STL
vector
-
建在堆上,可伸缩,可能有预留空间的动态数组
-
end()只能用于表示位置,不能用于访问元素
-
vector构造/析构/拷贝/移动时,其中所有元素自动构造/析构/拷贝/移动
-
增大capacity时,其实是把原vector复制一遍,再把新加入的元素拷贝到新的地址,然后释放原来的vector的内存。因此当capacity扩大后,原迭代器会失效。
-
如果元素类型是指针,vector不管指针所指向的对象的生命周期
-
vector<int> v(3); v.push_back(1); v.push_back(1); //v.capacity() == 6 vector<int> v2 = v; //v2.capacity() == 5
vector的内存管理
- 插入元素时,会分配新的内存空间,把pos之前的元素拷贝到新空间,插入新元素,再将pos及之后的元素拷贝到新空间,最后销毁原空间。开销很大
迭代器
-
迭代器支持原生指针的多数操作,vector的迭代器支持所有操作
-
迭代器的有效性
有些容器的某些操作会使已创建的迭代器失效
尽量避免在遍历的同时增删元素
泛型算法
sort(vec.begin(), vec.end());
sort(arr, arr + 3);
//原生数组和其他容器都支持
std::sort(std::begin(x), std::end(x));
string
std::string s1 = "abc";
std::string s2 = "abc";
//字符串常量是左值,所有内容相同的字符串常量都共用一个地址
cout<<&"abc"<<endl;
cout<<&"abc"<<endl;
/**输出
0x100a1bf94
0x100a1bf94
*/
-
capacity和size永远相等,按需扩展,和vector的capacity成倍增长不同
-
尽可能使用std::string,不用c风格字符串
固定宽度类型
-
C++内置类型的short、int、long等类型的字节宽度在不同平台是不一样的,应当优先使用C++11标准库定义的跨平台的固定宽度类型:int8_t int16_t int32_t int64_t uint8_t uint16_t uint32_t uint64_t
-
printf等格式化函数中,格式化符是与内置类型绑定的,如%d对应int。使用固定宽度类型后,不应该再使用这类函数。
byte
-
将对象编码为字节信息(跨系统传递对象时)。以前经常使用char或unsigned char来表示字节
-
为避免歧义,C++17引入了std::byte类型,代表二进制bit内容,只能做位运算,不能直接赋值。
-
可用static_cast将其他类型转换成byte
-
将对象编码为字节信息时,应当保证编码方式在任何平台上都是固定的,而不依赖于当前平台的内存字节布局。主流网络协议都是大端序
auto
-
防止未初始化变量、类型名太长
-
auto x = -1; cout << boolalpha << (x < sizeof(x)) << endl; //x被转换为unsigned,输出false
-
当想要的类型不是int时,不要使用auto+整数数字初始化
-
不要在表达式中混用有符号和无符号类型
-
auto不会自动添加const、引用等类型(使用模板推导类型)
int &getRef(); int main() { //这里x是int,而不是int& auto x = getRef(); //int & auto &y = getRef(); //const int const auto z = x + 1; }
-
使用auto &时,总是得到引用,且const属性会被保留
-
auto不可能推导出原生数组,原因是不能用一个数组直接初始化另一个数组
-
auto可以用于推导函数返回值,前提是函数定义可见、每个return表达式类型一致。如果函数定义在当前文件不可见,则编译器无法推导返回值类型,报编译错误。
统一{}初始化
//多种初始化方式
int x(0);
int y = 0;
int z = {0};
-
在C++11中,{}初始化方式适用于所有初始化场景
-
{}初始化不允许implicit缩窄转换
-
大括号列表在某些时候会被自动转换为std::initializer_list
类型,此时每个元素的类型必须一样 -
std::initializer_list
中的元素不能修改。他只是对原始大括号列表的数据引用,其本身的复制开销很小,没有必要传递它的引用。 -
不要直接保存std::initializer_list
,以免发生悬空引用 -
可以通过添加std::initializer_list
参数的构造函数,让自定义的类支持统一初始化 -
大括号列表表达式没有类型,不能用于模板参数类型推导。但如果确定类型为initializer_list ,则大括号列表可以自动转换为initializer_list
constexpr
-
const修饰的变量,其值不能修改,但其值不一定能在编译器确定
-
constexpr确保在编译期实现求值,可以用于数组长度和模板参数。
-
使用constexpr有利于编译器做更好的优化
-
constexpr常量初始化时,其值必须在编译时就能算出来,因此不能使用普通变量和普通函数,但可以使用constexpr的变量和函数
constexpr int x = 5; //👌 constexpr int y = x + 1;//👌 int m = x; //👌 constexpr int z = m; //だめ constexpr double w = std::pow(1, 2.0);//だめ
-
constexpr函数可以在运行时调用
-
constexpr函数隐含inline属性,因此对外提供的接口一般直接定义在头文件中
-
尽可能把函数定义成constexpr,但是一个函数要能参与编译期计算,必须符合一定条件
-
constexpr函数内只能调用constexpr函数(C库、C++17库的函数都不是constexpr)
-
禁止分配堆内存(如很多容器)(从C++20开始,大部分容器可以在constexpr中使用)
-
函数内的局部变量必须在声明时初始化
-
不会抛出异常、不能带虚函数、没有指针类型的转换
-
-
构造函数也可以是constexpr
static_assert编译期校验
std::array
-
类似传统数组,内部元素必须是同一类型,大小必须在编译时已知
-
用size() 直接获取元素个数
-
传参时不会退化为指针
-
使用at() 方法,越界时会抛出异常
-
没有构造/析构函数
-
初始化行为和普通数组一样
-
C++17可以同时推导类型和长度
std::array arr = {1, 2, 3}; //指定类型后不能推导长度
右值引用
-
C++11新增右值引用,可以将对象内容移动,避免拷贝
-
对const T &&进行move,不会move,而是拷贝
-
所有参数都是左值
=delete
class MyClass
{
public:
MyClass(const MyClass &) = delete;
MyClass &operator = (const MyClass &) = delete;
};
=default
-
构造函数:对于有默认构造函数的成员,调用其默认构造函数,否则不赋予初始值
-
析构函数:队友有析构函数的成员,调用其析构函数
-
拷贝构造和拷贝赋值:依次调用每个成员的拷贝构造/赋值
-
移动构造和移动赋值:依次调用每个成员的移动构造/赋值
-
小心浅拷贝
unique_ptr
-
不允许拷贝,只允许移动
-
实现了解引用(*),成员访问(->),和其他指针比较(==),在使用时和普通指针一样
-
使用std::make_unique
shared_ptr
-
一个对象多处使用,不确定最后由谁释放
-
std::make_shared
-
当对象之间出现循环引用时,会导致引用计数永不减为0,导致内存泄漏
-
当能确定对象的所有权时,使用unique_ptr
函数对象 & lambda函数
-
lambda函数:在需要使用函数对象的地方,让编译器自动生成一个
-
编译器自动生成的lambda函数对象没有类名,只能用decltype 获取类型。任意两个lambda的类型都不同
int thres = 10; auto lambdaA = [thres](int x) {return x < thres;};
- 开销很大的对象应该按引用捕获
-
保存的引用不能超过引用对象的生命周期
-
默认不允许修改,相当于operator() 函数加上了const 。如果需要修改,需要explicitly添加mutable
int thres = 10; auto lambdaB = [thres](int x) mutable {return ++x < ++thres;};
Range-Based Loop
- 可给自定义的类实现begin() 和end() 从而实现range-based loop
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现