C++ 【轻量版STL】【造轮子】源码阅读学习笔记
参考内容
std::allocator
在内部生成一个allocator的实例 用来管理内存 完成对象的构造和析构
template<typename T, std::size_t N = 16, typename Allocator = std::allocator<T>>
std::conditional
如果第一个参数判断为true 则使用类型1(第二个参数类型) 否则使用类型2(第三个参数类型)
#include <type_traits>
using AAA = typename std::conditional<
flag == true,
bb::test_struct,
bb::test_struct_2 >::type;
连续判断:如果第一个判断为false 继续判断
namespace Ubpa::details {
template <std::size_t N>
using static_vector_size_type
= std::conditional_t<(N < std::numeric_limits<uint8_t>::max()), std::uint8_t,
std::conditional_t<(N < std::numeric_limits<uint16_t>::max()), std::uint16_t,
std::conditional_t<(N < std::numeric_limits<uint32_t>::max()), std::uint32_t,
std::conditional_t<(N < std::numeric_limits<uint64_t>::max()), std::uint64_t,
std::size_t>>>>;
}
std::ptrdiff_t
计算两个指针地址的差
计算公式:地址的差值 / 指针所指类型
std::reverse_iterator
反向迭代器 从容器的最后一个元素开始遍历 ++是查看前一个元素 与正常的迭代器是相反的
std::aligned_storage_t
std::aligned_storage_t<sizeof(T)* N, alignof(T)> m_storage:创建一块大小为T * N字节的内存空间 同时内存对齐要求为T
这个函数保证了内存对齐
内存对齐的好处:
- 提高了代码的可移植性 有些硬件无法访问非对齐的内存
- 大大提高了CPU的速度 如果内存对齐 CPU将内存块的数据写入寄存器时可以一块一块写入 否则只能先写入再剔除 非常影响效率
reinterpret_cast
按字节拷贝的类型转换 常用于相似类型的指针或者引用之间相互转换 风险大
#include <iostream>
using namespace std;
class A
{
public:
int i;
int j;
A(int n):i(n),j(n) { }
};
int main()
{
A a(100);
int &r = reinterpret_cast<int&>(a); //强行让 r 引用 a
r = 200; //把 a.i 变成了 200
cout << a.i << "," << a.j << endl; // 输出 200,100
int n = 300;
A *pa = reinterpret_cast<A*> ( & n); //强行让 pa 指向 n
pa->i = 400; // n 变成 400
pa->j = 500; //此条语句不安全,很可能导致程序崩溃 【*】
cout << n << endl; // 输出 400
【*】:编译器认为j的值为n后面的四字节中 于是编译器将向这四字节中写入500 但这四字节不知道存的是什么 运行后可能会导致程序崩溃
std::uninitialized_value_construct等
std::uninitialized_value_construct//初始化内存
std::uninitialized_value_construct(begin(), end())//初始化从begin到end大小的内存
//其中begin和end返回的都是指针
std::uninitialized_fill(begin(), end(), value)//大概是初始化内存后用value去填充
std::uninitialized_copy(other.begin(), other.end(), begin())//内存复制
std::uninitialized_move(other.begin(), other.end(), begin())//内存move 消除原有内存
参数列表初始化
std::initializer_list<value_type> ilist
实现了vector可以直接指定初始化的数值 而无需指定初始化vector的大小:
std::vector<int> a = {1, 2, 3};
自己设计vector时使用这种功能:
static_vector(std::initializer_list<value_type> ilist) : m_size{ static_cast<size_type>(ilist.size()) } {
assert(ilist.size() <= N);
std::uninitialized_copy(ilist.begin(), ilist.end(), begin());
}
std::is_trivially_copy_assignable_v
std::is_trivially_copy_assignable_v<value_type> 判断参数是否是具有普通复制赋值运算符的类
std::is_trivially_copy_constructible_v<value_type> 判断参数是否是具有普通复制构造函数的类
is_trivially_destructible_v
这里大概可能是判断value_type类型是否可以计算sizeof?可能和memcpy的底层实现有关吧
判断为否的话直接使用内存复制的方式完成拷贝。
if constexpr (std::is_trivially_copy_assignable_v<value_type> && std::is_trivially_copy_constructible_v<value_type>) {
std::memcpy(&m_storage, &rhs.m_storage, rhs.m_size * sizeof(value_type));
}
else {
std::copy(rhs.begin(), rhs.begin() + m_size, begin());
std::uninitialized_copy(rhs.begin() + m_size, rhs.end(), end());
}
std::memcpy
void *memcpy(void*dest, const void *src, size_t n)
//由src指向地址为起始地址的连续n个字节的数据复制到以destin指向地址为起始地址的空间内。
noreturn
告诉编译器这个函数用不返回 帮助编译器优化
例子中这个函数用于当发生数组越界访问时抛出异常 所以它不用任何的返回值
该函数要么总是抛出异常 要么永不停止
[[noreturn]] void throw_out_of_range() const { throw std::out_of_range("invalid static_vector subscript"); }
个人理解和void的区别:
void函数在函数调用之后 编译器会做一些清理工作 比如调整栈指针等 noreturn告诉编译器可以省略清理工作的环节 可以省去很多无关紧要的代码