3.15
六大组件
- 分配器:用来管理内存。
- 迭代器:将容器和算法粘合在一起,用来遍历STL容器中的部分或全部元素。
- 容器:封装了大量常用的数据结构。
- 算法:定义了一些常用算法。
- 仿函数:具有函数特质的对象。
- 适配器:主要用来修改接口。
据说分为这6大组件,开始吧。
第一个拦路虎:模板
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。 模板是泛型编程的基础
模板:类模板和函数模板,着重了解类模板
下文介绍的很清楚
3.16
本来拿着一本《STL源码剖析》看,我左看右看,上看下看,翻看第一章,甩出一大段莫名其妙的代码,也没有举例,奈何在网上也没找到更通俗的书籍。为了不拖累计划,狠狠心,直接从源码开始看
namespace是个什么东东?
命名空间(namespace)可以被看作是一个用来组织和区分代码的容器,就像文件夹在计算机中用来组织文件一样。它可以帮助程序员避免命名冲突,让不同的代码模块可以有相同的名字而不会发生冲突。想象一下,如果整个世界只有一个人,那么每个人的名字都必须是独一无二的;但如果把人分成不同的国家,每个国家都可以有一个叫做“张三”的人,因为他们在不同的国家,所以不会混淆。
利用using声明可以在引用名空间成员时不必使用名空间限定符::
平时用的using namespace std,展开的是std标准库中的命名空间;
using namespace mystl::test
这行代码的作用是引入了
mystl::test
命名空间中的所有符号到当前作用域。这意味着在这个作用域内,您可以直接使用mystl::test
命名空间中定义的函数、变量和类,而无需在每次使用时显式地指定命名空间。std::cout.sync_with_stdio(false);
std::cout
是标准输出流对象,这是一个用于控制std::cout
与C标准库的stdio
流同步的函数调用(太高兴了,找到了前人留下的解释!!太疯狂了!太刺激了!没有课本,没有老师,也没有网课视频,就这样拼拼凑凑,左拾右捡,开始了我的第一次正式的项目自学!)
意外发现这个,介绍了文件之间的关系,从这里开始打算自底向上学习,用颜色标记。
6.18原网址已经失效了
层级 | 类 |
第八层 | unordered_map.h、unordered_set.h |
第七层 | algorithm.h、astring.h、hashtable.h、map.h、queue.h、set.h、stack.h |
六 | algo.h、basic_string.h、deque.h、lish.h、rb_tree.h、vector.h |
五 | memory.h |
四 | allocator.h、uninitialized.h |
三 | algobase.h、construct.h、heap_algo.h、numeric.h、set_algo.h |
二 | iterator.h、util.h |
一 | exceptdef.h、functional.h、type_traits.h |
底层 | cstddef、ctime、cstring、new、iostream、initializer_list、stdexcept、cassert、cstdlib、climits、type_traits、 |
第一层
exceptdef.h
#include <stdexcept>
- 这个头文件定义了一组标准异常类,用于处理运行时错误。与断言不同,异常处理允许程序在错误发生时继续执行,而不是立即终止。通过使用异常处理机制,程序员可以定义错误处理的代码块(catch块),以便在发生特定类型的错误时执行相应的操作。
stdexcept
头文件中的异常类包括std::runtime_error
、std::invalid_argument
、std::out_of_range
等。这些类都是std::exception
类的派生类,因此它们都具有一些共同的接口,如what()
方法,用于获取有关异常的描述性字符串。
#include <cassert>
- 这个头文件主要提供了断言(assertions)的功能。断言是一种在调试过程中使用的机制,它允许程序员在代码中插入一些检查点,以确保程序在运行时满足某些条件。如果某个断言失败(即条件不满足),程序将立即终止,并可能输出一条错误消息。
断言主要用于在开发和测试阶段捕捉程序中的错误。在发布版本(production version)中,断言通常会被禁用,以避免影响程序的性能和用户体验。
#ifndef MYTINYSTL_EXCEPTDEF_H_
#define MYTINYSTL_EXCEPTDEF_H_作用是:如果
MYTINYSTL_EXCEPTDEF_H_
未定义,则定义它并继续编译,否则跳过整个头文件的内容。这样可以确保头文件只被包含一次,避免了重定义错误。宏(Macro):是一种在编程中用于进行代码替换的预处理指令,常用#define定义。
这些宏提供了一种方便的方式来在满足某些条件时抛出特定类型的异常。
宏的使用:当作文本替换器,必须在一个物理行内定义完毕
【C++中的宏【入门】】 https://www.bilibili.com/video/BV1qM4y127SZ/?share_source=copy_web&vd_source=32c97dd6174d205c8e8939d4c140e6af
assert(expr)
:这是一个标准C/C++库中的宏,用于在程序执行时检查表达式expr
是否为真。如果expr
为假,则assert
宏会触发断言失败,打印出错信息并终止程序的执行。在调试模式下,这有助于快速发现程序中的逻辑错误。Cerr通常用于输出错误信息与其他不属于正常逻辑的输出内容。默认情况下,写到cerr的数据是不缓冲的
functional.h
#include <cstddef>
定义了宏
type_traits.h
类型萃取
第二层
iterator.h
下面没看完,了解typedef基本用法,感觉用处就是可以简化复杂的声明
C++typedef的详细用法_typedef c++-CSDN博客
任何声明变量的语句前面加上typedef之后,原来是变量的都变成一种类型。不管这个声明中的标识符号出现在中间还是最后
迭代器的作用:连接容器和算法的桥梁
【从零手撕STL源码】5、三个问题轻松了解迭代器_哔哩哔哩_bilibili
up分别手写了数组,链表节点的遍历和打印,通过在迭代器重载,实现了对两种容器的输出
util.h
通用工具,包括 move, forward, swap 等函数,以及 pair 等
#include <cstddef>
定义了一些常用的常量、宏、类型和函数:NUll nullptr_t size_t ptrdiff_t max_align_t offsetof(type, mem)
#include "type_traits.h"
用于提取类型信息,类型萃取,算法向迭代器询问,type traits调查iterator,并返回五个信息给算法。
先了解一下迭代器,发现自己基础好不扎实,找了些题单来练习容器
【从零手撕STL源码】6、type traits_哔哩哔哩_bilibili
第六层
vector.h
跟着【【从零手撕STL源码】3、手撕一个简易vector】 https://www.bilibili.com/video/BV1Ga41177x8/?share_source=copy_web&vd_source=32c97dd6174d205c8e8939d4c140e6af
实现了一个简单牛逼的vector!但是有错误,细节也需再了解。只是一个了解大概的入门。不能太纠结细节o(╥﹏╥)o
C++ 空指针是使用NULL 还是nullptr - 狗蛋的文章 - 知乎
https://zhuanlan.zhihu.com/p/686830080
下面是代码
#include<iostream>
using namespace std;
template<typename T>
class MyVector
{
public:
typedef T value;
typedef T* iterator;//就是一个指针
typedef T& reference;
public:
MyVector(int len = 0) :m_len(len), m_Data(nullptr), start(nullptr), pos(0)
{
if (len > 0)
{
m_Data = new value[len];
start = m_Data;
}
}
~MyVector()
{
delete[]m_Data;
}
void push_back(const value& v)
{
if (m_len != pos)//?
{
*(start + pos) = v;
pos++;
}
else {
cout << "数组越界了" << endl;
}
}
void pop_back()
{
--pos;
}
int size()
{
return this->m_len;
}
iterator begin()
{
return this->start;
}
iterator end()
{
return this->start + pos;
}
value& operator[](int n)
{
if (n < pos)
return *(start + n);
else {
cout << "数组越界了!" << endl;
}
}
//动态扩容的本质就是数组的copy
public:
iterator m_Data;
iterator start;
int m_len;//申请了多少
int pos;//从0开始,标记共有几个元素
};
int main()
{
MyVector<int> vec(10);//一个模板类
cout << vec.start << endl;
for (int i = 0; i < vec.size(); i++)
{
vec.push_back(i);
cout << vec.pos << ' ';
}
cout << endl;
for (MyVector<int>::iterator it = vec.begin(); it != vec.end(); it++)
{
cout << *it << " ";
}
cout << endl;
vec.pop_back();
for (MyVector<int>::iterator it = vec.begin(); it != vec.end(); it++)
{
cout << *it << " ";
}
return 0;
}
vector源码分析
#include <initializer_list>
C++11使用{}初始化,会形成一个initializer_list,背后有个array<T,n>,调用函数时该array内的元素可以被编译器分解逐一传递给函数。但是如果函数参数是一个initializer_list,这“包”数据(即{t1,t2…tn})将整体传入到函数中。
#include "iterator.h"
#include "memory.h"
#include "util.h"这个文件包含一些通用工具,包括 move, forward, swap 等函数,以及 pair 等
#include "exceptdef.h"目的是提供一些用于错误处理和调试的宏和异常抛出工具
#include "algo.h"
插播——右值引用
c++ 中的左右值 - フランドール·スカーレット - 博客园 (cnblogs.com)
讲的非常清晰啦,但是不知道有什么用》》降低内存消耗
先跳过
底层
C/C++编程:头文件<cstddef>、<cstdlib>、<cstring>_<cstddef>有什么-CSDN博客