- 博客(27)
- 收藏
- 关注
原创 Linux 软件包管理与 vim 编辑器入门
一台刚装好的 Linux 机器,日常动作只有两类:往系统里加一个工具,改一个配置文件。前者交给包管理器,后者交给文本编辑器。这两件事把发行版之间的差异和编辑器的操作习惯都暴露了出来,值得单独理一遍。本文覆盖 yum 的操作、CentOS 与 Ubuntu 的命令对照,以及 vim 的三种基本模式与基础编辑动作。
2026-11-11 22:05:59
300
原创 STL set:有序去重集合的语义、接口与边界
为真,容器无法区分「相等」和「小于」,插入第二个元素时可能走错分支,也可能把元素丢掉。把这两步合进容器的存储规则:元素进入时按比较规则落到红黑树的固定位置,重复值被丢弃,从头遍历一遍就是升序序列。任何「改一个元素」的需求,实际都是「删掉一个、插入另一个」,两次。构造时逐个插入,重复值被丢弃,遍历时按中序取出,得到的就是去重且升序的结果。与节点句柄,可以取出节点、改 key 再插回,省掉一次重新分配,代码会变长一些。会重建容器,每个元素一个节点,遍历靠指针跳转,换来的是后续插入仍然保持有序。
2026-11-11 22:03:52
244
原创 C++11 的两块地基:列表初始化与右值引用
C++98 里已经有引用,C++11 新增右值引用之后,原来的引用被改称左值引用。两种引用都是给对象取别名,区别在于能绑定什么样的表达式。左值是表示数据的表达式,比如变量名、解引用的指针、数组元素。它有持久状态,存在内存里,可以取地址。左值能出现在赋值号左边,也能出现在右边。被const修饰的左值不能赋值,取地址不受影响。右值是字面值常量,或者表达式求值过程中产生的临时对象。它只能出现在赋值号右边,不能取地址。这两个名字的来源值得记一下。
2026-11-09 21:53:14
273
原创 AVL 树:平衡因子、四种旋转与完整实现
对 5 做一次左单旋,5 的右孩子 8 升上来,5 变成 8 的左孩子,8 原来的左子树 7 改挂到 5 的右边。此时 10 的左孩子是 8,8 的左孩子是 5,形状变成左-左,再对 10 右单旋。取出之后,两次挂接的先后不再影响正确性,代码按“先把 b 挂到 10 的左边,再把 10 挂到 5 的右边”的顺序写,和图示一致,便于对照检查。b 中所有键都大于 5、小于 10,它既可以当 5 的右子树,也可以当 10 的左子树。旋转后 5 的两侧高度都是 h+1,它自己的高度是 h+2,正好等于插入前的值。
2026-11-09 21:49:47
318
原创 二叉搜索树:一条不变量撑起的查找结构
左子树中所有节点的值都小于等于 N 的值右子树中所有节点的值都大于等于 N 的值左右子树各自也是二叉搜索树注意「所有」这两个字。约束覆盖整棵子树,而不只是直接孩子。只看父子关系的判断是错的。反例很好构造:根为 10,左孩子为 5,5 的右孩子为 12。父子关系 5 < 10 成立,12 却落在根的左子树里,整棵树已经乱了。由定义可以直接推出一条等价结论:对 BST 做中序遍历(左-根-右),结果必然非递减。反过来,中序非递减也是判定一棵二叉树是否为 BST 的充分条件。
2026-11-03 15:25:42
259
原创 C++ 多态:虚函数表、重写规则与 override/final
同时,这个类的每个对象多出一个指针,称为虚表指针(vptr),指向所属类的虚表。返回类型允许不同,前提是基类版本返回某个类的指针或引用,派生类版本返回该类的派生类的指针或引用。基类函数是虚函数,派生类写的同名函数参数列表不同,同样只构成隐藏,重写没有发生。这种「同一接口,不同实现」的能力叫多态,它在继承之上工作,代价是一次运行期的间接调用。基类的析构函数声明为虚函数,是唯一一条不需要权衡的规则。标准对此有明确规定:构造和析构期间发起的虚调用,只分发到当前构造或析构层次对应的版本,不会落到更派生的类。
2026-11-03 15:23:13
327
原创 继承(下):派生类构造、多继承与菱形虚拟继承
继承让派生类复用基类的成员,写法是。这个语法本身不复杂,复杂的是对象在内存里怎么摆,以及构造析构按什么顺序执行。这篇处理三个进阶问题:派生类对象的构造析构顺序、多继承的布局与名字冲突、菱形继承为什么需要虚拟继承。文中代码默认已包含<iostream>与<string>,并使用。
2026-10-31 21:20:53
15
原创 继承(上):代码复用、访问控制与对象切片
格式是在类名后面加冒号,再写继承方式和基类名。// 派生类新增的成员冒号后面的publicpublicprotectedprivate。它决定基类成员在派生类里还剩多少访问权限。规则用一张表能说全。基类中的成员public 继承protected 继承private 继承public 成员派生类的 public 成员派生类的 protected 成员派生类的 private 成员protected 成员派生类的 protected 成员派生类的 protected 成员。
2026-10-31 21:19:29
231
原创 C++ 模板进阶:非类型模板参数、特化与分离编译
模板的定义和使用它的代码必须在同一个编译单元里可见。把定义搬进a.h,每个包含该头文件的源文件都能自行实例化。// a.hmain.cpp和a.cpp都会包含a.h,各自生成一份的代码。多份同名符号不会冲突,模板实例化产生的是弱符号,链接器会把它们合并成一份。标准也允许同一个模板定义出现在多个编译单元中,这属于 ODR 的例外,不需要为它加inline。vectorstringmap的定义全部写在头文件里。
2026-10-29 19:54:31
165
原创 手写 list 容器:哨兵头节点与迭代器封装
std::list对外只暴露插入、删除、遍历这几类接口。拆到底层,需要设计的地方只有两处:节点之间怎么连,迭代器怎么把「节点指针」翻译成「指向元素的指针」。前者决定插入删除的代价,后者决定这个容器能不能直接套用范围 for 和 STL 算法。这篇从零实现一个可编译的list,重点放在迭代器上。链表迭代器是手写容器里第一个必须用类来模拟指针的地方,运算符重载的每一条规则都在这里派上用场。
2026-10-29 19:53:18
201
原创 STL 容器适配器:stack 与 queue 的接口、底层容器与经典题
三个候选容器放在stack底层容器pushpop隐藏代价vector均摊 O(1)O(1)扩容时搬移全部元素,引用与迭代器全部失效listO(1)O(1)每个元素额外两个指针,节点分散分配deque均摊 O(1)均摊 O(1)中段插入删除 O(n),适配器用不到vector做stack在接口上完全可行。栈只在尾部操作,push_back和pop_back都是均摊 O(1)。问题出在扩容:容量满时vector申请一块更大的连续空间,把旧元素逐个搬过去,再释放旧空间。
2026-10-25 22:36:00
308
原创 STL list 详解:双向循环链表的结构、接口与容器选型
vector把元素放在一段连续内存里。在中间插入一个元素,后面的元素全要往后挪,数据量大时这次挪动的代价很高。list换了一条路:每个元素单独分配一个节点,节点之间用指针串起来。插入和删除只改几个指针,已有元素原地不动。代价同样明确。list没有下标,访问第 n 个元素要从头走 n 步。节点分散在堆上,遍历时的缓存命中率远低于vector。这两面都由同一个结构决定,先把这个结构讲清楚,后面的接口和坑才有出处。
2026-10-24 20:25:47
367
原创 C++ STL string 类(一):构造、容量、访问与修改
重新分配缓冲区时,旧内存被释放,新数据拷到新地址。此前保存的迭代器、指针、引用仍指向旧地址,全部失效,继续使用是未定义行为。它的用途是提前申请足够空间,避免后续插入时反复扩容。的参数小于当前容量时,标准不要求缩容。只把有效字符数归零,不动已分配的缓冲区。这对后续复用有利,容量还在,再次插入不用重新分配。容量相关接口处理「有效字符数」和「已分配空间」两件事,两者是独立的量。不指向有效元素,只作循环终止标记,不要解引用。,这两个语法由 C++11 引入,先做说明。,它是一个表示「直到末尾」的哨兵值,类型是。
2026-10-21 19:09:29
141
原创 C++ 模板(初阶):函数模板与类模板
返回值类型 函数名(参数列表) { }template是引入模板的关键字,尖括号里是模板参数列表,每个参数前用typename或class修饰。关于typename和class:两者在这里作用相同,都用来声明一个类型参数,可以互换。struct不能代替class。//类型不匹配return 0;Swap(i, j)传入两个int传入两个char,编译器各自生成一份对应的函数。class 类模板名// 类内成员定义// 类模板。
2026-10-20 12:18:19
344
原创 C++ 类和对象(下):初始化列表、类型转换、static 成员、友元与内部类
自定义类型成员走初始化列表时,会调用它自己的默认构造函数。该类型没有默认构造,编译报错。内置类型成员走初始化列表时,如果没有显式初始化、也没有声明处缺省值,是否被清零由编译器决定,C++ 标准没有规定。所以不要依赖未显式初始化的内置成员,这类值在不同编译器上表现不一致。
2026-10-17 13:20:02
288
原创 C++ 类的默认成员函数:构造函数与析构函数
Date d3;定义一个对象,Date d3();却会被编译器当成函数声明。原因在于这行代码在 C++ 语法里有两种解读方式。一种解读是「定义一个名为d3的Date对象,括号是多余的初始化器」。另一种解读是「声明一个名为d3的函数,返回类型是Date,不接受参数」。C++ 规定:只要一段代码能被解析成函数声明,就按函数声明处理。于是Date d3();落到第二种解读上,d3成了一个函数名,而不是对象。这个现象有个专有名称,叫 most vexing parse。Date d1;
2026-10-17 13:15:59
190
原创 C++ 类的默认成员函数:构造函数与析构函数
Date d3;定义一个对象,Date d3();却会被编译器当成函数声明。原因在于这行代码在 C++ 语法里有两种解读方式。一种解读是「定义一个名为d3的Date对象,括号是多余的初始化器」。另一种解读是「声明一个名为d3的函数,返回类型是Date,不接受参数」。C++ 规定:只要一段代码能被解析成函数声明,就按函数声明处理。于是Date d3();落到第二种解读上,d3成了一个函数名,而不是对象。这个现象有个专有名称,叫 most vexing parse。Date d1;
2026-10-16 20:51:35
250
原创 C++ const 成员函数与取地址运算符重载
这个默认行为在多数场景下没有问题,但在两类需求面前会露出缺口:一类是希望某个对象创建之后不再被改动,另一类是希望控制外部获取对象地址的方式。把它用在普通成员上会削弱 const 的约束,只应在确实属于「逻辑只读、物理可写」的成员上使用。只有在明确需要隐藏地址,或者地址语义需要重定向,并且能控制所有后续地址使用方式时,才考虑重载。对象分 const 和非 const 两种,成员函数也分 const 和非 const 两种。const 成员函数不能调用同一个类的非 const 成员函数,因为它手里的。
2026-10-16 20:50:26
305
原创 C++ 类和对象(上):封装、实例化与 this 指针
面向过程写程序时,数据和处理数据的函数是分开的。以栈为例,C 语言里需要一个Stack结构体,再配一组STPushSTPop函数。任何拿到结构体指针的代码都能直接改top,数据没有保护。C++ 的做法是把数据和操作数据的函数放进同一个类,再用访问限定符控制外部能碰哪些成员。这一篇围绕类怎么定义、对象怎么创建、对象占多大空间、this 指针做了什么展开。class是定义类的关键字,后面的名字是类名,{}内是类的主体。类定义结束处的分号不能省略,漏掉会直接编译报错。类体内的内容统称类的成员。
2026-10-13 20:24:51
195
原创 C++ 基础:输入输出、缺省参数、函数重载与引用
它们的共同点是语法都很短,但每一处背后都压着一条硬规则。既能匹配无参版本,也能匹配"用缺省值"的版本,编译器无法抉择,于是报错。反过来,把缺省值全部堆在右边,调用时从左边开始填,省略的必然是最右边那几个,位置永远唯一确定。更值得注意的是,C++ 的流可以支持自定义类型对象的输入输出,这是。引用不占用独立的内存空间,它给已存在的变量取一个别名,与被引用的变量共用同一块空间。单独作为一条语句时,编译器看到的就是"调用一个无参函数",返回类型帮不上忙。就是典型:它可以是取地址,可以是按位与,也可以是引用声明——
2026-10-13 17:20:52
222
原创 数据结构--排序总结篇
排序算法,是计算机科学里的基础且关键部分。它在数据处理中无处不在,小到简单数组,大到海量数据集合,都离不开排序。 不同排序算法各有特点,时间与空间复杂度各异,适用场景也不尽相同。 接下来,我们就来详细探究各种排序算法,了解它们的原理、实现以及实际应用中的要点,助力大家在编程中更好地运用排序算法。基本思想:直接插入排序是一种简单的插入排序法,其基本思想是把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的序列中,直到所有的记录插入完为止,最后得到一个新的有序序列。直接插入排序的特性:元素集合越接近有序,
2026-05-07 19:18:39
367
原创 数据结构--二叉树初讲(二)
上次文章简单讲了一下二叉树的基本概念,包括二叉树的基本原理,二叉树的分类,特殊的二叉树等等,今天来继续来深入的了解下二叉树。
2026-04-22 20:50:43
355
原创 数据结构初级—— 二叉树初讲
父结点/双亲结点:若一个结点含有子结点,则这个结点称为其子结点的父结点;如上图:A是B的父结点子结点/孩子结点:一个结点含有的子树的根结点称为该结点的子结点;如上图:B是A的孩子结点结点的度:一个结点有几个孩子,他的度就是多少;比如A的度为6,F的度为2,K的度为0树的度:⼀棵树中,最大的结点的度称为树的度;如上图:树的度为 6叶子结点/终端结点:度为 0 的结点称为叶结点;如上图: B、C、H、I... 等结点为叶结点分支结点/非终端结点:度不为 0 的结点;
2026-04-14 17:23:48
378
原创 贪吃蛇项目——界面打印和蛇的相关设定
今天我们来搞懂贪吃蛇项目里「界面怎么画」和「怎么设置蛇的状态」。这两块是贪吃蛇的基础。文中会附完整代码,并对每个关键步骤的控制台输出效果进行精确的文字描述。首先咱们把文件分清楚,避免后续代码乱糟糟。按照功能拆分三个文件:要实现蛇的状态管理,首先需要用数据结构 “描述” 蛇、游戏规则,设计的结构体和枚举是整个项目的核心:蛇的身体是 “一节一节” 的,因此用链表节点描述每一节身体 链表的优势:蛇的长度会动态变化(吃食物变长),链表相比数组更适合 “动态增删节点”,且无需提前分配固定内存; 坐标设计:
2026-03-06 22:14:22
387
原创 贪吃蛇项目——基本工具部分
COORD是Windows API中定义的一个结构体,用于表示控制台屏幕缓冲区中的字符坐标(行和列)。该结构体常用于控制光标位置、文本颜色等操作,需包含头文件。SHORT X;// 水平坐标(列)SHORT Y;// 垂直坐标(行)} COORD;X:横坐标,表示字符所在的列(从0开始)。Y:纵坐标,表示字符所在的行(从0开始)。GetAsyncKeyState是Windows API中的一个函数,用于检测指定虚拟键的状态。该函数属于winuser.h。
2026-02-23 00:10:16
514
原创 C语言·指针的类型扩展及其应用
本文探讨了C语言指针的类型扩展机制。数组指针(int(*p)[N])通过类型系统为地址附加访问契约,使指针算术以整行为步长,实现多维数组语义。二维数组传参时列数不可省略而行数可省,因为编译器需要列宽计算偏移。函数指针(void(*f)(void))将地址解释为代码入口,严格类型匹配确保调用安全。函数指针数组可实现跳转表和多态分发。指针类型本质是编译期对内存布局的静态约束,运行时只有地址和字长真实存在,正确使用这些类型契约能在底层操作与抽象安全间取得平衡。
2026-01-31 22:45:57
604
原创 C语言·指针的定义和简单操作
数据类型 *指针变量名;数据类型:这指的是指针所指向的那个变量的数据类型。例如,如果指针要指向一个int变量,数据类型就是int;如果指向一个float变量,数据类型就是float。符号是定义指针变量的关键符号。它紧跟在数据类型之后(可以有空格),表示正在声明一个指针。指针变量名:给指针变量起一个名字,遵循C语言标识符命名规则。
2026-01-25 23:12:37
797
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅