<?xml version="1.0" encoding="utf-8" ?><rss version="2.0"><channel><title><![CDATA[z1qing的博客]]></title><description><![CDATA[]]></description><link>https://blog.youkuaiyun.com/z1qing</link><language>zh-cn</language><generator>https://blog.youkuaiyun.com/</generator><copyright><![CDATA[Copyright &copy; z1qing]]></copyright><item><title><![CDATA[Linux 软件包管理与 vim 编辑器入门]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/170563609</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/170563609</guid><author>z1qing</author><pubDate>Wed, 11 Nov 2026 22:05:59 +0800</pubDate><description><![CDATA[一台刚装好的 Linux 机器，日常动作只有两类：往系统里加一个工具，改一个配置文件。前者交给包管理器，后者交给文本编辑器。这两件事把发行版之间的差异和编辑器的操作习惯都暴露了出来，值得单独理一遍。本文覆盖 yum 的操作、CentOS 与 Ubuntu 的命令对照，以及 vim 的三种基本模式与基础编辑动作。]]></description><category></category></item><item><title><![CDATA[STL set：有序去重集合的语义、接口与边界]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/170563484</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/170563484</guid><author>z1qing</author><pubDate>Wed, 11 Nov 2026 22:03:52 +0800</pubDate><description><![CDATA[为真，容器无法区分「相等」和「小于」，插入第二个元素时可能走错分支，也可能把元素丢掉。把这两步合进容器的存储规则：元素进入时按比较规则落到红黑树的固定位置，重复值被丢弃，从头遍历一遍就是升序序列。任何「改一个元素」的需求，实际都是「删掉一个、插入另一个」，两次。构造时逐个插入，重复值被丢弃，遍历时按中序取出，得到的就是去重且升序的结果。与节点句柄，可以取出节点、改 key 再插回，省掉一次重新分配，代码会变长一些。会重建容器，每个元素一个节点，遍历靠指针跳转，换来的是后续插入仍然保持有序。]]></description><category></category></item><item><title><![CDATA[C++11 的两块地基：列表初始化与右值引用]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/170368930</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/170368930</guid><author>z1qing</author><pubDate>Mon, 09 Nov 2026 21:53:14 +0800</pubDate><description><![CDATA[C++98 里已经有引用，C++11 新增右值引用之后，原来的引用被改称左值引用。两种引用都是给对象取别名，区别在于能绑定什么样的表达式。左值是表示数据的表达式，比如变量名、解引用的指针、数组元素。它有持久状态，存在内存里，可以取地址。左值能出现在赋值号左边，也能出现在右边。被const修饰的左值不能赋值，取地址不受影响。右值是字面值常量，或者表达式求值过程中产生的临时对象。它只能出现在赋值号右边，不能取地址。这两个名字的来源值得记一下。]]></description><category></category></item><item><title><![CDATA[AVL 树：平衡因子、四种旋转与完整实现]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/170368725</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/170368725</guid><author>z1qing</author><pubDate>Mon, 09 Nov 2026 21:49:47 +0800</pubDate><description><![CDATA[对 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，正好等于插入前的值。]]></description><category></category></item><item><title><![CDATA[二叉搜索树：一条不变量撑起的查找结构]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/169944992</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/169944992</guid><author>z1qing</author><pubDate>Tue, 03 Nov 2026 15:25:42 +0800</pubDate><description><![CDATA[左子树中所有节点的值都小于等于 N 的值右子树中所有节点的值都大于等于 N 的值左右子树各自也是二叉搜索树注意「所有」这两个字。约束覆盖整棵子树，而不只是直接孩子。只看父子关系的判断是错的。反例很好构造：根为 10，左孩子为 5，5 的右孩子为 12。父子关系 5 < 10 成立，12 却落在根的左子树里，整棵树已经乱了。由定义可以直接推出一条等价结论：对 BST 做中序遍历（左-根-右），结果必然非递减。反过来，中序非递减也是判定一棵二叉树是否为 BST 的充分条件。]]></description><category></category></item><item><title><![CDATA[C++ 多态：虚函数表、重写规则与 override/final]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/169944947</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/169944947</guid><author>z1qing</author><pubDate>Tue, 03 Nov 2026 15:23:13 +0800</pubDate><description><![CDATA[同时，这个类的每个对象多出一个指针，称为虚表指针（vptr），指向所属类的虚表。返回类型允许不同，前提是基类版本返回某个类的指针或引用，派生类版本返回该类的派生类的指针或引用。基类函数是虚函数，派生类写的同名函数参数列表不同，同样只构成隐藏，重写没有发生。这种「同一接口，不同实现」的能力叫多态，它在继承之上工作，代价是一次运行期的间接调用。基类的析构函数声明为虚函数，是唯一一条不需要权衡的规则。标准对此有明确规定：构造和析构期间发起的虚调用，只分发到当前构造或析构层次对应的版本，不会落到更派生的类。]]></description><category></category></item><item><title><![CDATA[继承（下）：派生类构造、多继承与菱形虚拟继承]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/169652409</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/169652409</guid><author>z1qing</author><pubDate>Sat, 31 Oct 2026 21:20:53 +0800</pubDate><description><![CDATA[继承让派生类复用基类的成员，写法是。这个语法本身不复杂，复杂的是对象在内存里怎么摆，以及构造析构按什么顺序执行。这篇处理三个进阶问题：派生类对象的构造析构顺序、多继承的布局与名字冲突、菱形继承为什么需要虚拟继承。文中代码默认已包含<iostream>与<string>，并使用。]]></description><category></category></item><item><title><![CDATA[继承（上）：代码复用、访问控制与对象切片]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/169652400</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/169652400</guid><author>z1qing</author><pubDate>Sat, 31 Oct 2026 21:19:29 +0800</pubDate><description><![CDATA[格式是在类名后面加冒号，再写继承方式和基类名。// 派生类新增的成员冒号后面的publicpublicprotectedprivate。它决定基类成员在派生类里还剩多少访问权限。规则用一张表能说全。基类中的成员public 继承protected 继承private 继承public 成员派生类的 public 成员派生类的 protected 成员派生类的 private 成员protected 成员派生类的 protected 成员派生类的 protected 成员。]]></description><category></category></item><item><title><![CDATA[C++ 模板进阶：非类型模板参数、特化与分离编译]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/169463719</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/169463719</guid><author>z1qing</author><pubDate>Thu, 29 Oct 2026 19:54:31 +0800</pubDate><description><![CDATA[模板的定义和使用它的代码必须在同一个编译单元里可见。把定义搬进a.h，每个包含该头文件的源文件都能自行实例化。// a.hmain.cpp和a.cpp都会包含a.h，各自生成一份的代码。多份同名符号不会冲突，模板实例化产生的是弱符号，链接器会把它们合并成一份。标准也允许同一个模板定义出现在多个编译单元中，这属于 ODR 的例外，不需要为它加inline。vectorstringmap的定义全部写在头文件里。]]></description><category></category></item><item><title><![CDATA[手写 list 容器：哨兵头节点与迭代器封装]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/169463664</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/169463664</guid><author>z1qing</author><pubDate>Thu, 29 Oct 2026 19:53:18 +0800</pubDate><description><![CDATA[std::list对外只暴露插入、删除、遍历这几类接口。拆到底层，需要设计的地方只有两处：节点之间怎么连，迭代器怎么把「节点指针」翻译成「指向元素的指针」。前者决定插入删除的代价，后者决定这个容器能不能直接套用范围 for 和 STL 算法。这篇从零实现一个可编译的list，重点放在迭代器上。链表迭代器是手写容器里第一个必须用类来模拟指针的地方，运算符重载的每一条规则都在这里派上用场。]]></description><category></category></item><item><title><![CDATA[STL 容器适配器：stack 与 queue 的接口、底层容器与经典题]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/169070058</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/169070058</guid><author>z1qing</author><pubDate>Sun, 25 Oct 2026 22:36:00 +0800</pubDate><description><![CDATA[三个候选容器放在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申请一块更大的连续空间，把旧元素逐个搬过去，再释放旧空间。]]></description><category></category></item><item><title><![CDATA[STL list 详解：双向循环链表的结构、接口与容器选型]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/168977024</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/168977024</guid><author>z1qing</author><pubDate>Sat, 24 Oct 2026 20:25:47 +0800</pubDate><description><![CDATA[vector把元素放在一段连续内存里。在中间插入一个元素，后面的元素全要往后挪，数据量大时这次挪动的代价很高。list换了一条路：每个元素单独分配一个节点，节点之间用指针串起来。插入和删除只改几个指针，已有元素原地不动。代价同样明确。list没有下标，访问第 n 个元素要从头走 n 步。节点分散在堆上，遍历时的缓存命中率远低于vector。这两面都由同一个结构决定，先把这个结构讲清楚，后面的接口和坑才有出处。]]></description><category></category></item><item><title><![CDATA[C++ STL string 类（一）：构造、容量、访问与修改]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/168660378</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/168660378</guid><author>z1qing</author><pubDate>Wed, 21 Oct 2026 19:09:29 +0800</pubDate><description><![CDATA[重新分配缓冲区时，旧内存被释放，新数据拷到新地址。此前保存的迭代器、指针、引用仍指向旧地址，全部失效，继续使用是未定义行为。它的用途是提前申请足够空间，避免后续插入时反复扩容。的参数小于当前容量时，标准不要求缩容。只把有效字符数归零，不动已分配的缓冲区。这对后续复用有利，容量还在，再次插入不用重新分配。容量相关接口处理「有效字符数」和「已分配空间」两件事，两者是独立的量。不指向有效元素，只作循环终止标记，不要解引用。，这两个语法由 C++11 引入，先做说明。，它是一个表示「直到末尾」的哨兵值，类型是。]]></description><category></category></item><item><title><![CDATA[C++ 模板（初阶）：函数模板与类模板]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/168544946</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/168544946</guid><author>z1qing</author><pubDate>Tue, 20 Oct 2026 12:18:19 +0800</pubDate><description><![CDATA[返回值类型 函数名(参数列表) { }template是引入模板的关键字，尖括号里是模板参数列表，每个参数前用typename或class修饰。关于typename和class：两者在这里作用相同，都用来声明一个类型参数，可以互换。struct不能代替class。//类型不匹配return 0;Swap(i, j)传入两个int传入两个char，编译器各自生成一份对应的函数。class 类模板名// 类内成员定义// 类模板。]]></description><category></category></item><item><title><![CDATA[C++ 类和对象（下）：初始化列表、类型转换、static 成员、友元与内部类]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/168240671</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/168240671</guid><author>z1qing</author><pubDate>Sat, 17 Oct 2026 13:20:02 +0800</pubDate><description><![CDATA[自定义类型成员走初始化列表时，会调用它自己的默认构造函数。该类型没有默认构造，编译报错。内置类型成员走初始化列表时，如果没有显式初始化、也没有声明处缺省值，是否被清零由编译器决定，C++ 标准没有规定。所以不要依赖未显式初始化的内置成员，这类值在不同编译器上表现不一致。]]></description><category></category></item><item><title><![CDATA[C++ 类的默认成员函数：构造函数与析构函数]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/168240620</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/168240620</guid><author>z1qing</author><pubDate>Sat, 17 Oct 2026 13:15:59 +0800</pubDate><description><![CDATA[Date d3;定义一个对象，Date d3();却会被编译器当成函数声明。原因在于这行代码在 C++ 语法里有两种解读方式。一种解读是「定义一个名为d3的Date对象，括号是多余的初始化器」。另一种解读是「声明一个名为d3的函数，返回类型是Date，不接受参数」。C++ 规定：只要一段代码能被解析成函数声明，就按函数声明处理。于是Date d3();落到第二种解读上，d3成了一个函数名，而不是对象。这个现象有个专有名称，叫 most vexing parse。Date d1;]]></description><category></category></item><item><title><![CDATA[C++ 类的默认成员函数：构造函数与析构函数]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/168144085</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/168144085</guid><author>z1qing</author><pubDate>Fri, 16 Oct 2026 20:51:35 +0800</pubDate><description><![CDATA[Date d3;定义一个对象，Date d3();却会被编译器当成函数声明。原因在于这行代码在 C++ 语法里有两种解读方式。一种解读是「定义一个名为d3的Date对象，括号是多余的初始化器」。另一种解读是「声明一个名为d3的函数，返回类型是Date，不接受参数」。C++ 规定：只要一段代码能被解析成函数声明，就按函数声明处理。于是Date d3();落到第二种解读上，d3成了一个函数名，而不是对象。这个现象有个专有名称，叫 most vexing parse。Date d1;]]></description><category></category></item><item><title><![CDATA[C++ const 成员函数与取地址运算符重载]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/168144077</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/168144077</guid><author>z1qing</author><pubDate>Fri, 16 Oct 2026 20:50:26 +0800</pubDate><description><![CDATA[这个默认行为在多数场景下没有问题，但在两类需求面前会露出缺口：一类是希望某个对象创建之后不再被改动，另一类是希望控制外部获取对象地址的方式。把它用在普通成员上会削弱 const 的约束，只应在确实属于「逻辑只读、物理可写」的成员上使用。只有在明确需要隐藏地址，或者地址语义需要重定向，并且能控制所有后续地址使用方式时，才考虑重载。对象分 const 和非 const 两种，成员函数也分 const 和非 const 两种。const 成员函数不能调用同一个类的非 const 成员函数，因为它手里的。]]></description><category></category></item><item><title><![CDATA[C++ 类和对象（上）：封装、实例化与 this 指针]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/167812451</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/167812451</guid><author>z1qing</author><pubDate>Tue, 13 Oct 2026 20:24:51 +0800</pubDate><description><![CDATA[面向过程写程序时，数据和处理数据的函数是分开的。以栈为例，C 语言里需要一个Stack结构体，再配一组STPushSTPop函数。任何拿到结构体指针的代码都能直接改top，数据没有保护。C++ 的做法是把数据和操作数据的函数放进同一个类，再用访问限定符控制外部能碰哪些成员。这一篇围绕类怎么定义、对象怎么创建、对象占多大空间、this 指针做了什么展开。class是定义类的关键字，后面的名字是类名，{}内是类的主体。类定义结束处的分号不能省略，漏掉会直接编译报错。类体内的内容统称类的成员。]]></description><category></category></item><item><title><![CDATA[C++ 基础：输入输出、缺省参数、函数重载与引用]]></title><link>https://blog.youkuaiyun.com/z1qing/article/details/167810356</link><guid>https://blog.youkuaiyun.com/z1qing/article/details/167810356</guid><author>z1qing</author><pubDate>Tue, 13 Oct 2026 17:20:52 +0800</pubDate><description><![CDATA[它们的共同点是语法都很短，但每一处背后都压着一条硬规则。既能匹配无参版本，也能匹配"用缺省值"的版本，编译器无法抉择，于是报错。反过来，把缺省值全部堆在右边，调用时从左边开始填，省略的必然是最右边那几个，位置永远唯一确定。更值得注意的是，C++ 的流可以支持自定义类型对象的输入输出，这是。引用不占用独立的内存空间，它给已存在的变量取一个别名，与被引用的变量共用同一块空间。单独作为一条语句时，编译器看到的就是"调用一个无参函数"，返回类型帮不上忙。就是典型：它可以是取地址，可以是按位与，也可以是引用声明——]]></description><category></category></item></channel></rss>