std::move()

将参数转换为右值引用,在调用移动构造函数或移动赋值操作符时可以进行资源的转移而非拷贝,从而提高效率

std::swap()

实现

1
2
3
4
5
6
template<typename T>
void swap(T& a, T& b) {
T temp = std::move(a);
a = std::move(b);
b = std::move(temp);
}

功能

可以交换两个动态数组,但是实际上是交换两个数组的指针,操作量非常小,效率非常高
也常常用于所谓需要 “copy” 的地方,特别是需要硬拷贝的类的赋值
一般类的赋值涉及指针的时候,需要额外 new 一个新的指针进行等于,且在判断是否 new 的时候需要判断输入对象和执行对象不相同(相同的话在析构的时候会将当前的数组指针释放且会重复申请同一个数值的多个指针)
使用这个 swap 函数进行 就不需要担心这个问题

STL 依赖的特殊关键字

explicit

阻止编译器隐藏转换数据类型,比如

1
2
3
explicit FeetInches(int a);
FeetInches a = 5; // error
FeetInches a(5); // right

如果没有 explicit 关键字,编译器会自动进行转换, 将 5 转换成 FeetInches(int) 的构造函数,并且返回构造好的对象 a
但是如果加上关键字 explicit 那么编译器会报错,因为它不会自动转换

mutable

这个关键字是解决 const 成员函数不能修改成员变量的问题

1
2
3
4
5
6
7
8
9
10
class A {
int a;
mutable int b;
public:
const int& getA() const {
return a;
} // error
const int& getB() const {
return b;
} // right

STL 常用的容器

链表

  • std::list 双向链接,读取 .size() 只需要 O(1) 复杂度
  • std::slist 单向链表,读取 .size() 需要 O(n) 复杂度, 拥有 smart_pointer, 即可以自动释放内存
  • std::forward_list 单向链表,不存在 .size() 方法

迭代器 iterator

迭代器的设计模拟了指针的行为(Pointers in C++),使得程序员能够通过它们访问和操作数据
我们常用的 begin(), end() 等方法返回的就是 iterator

迭代器的分类

iterator_class.png
cbegin() 和 cend() 无论容器是否为常量,总是返回 const_iterator 类型

  • 对于常量容器,begin() 和 end() 也会返回 const_iterator 类型。这是因为在常量容器上,我们不能修改其元素的值,这与 cbegin() 和 cend() 的行为一致
  • 非常量容器上,使用 const auto& 在范围基于的 for 循环中不会改变 begin() 和 end() 的返回类型。它们仍然返回 iterator 类型,但是元素的引用是常量

反向迭代器 reverse_iterator

rbegin() 和 rend() 返回的是反向迭代器,它们是 iterator 的适配器,可以逆向遍历容器

1
2
3
4
std::vector<int> v = {1, 2, 3, 4, 5};
for (auto it = v.rbegin(); it != v.rend(); ++it) {
std::cout << *it << " ";
}

注意这里的 it 会自动识别到 reverse_iterator 类型, 其 ++ 对应迭代器向前移动

赘余内存 memory overhead

向量的赘余内存

对于每一个 vector,无论其尺寸大小,都会有 3 个指针指向其内存空间, 那么,对于一个高维度vector,我们要简单计算一下其赘余内存
vector<vector<vector<int>>> cube(a,b,c) 的额外指针数量为 3+3a+3ab3 + 3a + 3ab, 其中 33 为三维矩阵的指针,3a3a 为每一层的指针,3ab3ab 为层内的每一行的指针,而c 表示每一行向量的尺寸,额外指针数量和 cc 无关

函数对象 Functor

一个可以通过函数调用操作符 () 这种形式来调用的对象。实际上,functor 是任何定义了 operator() 的类的实例

  • 灵活性: Functors 可以重载多个版本的 operator(),允许根据不同的参数类型或数量执行不同的操作,类似于函数重载
  • 状态保持: Functors 可以拥有状态,即可以在多次调用之间保持状态