C++构造与析构全解析:深浅拷贝、初始化列表与RAII避坑指南

类和对象讲到中期,最绕不开的两个成员函数就是构造函数和析构函数。你可以把类当成一张图纸,对象是按照图纸盖起来的房子,构造函数是开工时的地基浇筑和剪彩仪式,析构函数是拆除前的安全检查和管线回收——没有这两样,盖房和拆房全靠运气,这在C++里是绝对走不远的。

构造函数决定了对象出生时带什么数据、资源从哪来;析构函数决定了对象消失时资源怎么还回去。很多人学完“类的基本语法”就卡在这里:为什么编译器偶尔报“没有合适的构造函数”?为什么同一个对象析构两次?为什么明明delete了还泄漏?这篇就集中把构造与析构这两个成员函数讲透。适合刚学完对象基础语法、想把类写得“能用又安全”的同学;被深浅拷贝坑过的老手也值得扫一遍常见错误清单。

1. 构造函数:对象出生前的“第一件事”

1.1 构造函数到底在解决什么问题

一个类除了成员变量和成员函数,编译器还悄悄给每个类都准备了一组“默认成员函数”:默认构造、析构、拷贝构造、拷贝赋值,C++11之后又多了移动构造和移动赋值。这六个函数不写,编译器在需要时会自动生成;但写错其中一个,往往比不写还危险。

构造函数的名字和类名完全相同,没有返回值——不是void,是真的什么都不返回。它可以重载,所以你才能写出这种写法:

class Student { public: Student() { // 什么都不干 } Student(const char* name, int id) { // 带两个参数 } };

构造函数最核心的任务只有一个:把对象里每一个成员变量设置成合理状态。这句话说起来很轻,做起来要命——因为绝大多数BUG都来自“对象出生时状态就不对”。我见过新手写一个类,成员是int age,构造函数忘了初始化,打印出的年龄直接是垃圾值。在Debug版里可能还能稳定复现,Release版里直接时好时坏,非常难查。

一个重要的坑先放在这:只要你手动写了任何一个构造函数,编译器就不再替你合成默认构造函数。也就是说,你定义了一个带参构造后,再写Student s;,编译器直接报错“没有默认构造函数”。很多人刚学的时候在这里卡一晚上。解决办法是显式补一个无参构造,或者给带参构造的参数全部用缺省值。

1.2 初始化列表:这才是“初始化”的正确姿势

接上面的Student,更专业的写法是用初始化列表:

class Student { public: Student() : name("unknown"), id(0) {} Student(const char* nm, int nid) : name(nm ? nm : "unknown"), id(nid) {} private: const char* name; int id; };

这里有两个细节值得讲清楚。其一,初始化列表是在进入构造函数体之前执行的,成员在这里直接被初始化;而在构造体内写name = ...,本质是先默认初始化成员,再赋值,多一次操作。对int、指针这种廉价类型差别不大,但对string、vector这类带资源的类型,先默认构造再赋值,白白多一次构造和析构的开销。其二,有些成员根本不能靠体内赋值,比如const成员和引用成员:它们必须在初始化列表里初始化,体内赋值是违法的,因为const一旦初始化就不可变,引用一旦绑定就不可换。

还有更隐蔽的一点:初始化顺序由成员声明顺序决定,不是由初始化列表里谁先谁后决定。假设你声明了a和b两个成员,但列表里写b先、a后,编译器按a再b初始化,如果你在初始化a时依赖b的初始值,就会拿到半成品。这类问题编译器不警告,运行结果又常常“碰巧对”,等换编译器或换平台就翻车。经验法则:初始化列表的书写顺序和成员声明顺序保持一致,不要靠记忆,要用代码风格约束。

class Demo { int a; int b; public: Demo(int x) : b(x), a(b) {} // 危险:a比b先初始化,此时b还没被赋值 };

我把上面这个代码放到某个聊天群里问过,一半人第一眼看不出问题——这正好说明顺序问题有多容易被忽略。

2. 拷贝构造与深浅拷贝:C++新手最容易栽跟头的地方

2.1 拷贝构造函数在哪些时候被偷偷调用

拷贝构造的名字是类名(const 类名& other),它根据一个已有对象创建一个新对象。哪些场景会触发它?三个高频场景:

  • T t2 = t1;——用一个对象初始化另一个对象,走拷贝构造;
  • 函数传值,形参是T类型,实参传进来时会用实参拷贝一份临时形参;
  • 函数返回T类型,把局部对象返回给调用方时,需要用拷贝生成返回值。

注意第三点很容易被混淆:C++编译器普遍做返回值优化(RVO/NRVO),常见写法下明明看到构造函数只调了一次,不代表它没这个能力。写代码时不能依赖“反正编译器会优化”来偷懒,资源管理必须按最坏情况设计。

同样一个等号,还要区分拷贝构造和拷贝赋值:

Student s1; Student s2 = s1; // 拷贝构造:s2还不存在,从s1构造出来 Student s3; s3 = s1; // 拷贝赋值:s3已存在,把s1的状态赋给s3

新手搞不清这两个概念,于是出现了很经典的“对象还没创建好就operator=”,编译器一脸懵地报错。

2.2 默认的浅拷贝到底坑在哪里

重点来了。类里如果只有int、double这类内部类型,编译器生成的默认拷贝构造完全够用。可一旦成员里有裸指针,默认拷贝构造是逐字节复制指针的值。换句话说,两个对象的指针成员指向同一块内存。这就是浅拷贝。

看这个最简单的类:

class StringBuf { public: StringBuf(const char* s) { len = strlen(s); data = new char[len + 1]; strcpy(data, s); } ~StringBuf() { delete[] data; } private: char* data; int len; };

你写StringBuf a("hello"); StringBuf b = a;之后,a和b里的data都指向同一段堆内存。等a和b各自析构,delete[]执行两次。同一块内存被释放两次,这在大部分平台上直接崩,少部分平台表现为“内存被系统回收后再被写坏”,各种诡异崩溃都来了。

解决办法是显式写深拷贝构造,把指针指向的数据复制一遍,而不是复制指针本身:

StringBuf(const StringBuf& other) { len = other.len; data = new char[len + 1]; strcpy(data, other.data); }

深拷贝的代价是新分配一份内存,但语义上完全独立,两个对象各管各的,谁析构都不影响另一个。这是“值语义”在C++里最重要的体现。

2.3 拷贝赋值运算符与“三/五法则”

有拷贝构造,自然还要有拷贝赋值运算符,否则你写完StringBuf a; StringBuf b; b = a;,又回到浅拷贝的路子。赋值和构造的差异决定了它的写法:被赋值的对象已经存在,里面可能还持有旧资源,所以要先释放旧的,再拷贝新的。

StringBuf& operator=(const StringBuf& other) { if (this == &other) { return *this; } delete[] data; len = other.len; data = new char[len + 1]; strcpy(data, other.data); return *this; }

四件事缺一不可:判断自赋值、释放旧资源、深拷贝新资源、返回*this。自赋值判断看起来多余,但b = a;这种写法你永远不知道哪天会出现a自身赋值的循环;先delete再new的顺序也不要换,否则自赋值时你删了对方唯一的资源,然后还没东西可拷。

三条法则说到这里就成型了:如果你要写析构函数(说明有需要释放的资源),那拷贝构造和拷贝赋值通常也必须一起写,这就是经典的三法则。C++11之后加入移动构造和移动赋值,扩展成五法则。移动语义后面再展开,现在先把三条记住,能帮你躲开一大半内存错误。

3. 析构函数:对象离场时的收尾工作

3.1 析构函数的基本规则和调用时机

析构函数名字是~类名(),没有参数、没有返回值、不能重载、一个类最多一个。它在对象生命周期结束时被自动调用:

  • 栈上对象:离开作用域时调用;
  • 堆上对象:delete时调用;
  • 临时对象:所在的完整表达式结束时调用;
  • 程序正常退出:全局对象和静态对象的析构在main之后执行。

析构函数最常见的用途就是释放资源:delete掉new出来的内存、关闭打开的文件、断开网络连接、归还锁。不看不知道,一看吓一跳,很多人写第一个LinkedList的时候,析构函数就是一片空白,然后每次程序跑完,系统报内存泄漏一大堆。

手动调用析构函数几乎是要避免的。唯一合理的场景是placement new配合手动析构,那属于内存池这类底层玩法,业务代码里基本碰不到。日常写代码,只需要保证每个栈对象自动析构,堆对象在delete时析构,其他情况交给语言规则。

3.2 资源管理与RAII:用对象生命周期管理资源

如果只让我选一个C++最重要的理念,我选RAII:资源获取即初始化。意思是把资源的获取放在构造函数里,把资源的释放放在析构函数里,让资源生命周期与对象生命周期完全绑定。文件、锁、网络句柄、内存,都该包进这样的对象里。

比如一个简化版的锁管理类:

class LockGuard { public: LockGuard(SomeMutex& m) : mutex_(m) { mutex_.lock(); } ~LockGuard() { mutex_.unlock(); } private: SomeMutex& mutex_; };

在函数里写LockGuard guard(mutex);,函数任何分支return、抛异常,析构函数都会执行,锁一定被释放。这就是为什么现代C++反复强调“不要裸new、裸delete,交给容器和智能指针",背后全是RAII:把资源放进一个对象里,然后忘记释放这件事,它自己会管好自己。

从这个角度看,析构函数不是“对象销毁前的清理仪式”,而是“对象作为资源管理器的退出机制”。想通这一点,设计类时的思路会清晰很多:先把成员变量想明白,谁拥有资源、谁负责释放,构造函数和析构函数自然水到渠成。

3.3 析构函数里的三个高频坑

第一个坑:析构函数不释放私有资源。类里new了一块缓冲区,析构却留空,跑几次程序就泄漏。我有段时间排查一个进程内存持续增长,最后发现是某个日志输出类析构没释放,每输出一次泄漏几百字节,压力测试跑半天内存就越界。教训是:类里有哪些new,析构里就要有对应的delete。

第二个坑:多态场景下析构函数不是virtual。基类有虚函数,派生类里也持有资源,但基类析构没有virtual。delete基类指针指向的派生类对象时,因为对象的静态类型是基类,编译器只调用基类析构,派生类的资源不释放,直接泄漏一半。写法上:只要这个类设计成要被继承(有virtual函数),就顺手把析构写成virtual。这条建议是强制性的,不是可选项。

第三个坑:析构函数抛异常。两个对象的析构如果都抛异常,程序会直接触发异常处理规则终止运行。更麻烦的是,一个对象析构抛异常时,其余成员可能没法正常析构。我的原则:析构函数里永远不要抛异常,遇到错误要么记录日志,要么吞掉,实在不行就让进程终止。C++11之后的默认行为是析构函数隐式noexcept,你要是不小心让它抛出来,编译器会直接告诉你:析构默认不能抛,除非你显式声明。

4. 话分两头:构造顺序、析构顺序与移动构造

很多人的理解停留在“构造和析构就是两个函数,写出来就行”,但实际工程里还有几个细节很容易漏,漏了就要踩坑。我把构造顺序、析构顺序、委托构造、移动构造逐一提出来讲。

4.1 构造顺序与析构顺序

对象析构顺序这一点,许多新手第一次写多个对象时就懵了。假设类里有两个成员,编译器构造时按声明顺序构造成员,再执行构造函数体;析构时则是先执行析构函数体,再按声明逆序析构成员。多个对象在同一个作用域时,遵循“后构造的先析构”规律。

class Order { public: Order() = default; ~Order() = default; private: std::string s1; // 先构造 std::string s2; // 后构造 }; // 析构顺序:s2先,s1后

继承场景则按“基类先构造、派生类再构造;派生类先析构、基类后析构”的顺序。这也解释了为什么析构函数必须virtual这件事:基类指针delete时,语法确保会沿着派生类到基类调用整条析构链。

4.2 委托构造函数

C++11之后,构造函数可以写在另一个构造函数里调用另一个构造函数,叫委托构造。典型场景是多个构造函数做同样的初始化逻辑:

class Student { public: Student() : Student("unknown", 0) {} Student(const std::string& nm, int i) : name(nm), id(i) { // 只有这个真实构造在这里工作 } };

优点是把重复代码收拢,缺点是稍微增加理解成本。一个细节:委托目标先执行完,当前构造函数体再执行;多个构造函数之间不能互相委托成环,否则编译期就报错。我见过有人试图在三个构造函数间互相委托,最后连自己都绕晕了,不知道为什么构造不出来。

4.3 移动构造:资源转移的特殊构造函数

移动构造解决的是“返回大对象、传播大对象时还深拷贝一份,实在太浪费”的问题。它的参数是右值引用,语义是从源对象“偷走”资源而不是复制:

StringBuf(StringBuf&& other) noexcept : data(other.data), len(other.len) { other.data = nullptr; other.len = 0; }

核心操作是把源对象的指针直接拿走,然后把源对象置为空,确保源对象的析构函数不会重复delete。这里藏着两个高价值的细节。第一,必须把源对象的data置空,否则源对象析构时会把已经属于新对象的内存释放掉,新对象再析构时double free。第二,最好标记noexcept:标准库容器比如vector扩容时,如果移动构造是noexcept的就用移动,否则宁可退回拷贝,因为移动可能抛异常会导致容器不具备强异常安全。这个标记在性能敏感的代码里差异非常显著。

移动语义展开其实够写单独一篇,这里先把方向立住:移动构造是五法则成员之一,写之前想清楚你到底要“值语义深拷贝”还是“资源转移”。

5. 常见问题与排查技巧实录

5.1 我见过的高频报错速查表

写个小抄方便直接查:

现象大概率原因对策
对象声明就报“没有默认构造函数”手动写了带参构造,编译器不再自动生成默认构造显式补默认构造,或给参数全加缺省值
指向同一块内存的两个指针同一时刻被释放,崩溃默认浅拷贝造成只拷贝了指针写深拷贝构造 + 赋值
析构函数执行后再次访问对象成员悬空引用/悬空指针检查返回值和生命周期
多态delete后派生类资源没释放基类析构不是virtual基类析构加virtual
容器扩容后对象数量莫名其妙变多/性能明显变慢移动构造未标noexcept或未提供给移动构造加noexcept
拷贝过去后又改了某对象数据,另一个对象也变裸指针浅拷贝语义转深拷贝或改用标准库容器

这表里面每一条我都见过不止一个真实案例,本质上都是对默认成员函数的生成规则和资源所有权理解不到位。

5.2 构造析构的调试技巧

调试构造和析构,最高效的经验是“打日志 + 计数”。在构造函数和析构函数里分别打印进入和退出,再配合一个静态计数器统计当前存活对象个数,很多资源管理问题一眼就看穿:

static int alive = 0; Foo::Foo() { ++alive; std::cout << "Foo() alive=" << alive << "\n"; } Foo::~Foo() { --alive; std::cout << "~Foo() alive=" << alive << "\n"; }

如果alive一直涨,说明某处泄漏;如果alive变化符合“先进后出”曲线,说明析构顺序是正常的;如果某个对象析构函数被调用了两次,那基本就是浅拷贝问题。我在排查一个长期运行的服务端程序时就是靠这种计数,把泄漏的类从几百个候选里快速筛出来。

5.3 几条避免踩坑的实操建议

最后给几条实际经验:

  • 成员优先用标准库容器和智能指针,能不自管裸指针就不自管。
  • 一个类只要手动写了析构函数,就先停下来想想:拷贝构造和拷贝赋值是不是也要一起写?如果不想写,干脆用= delete禁用拷贝,避免意外复制。
  • new和delete要配对,哪里new哪里delete,尽量缩小裸指针的作用域。
  • 开始动手写类之前,先画一下成员资源的归属:谁创建、谁释放、对象间是否允许共享。
  • 排查内存崩溃,优先怀疑“两个对象共享同一块资源”,而不是怀疑内存被写坏,因为后者出现的概率远低于前者。

这样养成习惯后,构造与析构这两个函数写起来会越来越有把握,踩坑概率直线下降。

我个人实际操作中的体会是:构造函数和析构函数表面上是两个函数,实质上是类和对象资源管理的一体两端。把“对象出生时数据如何初始化、对象销毁时资源如何释放”想明白,C++里很多看起来吓人的崩溃,其实都是这两个函数没有写对。再多说一个小技巧:给类写构造函数时,我习惯先把所有成员变量列一遍,逐个标注是“值类型、引用还是指针、是否需要独占资源”,然后按列表决定要不要写析构和拷贝;这个动作花不了两分钟,却能避免掉绝大多数资源泄漏和double free。下一篇再聊运算符重载与类型转换也不迟——你会发现,今天的构造与析构基本功,就是那天所有“奇怪语法”的地基。

相关新闻

扣子智能体部署实战:从源码到公网回调的完整指南

扣子智能体部署实战:从源码到公网回调的完整指南

简介&#xff1a;这份资源是面向AI智能体开发初学者的扣子&#xff08;Coze&#xff09;平台部署实操包&#xff0c;由字节跳动推出的零代码/低代码平台承载&#xff0c;帮助没有编程基础的用户快速完成智能体从创建到发布的全流程。内容围绕陪伴机器人案例展开&#xff0c;涵盖…

2026/10/20 7:54:18 阅读更多 →
AI生成Dashboard代码实战:契约驱动与插件化架构

AI生成Dashboard代码实战:契约驱动与插件化架构

1. 为什么我决定把Dashboard的代码交给AI来写1.1 一个让我彻底改变想法的下午去年有段时间我同时在维护三个内部工具的后台面板&#xff0c;一个是给运营看的数据概览&#xff0c;一个是给客服用的工单统计&#xff0c;还有一个是给运维盯的接口健康度监控。三个面板长得不一样…

2026/10/20 7:54:18 阅读更多 →
SLSPC高阶PT-WPT系统Simulink建模复现全流程解析

SLSPC高阶PT-WPT系统Simulink建模复现全流程解析

做无线电能传输方向的人&#xff0c;估计这两年多多少少都见过PT-WPT这个词。如果是准备复现一类基于SLSPC系列的高阶PT-WPT系统研究论文&#xff0c;那大概率躲不开Simulink建模这一步。我前段时间刚把这么一套系统从参数推演到波形对标完整跑通&#xff0c;过程中踩的坑比预想…

2026/10/20 7:54:18 阅读更多 →

最新新闻

数据库慢排查实战:五步定位慢查询与锁等待问题

数据库慢排查实战:五步定位慢查询与锁等待问题

数据库又慢了。后台报警刷屏&#xff0c;接口超时&#xff0c;前端白转圈&#xff0c;老板在群里问“怎么回事”&#xff0c;你打开终端&#xff0c;脑子一片空白——这种情况干过线上运维的人都不陌生。数据库慢是一个现象&#xff0c;不是结论&#xff0c;你不可能听它说“我…

2026/10/20 15:07:44 阅读更多 →
MySQL锁机制全解析:从全局锁到行级锁,掌握锁等待与死锁排查

MySQL锁机制全解析:从全局锁到行级锁,掌握锁等待与死锁排查

凌晨两点&#xff0c;我被一条告警短信叫醒&#xff1a;核心订单库的更新成功率掉到了40%。登录备库一看&#xff0c;show processlist里一大片线程卡在Waiting for table metadata lock&#xff0c;后面还排着一大串正在执行的SELECT。经验告诉我&#xff0c;这一定是某个“顺…

2026/10/20 15:07:44 阅读更多 →
用代码生成PPT:基于Markdown与结构化数据的幻灯片工作流实践

用代码生成PPT:基于Markdown与结构化数据的幻灯片工作流实践

1. 为什么我又造了一个PPT工具轮子先坦白一件事&#xff1a;我做过不下二十套PPT模板&#xff0c;从年终汇报、项目复盘到产品路演&#xff0c;几乎每个季度都要跟幻灯片打交道。市面上现成的工具用了不少&#xff0c;在线协作平台也试过一圈&#xff0c;但每次遇到"要把一…

2026/10/20 15:07:44 阅读更多 →
数据库慢时最先排查的5个关键点(线上经验总结)

数据库慢时最先排查的5个关键点(线上经验总结)

数据库慢的时候&#xff0c;我最先看哪 5 个地方&#xff1f;&#xff08;线上经验总结&#xff09;先说个真实场景&#xff1a;某天凌晨 1 点&#xff0c;线上告警突然响起来&#xff0c;接口超时率从 0.5% 飙到 15%&#xff0c;数据库 CPU 使用率直接顶到 99%。群里一片慌乱&…

2026/10/20 15:07:44 阅读更多 →
Eclipse RCP多页编辑器实战:构建可扩展的桌面工作台

Eclipse RCP多页编辑器实战:构建可扩展的桌面工作台

1. 这不是普通编辑器&#xff0c;而是可插拔的“工作台级”编辑界面 你有没有遇到过这样的场景&#xff1a;在开发一个数据处理工具时&#xff0c;用户既要查看原始JSON结构&#xff0c;又要切换到表格视图做字段筛选&#xff0c;还得临时写一段校验脚本——结果只能开三个独立…

2026/10/20 15:07:44 阅读更多 →
继续教育毕业论文如何用AI工具?9大平台实测与避坑指南

继续教育毕业论文如何用AI工具?9大平台实测与避坑指南

毕业论文季一到&#xff0c;私信里问得最集中的问题就是&#xff1a;继续教育学生到底能不能用AI工具写论文&#xff1f;市面上关于AI论文平台的文章&#xff0c;很多就是丢一份名单&#xff0c;告诉你谁谁谁很强&#xff0c;但真正站在在职学习者角度&#xff0c;把平台类型、…

2026/10/20 15:06:42 阅读更多 →

日新闻

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

CLRC663芯片Pin-to-Pin替代实战:从评估到量产全记录

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

2026/10/20 1:01:30 阅读更多 →
ESP32舵机控制指南:原理、多路驱动与避坑实战

ESP32舵机控制指南:原理、多路驱动与避坑实战

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

2026/10/20 1:01:30 阅读更多 →
位移法实现一阶低通滤波:单片机ADC采样降噪实战

位移法实现一阶低通滤波:单片机ADC采样降噪实战

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

2026/10/20 1:01:30 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/20 2:50:50 阅读更多 →

月新闻

我发现了一个新思路:用 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/20 10:02:08 阅读更多 →
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/20 9:50:17 阅读更多 →
黑夜航拍船只数据集训练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/20 9:50:45 阅读更多 →