构造与析构:对象生命周期核心原理与工程踩坑实录

有段时间我在维护一个内部教学系统,里面有个很诡异的现象:新建账号第一次登录时,头像永远显示默认图,要手动刷新两次才能恢复。一开始我怀疑是缓存,查到最后发现,问题出在对象创建后的初始化阶段——那块逻辑本该由构造方法负责,却在调用方手里七拼八凑,少了一个步骤,对象就一直处于半初始化状态。从那以后,我把项目里所有类的构造方法和析构方法重新梳理了一遍,这类“幽灵状态”几乎绝迹。

这篇笔记就是基于那个阶段的排查和重构经验写的。我不会只对着语法念一遍,而是把构造方法、析构方法放在“对象的出生与消亡”这条完整时间线里讲,配合多语言对比和真实的踩坑案例。适合刚学会类与对象的语法、但对对象生命周期还比较懵的同学,也适合写过一阵子代码、但总感觉对资源释放说不清道不明的朋友。

1. 为什么一个类需要“出生动作”——从对象初始化失败说起

1.1 对象刚创建那几毫秒,是最容易失控的阶段

很多人的第一版代码长这样:先用类似结构体的方式创建一个对象,然后手动给每个字段赋值。这个写法非常符合直觉——对象不就是一块带字段和函数的内存嘛,建出来之后我要什么就填什么。

但问题在于,赋值动作由调用方负责,而不是由对象自己负责。一旦创建对象的地方变多,漏赋值几乎是必然的。比如下面这段 C 风格的代码:

struct User { char name[50]; int age; int role_id; }; struct User u; strcpy(u.name, "alice"); u.age = 18; // role_id 忘了赋

程序能编译,也能运行,但role_id是一个随机值。用户可能突然变成管理员,也可能突然没有任何角色。这就是典型的“半初始化状态”——对象已经存在,但它内部的数据不可信。

我在教学系统里遇到的头像问题,本质上就是同一个病根:某个对象依赖另一个字段做初始化,但字段是在对象创建之后、又被某段外部代码赋值,时间顺序一乱,后面的逻辑全碎了。

1.2 构造方法是把“初始化义务”收回类内部

构造方法(有的地方叫构造函数)要解决的就是这个问题:把对象出生时的初始化义务,从调用方手里收回到对象自己手里。它有几个硬性特征:方法名和类名相同、没有返回值、在创建对象时自动执行。

看一段对照代码:

// C++ 写法 class User { public: User(const std::string& name, int age, int role_id) : name_(name), age_(age), role_id_(role_id) {} private: std::string name_; int age_; int role_id_; };
// Java 写法 public class User { private String name; private int age; private int roleId; public User(String name, int age, int roleId) { this.name = name; this.age = age; this.roleId = roleId; } }
# Python 写法 class User: def __init__(self, name: str, age: int, role_id: int): self.name = name self.age = age self.role_id = role_id

从调用方视角看,语言虽然不同,体验是一样的:只要new User(...)或User(...)这个动作执行完,拿到的就应该是一个完整可用的对象,不需要再在外面补一堆赋值。构造方法把“初始化义务”封装进了类内部,也就把出错的概率压缩到了一个小范围内。

1.3 好的构造方法应该达到什么标准

结合我的排查经验,好的构造方法起码应该符合三条标准。

第一条,构造完成后,对象要自洽。所有“必需字段”都应有合理值,不能留下任何依赖外部后续赋值的状态。如果确实有暂时不明确的字段,就设计成显式的 setter 或独立方法,不要让调用方在构造方法之后“补作业”。

第二条,构造方法不要在中间依赖对象的未来状态。构造阶段就是构造阶段,你这时候调用一个普通方法,可能会有字段还没初始化;你调用一个可覆盖的虚方法,甚至可能触发派生类行为,这在后面会单独讲。

第三条,构造过程尽量短。不要在构造方法里做网络请求、大文件读取、复杂计算。有一次我见过有人把整个菜单权限配置加载塞进构造函数里,结果单元测试每创建一个对象就要连一次数据库,整个测试套件跑一个多小时。构造方法负责状态就绪,不负责重量级业务。

另外还有一点很多人容易忽略:构造失败怎么办。如果参数校验不通过,最干净的做法是直接抛异常,让对象创建失败,而不是返回一个“凑合能用但其实不完整”的对象。一个坏对象留在系统里,破坏力远大于一次异常。

2. 构造方法的重载与初始化细节:参数、默认值和语义

2.1 重载解决“同一个类,多种创建姿势”

现实中同一个类往往有不止一种创建方式。比如一个日期类,用户可能给你传年月日三个整数,也可能给你传"2024-05-01"这样的字符串,还可能给你传一个时间戳。如果只允许一种构造参数,调用方就非常痛苦。

不同语言解决这个问题的姿势不同。Java 靠方法重载,Python 靠默认参数加类方法,C++ 两者都能做。看一个 Python 的例子:

class Date: def __init__(self, year: int, month: int, day: int): self._validate(year, month, day) self.year = year self.month = month self.day = day @classmethod def from_string(cls, text: str) -> "Date": year, month, day = map(int, text.split("-")) return cls(year, month, day) @classmethod def from_timestamp(cls, ts: int) -> "Date": # 这里用时间戳转年月日,做一层转换 ... return cls(year, month, day)

对应的 Java 写法是:

public class Date { public Date(int year, int month, int day) { ... } public Date(String text) { // 解析 "2024-05-01" } public Date(long timestamp) { // 转换时间戳 } }

这种多构造方法设计的核心价值,是把“外部世界的各种输入格式”统一转换成“对象内部一致的状态”。调用方不需要知道内部怎么存日期,只需要从自己手上的数据格式出发,选出对应的创建入口。

2.2 初始化列表与构造器链的隐藏收益

如果你写 C++,还有一个很多人初次接触时不理解的细节:初始化列表。

class User { std::string name; int age; public: User(const std::string& n) : name(n), age(0) {} // 初始化列表 };

为什么不用name = n; age = 0;这种赋值写法?因为这两种写法背后的执行路径完全不同。使用初始化列表,name会直接以n作为初值构造一次;使用赋值写法,name会先被默认构造出一个空字符串,再被赋值成n。一次能完成的事,变成两次,既有开销,又可能踩到“成员没有默认构造函数导致无法赋值”的编译错误。

Java 里虽然没有初始化列表,但也有类似的“构造器链”约定:一个构造方法通过this(...)调用另一个构造方法,把公共初始化逻辑收拢到一处。Python 没有 Java 那样的重载,所以通常用默认参数 + 类方法组合,核心思想一样:初始化逻辑尽量单一,创建入口尽量多样。

2.3 “参数能少则少”不只是风格偏好

构造参数的数量,直接决定了调用方的使用成本。我见过一个配置类,构造函数挂了 17 个参数,调用处全靠注释才能勉强分清每个位置是什么。这已经不属于“构造方法设计”了,更像是一场灾难。

我的经验是:把那些对象能活所必需的东西放进构造参数,把那些“未来可能变化”的东西留给 setter 或配置方法。一个用户对象出生时必须有名字和角色,那么这两个就可以作为构造参数;头像路径、昵称、签名这些,则完全可以后续设置。

这样设计还有个附带好处:对象一旦构造完成,核心状态就不会被随意篡改,系统的不可变性变强,并发下的心智负担也小很多。

3. 析构方法不是为了“删除对象”,而是为了“归还资源”

3.1 先纠正一个最常见的误会

很多初学者把析构方法理解成“把对象从内存里删除”的方法。这个理解不准确。对象的实际内存回收,是由语言的运行时或编译器专门处理的,析构方法要做的是另一件事:在对象生命即将结束之前,给对象最后一次机会,清理它占用的外部资源。

典型资源包括:文件句柄、数据库连接、网络连接、锁、临时目录、缓冲区的底层句柄。这些资源不属于对象本身的内存,但对象活着时一直占着它们。如果对象消亡时没人去“归还”,这些资源就泄漏了。

打个比方,对象是一间客房,内存是房号,外部资源是房间里的钥匙和电器。客人退房时,服务员会来检查门窗、收回钥匙、关掉电器——这就是析构方法。最后把房号从系统里注销,那是另一个机制的事。

3.2 不同语言里的“临终时刻”

不同语言处理“对象临终”的机制差异很大,这个差异会直接影响你的代码怎么写。

语言/机制析构调用时机确定性典型场景
C++对象离开作用域或被 delete 时,必然调用析构函数高,完全确定RAII,资源随对象生命周期自动释放
Rust编译器在变量离开作用域时插入 Drop 调用高,完全确定所有权系统下的自动清理
Python引用计数归零时触发__del__,但不保证立即执行中,有循环引用时不确定不适合作为主要清理手段
Java对象被 GC 判定不可达后,可能调用 finalize,但已废弃低,不推荐资源清理应使用 try-with-resources
Go没有传统意义的析构,可用runtime.SetFinalizer低,不推荐主要靠显式 close 和 defer

我强烈建议把这张表记在心里。很多人把 Python 的__del__当成 C++ 析构函数用,结果在循环引用或者虚拟机退出顺序上踩了大坑。

3.3 一个真实的资源泄漏现场

早期我写过一段登录日志逻辑,每次登录都创建一个数据库连接,然后写完日志就扔在一边。表面上看没什么问题,Python 有垃圾回收,对象总会被回收吧?但数据库连接在对象真正被回收之前,一直打开着,而且连接池里的连接数有限,最直接的表现就是运行一段时间后,数据库“too many connections”报错。

正确的写法是把资源归还的责任落实到一个明确的位置:

def login(self, username: str): conn = get_connection() try: cursor = conn.cursor() cursor.execute("INSERT INTO login_log ...") conn.commit() finally: conn.close() # 无论成功失败,都归还连接

这里finally就是“析构思想”的简化版:不管中间发生什么,资源必须归还。

3.4 RAII 和 with 语句:把资源归还变成自动化

C++ 里最经典的资源管理手段叫 RAII(Resource Acquisition Is Initialization,资源获取即初始化)。名字有点绕,核心思想却很简单:让资源的生命周期绑定在某个局部对象的生命周期上,对象构造时获取资源,对象析构时自动释放资源。

class FileHandler { public: FileHandler(const char* path) { file_ = fopen(path, "r"); } ~FileHandler() { if (file_) fclose(file_); } private: FILE* file_; };

这样写完之后,函数里只要正常创建这个对象,什么都不用额外管,函数一结束资源就自动归还。这就是“不信任调用方记得清理”的设计思路。

Python 里的with open(...) as f,以及contextlib.closing,本质上也是这个思想的体现。你不在__del__里做关键清理,而是用一个显式上下文管理器把资源生命周期包住,这样释放时机由代码结构决定,而不是由垃圾回收的“心情”决定。

4. 生命周期里的连环坑:虚方法陷阱、析构顺序与循环引用

4.1 构造方法里调用可覆写的方法,会发生什么

这是一个老生常谈但值得反复讲的坑。先看 C++ 场景:

class Base { public: Base() { log(); } virtual void log() { std::cout << "Base\n"; } }; class Derived : public Base { private: std::string msg; public: Derived() : msg("hello") {} void log() override { std::cout << msg << "\n"; } };

当你创建一个Derived对象时,基类Base的构造函数会先执行,这时候它调用的log(),实际上还是Base的版本,不是Derived的版本,而且访问msg时根本不可能访问到,因为Derived的成员还没有被构造。

Java 的行为有所不同:Java 中的虚方法调用在构造方法里可以动态绑定,所以基类构造函数调用子类覆写方法时,会真的调用到子类版本——但此时子类字段都还是默认零值。两种语言的表象不同,底层原因相同:构造阶段,对象处于“不完整”状态,你不能按完整对象来操作它。

我处理这种问题的经验很简单:不要在构造函数里调用任何可覆写的实例方法。如果你想在构造时做多态行为,应该想办法把行为推迟到构造完成之后,或者通过参数注入。

4.2 析构顺序为什么容易引发崩溃

对象的析构顺序和构造顺序是反过来的。以 C++ 为例,一个派生类对象析构时,先执行派生类析构体的代码,再析构成员对象,最后执行基类析构。也就是说,先创建的部分后销毁,后创建的部分先销毁。

这个顺序本身没什么问题,但一旦对象之间存在“交叉引用”或“静态依赖”,就很容易出问题。比如 A 对象在析构时调用了一个全局 Logger 对象写日志,如果 Logger 对象已经先一步析构了,A 的析构函数就相当于拿着一个已经关闭的句柄去写数据,崩溃几乎不可避免。

应对思路是:析构方法里不要访问那些“可能已经不存在”的外部对象。全局对象、单例对象、跨模块共享的静态数据,都是高风险目标。如果必须在退出时做清理,最好提供一个显式的shutdown()或close()方法,由程序在受控时机调用。

4.3 循环引用让析构永远不触发

Python 引用计数机制下,两个对象互相引用,且各自定义了__del__,情况会变得非常微妙。引用计数永远不会归零,虽然现代 CPython 的循环收集器可以处理一部分,但“哪个对象的__del__先执行”无法保证,一旦两个析构逻辑互相依赖,就会出现难以预料的顺序错乱。

C++ 的shared_ptr也有类似的循环引用问题:两个对象各自持有对方的shared_ptr,引用计数永远到不了零,资源永久泄漏。这类问题的常见解法是打破环的一侧,改成“弱引用”,Python 用weakref,C++ 用weak_ptr。

class Node { std::shared_ptr<Node> next; std::weak_ptr<Node> prev; // 打破循环 };

这里prev用弱引用,表示“我引用你,但我的引用不会阻止你被销毁”。这是写带环数据结构时特别重要的设计决策,纯靠本能写代码,几乎必然会踩到这个坑。

5. 从构造到析构的完整排查实录:两个真实教训

5.1 教训一:临时对象在“不该死的时候”被回收

有一次我接手的项目里,偶发性地出现“文件锁错误”,频率不高,但一出现就会影响下一次写入。排查了很久,最后定位到一个包装类上:它构造时打开底层句柄,析构时会自动关闭句柄。

有一段代码为了图省事,把一个临时对象直接传给函数,临时对象的生命周期随着表达式结束就结束了。结果就是,句柄在析构方法里被提前关闭,而另一个模块还拿着这个句柄的副本在异步写入。表现为数据时不时写不进去,报“文件被占用”。

这个问题给我的教训是:不要在析构方法里做会影响外部系统状态的重操作,比如关句柄、删临时文件、发网络请求。资源释放应该被显式控制,而不是依赖对象消失这个“偶然事件”。析构方法更适合做轻量级兜底,除非你能保证对象的生死时机是完全确定的。

5.2 教训二:静态单例在程序退出时反复析构

另一个项目里,程序退出时经常崩溃,而且崩溃位置每次都不完全一样。查到最后,原因是一个全局的单例对象持有线程池,另一个全局对象在析构时又去访问这个单例。但全局对象的析构顺序在跨编译单元时是不确定的,有可能线程池已经被销毁,另一个对象的析构还在往里面提交任务。

解决方案也很直接:不要依赖全局析构顺序。把进程退出时的清理逻辑收敛到一个显式的入口,比如提供一个统一的Shutdown()函数,在主流程的最后一个受控调里执行,而不是放任各个对象的析构方法互相穿插。

5.3 让对象生命周期“可见”的三个调试习惯

排查这类问题,最有效的不是猜,而是让对象的生死变得可见。我常用的三个习惯:

第一个,在构造方法和析构方法里打日志,尤其在调试阶段。记录关键对象创建和销毁的顺序,配合时间戳,很快就能看出谁先谁后。但注意别在生产环境全量打,不然日志量会爆炸。

第二个,用一个全局计数器统计当前存活对象数量。构造方法里加一,析构方法里减一,定期查看这个数字。如果某个操作结束后计数器没有回到预期值,说明有泄漏的嫌疑。

第三个,打印创建时的调用栈。当对象出现在“不该出现的地方”时,直接从日志里看它是谁创建出来的、为什么还活着。这比翻业务代码推理省力得多。

断断续续整理完这些内容,我最大的体会是:构造方法和析构方法看起来是语法概念,实际上真正考的是“责任边界”。谁负责创建,谁就要保证对象从第一行代码起就处于完整状态;谁持有外部资源,谁就要在销毁前把它们安全交还。把这层账算清楚,很多偶发性 bug 会变得非常好查。希望这篇笔记能帮你少踩几年我踩过的坑。

相关新闻

Matlab实现BP神经网络多输入单输出回归预测及K折交叉验证

Matlab实现BP神经网络多输入单输出回归预测及K折交叉验证

多输入单输出回归预测&#xff0c;是我在实操里遇到频次最高的建模需求之一。手里攒了一堆自变量&#xff0c;想预测一个连续的因变量&#xff0c;数据不算多、关系又不线性&#xff0c;这时候BP神经网络往往是最省心的选择。它不像线性回归那样对模型形式要求严格&#xff0c;…

2026/10/19 21:21:08 阅读更多 →
大模型开发三大支柱:数据流水线、算法与基础设施实战解析

大模型开发三大支柱:数据流水线、算法与基础设施实战解析

这两年聊大模型开发&#xff0c;很多人都下意识盯着模型结构本身&#xff1a;用哪个开源底座、改多少层、参数量多大。可真到了自己跑项目&#xff0c;你会发现卡你的往往不是那几十行模型代码&#xff0c;而是数据怎么喂进去、训练怎么稳定不崩、多卡上怎么不浪费显存、迭代一…

2026/10/19 21:21:08 阅读更多 →
QT网络聊天室实战:从TCP粘包到文件分块传输

QT网络聊天室实战:从TCP粘包到文件分块传输

简介&#xff1a;基于QT的网络聊天室项目集实时聊天与文件传输于一体&#xff0c;面向C/Qt学习者、网络编程入门者以及需要GUI与通信综合实践的开发者。资源共54个文件&#xff0c;压缩包388KB&#xff0c;以cpp/h源代码、ui界面文件、qrc资源文件为主体&#xff0c;辅以png/jp…

2026/10/19 21:21:08 阅读更多 →

最新新闻

IM系统用户管理子服务架构设计与踩坑实践:从表结构到缓存策略

IM系统用户管理子服务架构设计与踩坑实践:从表结构到缓存策略

做IM系统&#xff0c;用户管理子服务往往是看起来最简单、坑却最多的那一块。很多人一开始会觉得&#xff0c;无非就是注册、登录、改个昵称、拉个好友列表&#xff0c;有什么好写的&#xff1f;但真正上手做才发现&#xff0c;一个IM系统里最基础的用户服务&#xff0c;牵扯到…

2026/10/19 22:03:35 阅读更多 →
DUT-OMRON数据集上的U-Net二值分割实战:边缘鲁棒与工业落地

DUT-OMRON数据集上的U-Net二值分割实战:边缘鲁棒与工业落地

简介&#xff1a;本资源是一套面向深度学习初学者与图像分割实践者的Unet多尺度分割实战项目&#xff0c;聚焦二值图像分割任务&#xff0c;特别适配DUT-OMRON数据集的端到端训练与推理需求。压缩包共2000个文件&#xff0c;主体为1979张PNG格式的训练/测试图像及对应mask&…

2026/10/19 22:03:35 阅读更多 →
XXE漏洞剖析:从XML外部实体注入到文件读取与SSRF防御

XXE漏洞剖析:从XML外部实体注入到文件读取与SSRF防御

大概在五年前做某次授权测试&#xff0c;目标系统有个不起眼的文件上传接口&#xff0c;响应报文里带着一段 XML 结构。当时随手改了改请求体&#xff0c;把实体声明换成了外部实体&#xff0c;结果直接把服务器上的/etc/passwd读了出来。那是我第一次真正意识到 XXE&#xff0…

2026/10/19 22:02:34 阅读更多 →
编译通过≠代码健康:C++静态检测原理与工具落地指南

编译通过≠代码健康:C++静态检测原理与工具落地指南

编译通过不等于代码健康&#xff1a;C静态检测到底在查什么我做C开发这些年&#xff0c;越来越觉得一个道理&#xff1a;编译器不报错&#xff0c;只是说你的代码符合语法和类型规则&#xff0c;它压根不关心你的逻辑是不是对的、指针是不是悬空的、资源有没有泄漏。很多问题要…

2026/10/19 22:02:34 阅读更多 →
基于Prometheus的MySQL自适应优化系统:从手工调参到自动闭环

基于Prometheus的MySQL自适应优化系统:从手工调参到自动闭环

1. 起因&#xff1a;静态参数与动态负载之间的拧巴 我维护的一套订单查询系统&#xff0c;跑在两个8C16G的实例上&#xff0c;平时QPS稳定在两千左右&#xff0c;配置也自认为调得“差不多了”。某天凌晨两点多&#xff0c;告警平台连续弹了三条&#xff1a; Threads_connecte…

2026/10/19 22:01:31 阅读更多 →
基于Python实现可靠数据传输协议:从停等到滑动窗口的完整课设指南

基于Python实现可靠数据传输协议:从停等到滑动窗口的完整课设指南

简介&#xff1a;这是计算机网络课程设计场景下的一份Python实现资源包&#xff0c;围绕UDP设计可靠数据传输协议&#xff0c;完整覆盖停等协议、GBN和SR的改进思路。内容对应实验要求&#xff0c;包括单向可靠传输、丢包模拟、双向数据传输以及基于协议的C/S文件传输应用&…

2026/10/19 22:01:31 阅读更多 →

日新闻

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

频率跟踪型PWM电源设计:臭氧发生器谐振点漂移与软开关实现

简介&#xff1a;本资源为电源技术方向的学术研究文档&#xff0c;聚焦介质阻挡放电型臭氧发生器电源的频率跟踪型PWM控制策略&#xff0c;面向电力电子、高压电源及臭氧应用领域的科研人员与工程技术人员。针对负载呈容性、等效电容随外加电压升高而增大、谐振频率随之漂移的难…

2026/10/19 0:00:47 阅读更多 →
Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

Vibe Coding 遇上 TaoToken:把开发者思维圣殿的灵感流接入统一 Key 通道

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 0:00:47 阅读更多 →
HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

HundredCV-Chat话题建模实战教程:利用4,871个内置话题标签构建主题分析系统

【免费下载链接】HundredCV-Chat 项目地址&#xff1a; https://ai.gitcode.com/hf_mirrors/Jax-dan/HundredCV-Chat 点击查看 免费下载 HundredCV-Chat 是一个由 100 位青年简历数据开发而来的中文多轮对话数据集&#xff0c;共包含 24,750 组日常闲聊对话和 4,871 个话题标签…

2026/10/19 0:00:47 阅读更多 →

周新闻

基于GMM的语音识别系统原理与MATLAB实现详解

基于GMM的语音识别系统原理与MATLAB实现详解

说到基于高斯混合模型&#xff08;GMM&#xff09;的语音识别系统&#xff0c;很多人第一反应就是一堆数学公式&#xff0c;觉得门槛很高。但如果你真的在MATLAB里把它落地跑通&#xff0c;会发现核心其实就是三件事&#xff1a;把声音变成特征向量&#xff0c;用GMM给每个类别…

2026/10/19 10:02:04 阅读更多 →
数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理入门:反白、灰度化、调色板修改与导出txt实践

数字图像处理这四个字听起来挺唬人&#xff0c;但落到具体操作上&#xff0c;无非就是让计算机按你的想法“摆弄”像素。我在一个模拟项目X里用了一套特别经典的基础处理流程&#xff1a;对图像做反白、改调色板、彩色转灰度&#xff0c;最后把像素数据导出成txt文件。单看每一…

2026/10/19 17:32:41 阅读更多 →
机器人大脑技术盘点:具身智能的感知、决策与数据闭环

机器人大脑技术盘点:具身智能的感知、决策与数据闭环

如果你的信息流最近也天天刷到“机器人大脑”这个词&#xff0c;你会发现一个很有意思的现象&#xff1a;每个人说的“大脑”根本不是同一个东西。有人指的是大语言模型&#xff0c;有人指的是运动控制系统&#xff0c;还有人指的是能理解物理世界的端到端模型。在2026年这个时…

2026/10/19 1:30:52 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 2:54:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/19 9:10:22 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/18 12:16:16 阅读更多 →