C++进阶篇(二):多态——虚函数、重写、虚函数表与三个经典坑(附完整代码仓库)

0.1 概述&序言

这里是白杨。上一篇继承的结尾留了一句话:想搞多态,必须继承。这一篇就来兑现——多态。

如果把继承比作"类怎么长"(谁是谁的爸),多态解决的就是"同一份调用代码,怎么跑出不同的行为"。这两章是绑在一起考的,面试里也常被连招:先问继承的坑,再问多态的构成,最后甩一道"输出什么"的选择题。

这篇要过四件事:

  • 多态的三个构成条件(附一个"值传递翻车"的反例)
  • 重写的细节:override / final / 协变,以及重载、重写、隐藏三兄弟的区别
  • 抽象类:纯虚函数= 0
  • 把原理摊开:虚函数表(vfptr / vftable)+ 三个经典坑(默认参数、虚析构、构造中调虚函数)

老规矩,跑得通的结果才写进来(环境见下);完整代码文末自取,也可以直接去仓库:gitee.com/BaiYang_SQ/poly-sim。

实测环境:w64devkit g++ 15.2.0(-std=c++11 -O2 -Wall -Wextra)+ VS2022;教学简化版。
文中代码均为本人手写并实测;官方文档:cppreference·virtual 说明符。

让我们开始吧。

一、什么是多态:同一份代码,三种票

多态这个词听着玄,场景很朴素——买票。普通人全价、学生打折、军人优先:动作都叫"买票",不同的人做,行为不同。

C++ 里的多态分两种:

  • 静态多态(编译时):函数重载、函数模板——编译阶段就把调用的函数定好了;
  • 动态多态(运行时):虚函数 + 继承——运行时根据对象的"真实身份"决定调谁。

这篇讲的都是后者。先看一段代码:

classPerson{public:virtualvoidBuyTicket(){cout<<"买票-全价"<<endl;}};classStudent:publicPerson{public:voidBuyTicket()override{cout<<"买票-打折"<<endl;}};classSoldier:publicPerson{public:voidBuyTicket()override{cout<<"买票-优先"<<endl;}};voidFunc(Person&ptr)// 基类引用调用{ptr.BuyTicket();}

Func收到的都是Person&,代码里也只有一句ptr.BuyTicket()——可传进去的对象不同,结果完全不同。实测:

买票-全价 买票-打折 买票-优先

【一句话记】多态 = 同一份调用代码、不同行为:指向谁(谁的对象),就调谁(谁的函数)。

二、构成条件:三个都要满足

  1. 必须通过基类的指针或引用调用——不能是值传递;
  2. 被调用的函数必须是虚函数(基类里加virtual);
  3. 派生类必须完成重写(覆盖)——返回值、函数名、参数全相同。

补一句虚函数的定义:类成员函数前面加virtual就是虚函数;非成员函数不能加virtual修饰——后半句是选择题常点的名。

第 1 条为什么强调"不能值传递"?来看一组对照实验。同一个虚函数、同样的重写,区别只在Func的参数:

voidFuncRef(Person&ptr){ptr.BuyTicket();}// 引用:构成多态voidFuncValue(Person ptr){ptr.BuyTicket();}// 值传递:不构成

实测输出:

[引用版 ] 买票-全价 [引用版 ] 买票-打折 [值传递版] 买票-全价 [值传递版] 买票-全价 (值传递把 st 切成了 Person,多态消失)

值传递版把 Student 对象"切"成了一个 Person 对象(切片,上篇讲过的老朋友)——ptr里根本没有 Student 的部分,多态从哪来?

【必背】三条件:基类指针/引用 + 虚函数 + 重写,缺一不可。

三、重写的细节:三同、override 与协变

重写(覆盖)的"三同":返回值、函数名、参数列表完全相同。

有个考试爱考的坑:派生类重写时不写virtual也构成重写——因为虚函数的属性会一路继承下去。但规范写法建议写上,语义更清楚。

写重写最容易出的事故是"差一点没写上":函数名敲错一个字母、参数少个const……编译器不报错,运行时才发现"多态没生效"。C++11 给的解法是override:

classBenz:publicCar{public:voidDrive()override{cout<<"Benz-舒适"<<endl;}// 如果 Drive 没真的重写上任何基类虚函数,编译器直接报错(C3668)};

配套的还有final:加在虚函数后面,禁止派生类再重写它;加在类名后面,禁止整个类被继承。

还有个冷门细节:协变——派生类虚函数返回"派生类对象的指针/引用"、基类虚函数返回"基类对象的指针/引用"时,也算重写:

classPBase{public:virtualA*BuyTicket(){...}};classPStudent:publicPBase{public:B*BuyTicket()override{...}// B 是 A 的派生类 → 协变};

实测输出买票-全价(返回 A*)/买票-打折(返回 B*)。协变实际意义不大,了解即可。

【面试】override的价值一句话:把"运行时的莫名其妙"变成"编译期的明确报错"。

四、重载 / 重写 / 隐藏:一表定三兄弟

这三个概念是面试的常客,判断标准其实就一张表:

对比项重载重写(覆盖)隐藏
作用域同一个父类 / 子类各一个父类 / 子类各一个
函数名相同相同相同
参数列表不同(类型或个数)相同不同(不构成重写)
是否虚函数无关都必须是虚函数不构成重写就是隐藏
附加规则返回值可同可不同返回值相同(协变例外)成员变量同名也算隐藏

跑一遍实测(每个概念一个例子):

[重载] 3 / 4 Derive::func Derive::other(int): 10 Base::other 基类 _num=111,派生 _num=999(变量同名隐藏)
  • 重载:Calc::Add(int,int)和Calc::Add(double,double),同作用域、参数不同;
  • 重写:Derive::func重写了Base::func,通过Base*调用照样走到派生类版本;
  • 隐藏:Derive::other(int)和Base::other()同名——它俩不构成重写(参数不同),基类版本被藏起来。想调基类版必须写d.Base::other()。

【必背】判断口诀:同作用域看参数 → 重载;父子都虚且三同 → 重写;剩下同名的一律叫隐藏。隐藏就是"重写没写成"的事故现场。

五、抽象类:纯虚函数 = 0

虚函数后面写上= 0,就成了纯虚函数——只声明、没有实现,等着派生类去重写。含有纯虚函数的类叫抽象类,它有两个规矩:

  • 不能实例化对象(Car car;会报error C2259:无法实例化抽象类);
  • 派生类如果不重写纯虚函数,那它也是抽象类。
classCar{public:virtualvoidDrive()const=0;// 纯虚函数};voidFunc(constCar&p){p.Drive();}intmain(){Func(Benz());Func(BMW());Func(WENJIE());}

实测输出:

Benz-舒适 BMW-操控 WENJIE-智能

纯虚函数某种程度上是"强制重写":不写,连对象都造不出来。它划出的是接口——基类只规定"必须有这个行为",具体怎么做各派生类自己定。

【一句话记】抽象类 = 一份接口规范;纯虚函数 = 必须交作业。

六、原理:vfptr 与虚函数表

到最硬核的一节了。多态到底怎么实现的?——虚函数表。

先说对象的内存布局。一个含虚函数的类,编译器会在对象里塞一个隐藏指针——虚函数表指针__vfptr(v=virtual,f=function),一般放在对象最前面。它指向一张表,表里存着这个类所有虚函数的地址。

拿一道经典题实测:sizeof 多少?

sizeof(NoVirt) = 8 字节(无虚函数) sizeof(HasVirt) = 16 字节(有虚函数,多出一个 vfptr)

x64 平台下就是"多出一个 8 字节指针"。再验证虚表的两条规律(用 memcpy 安全地读对象头 8 字节):

vfptr(s1) == vfptr(s2) ? 1(同类型的对象共用一张虚表) vfptr(p1) == vfptr(s1) ? 0(各类型各有独立的虚表)

虚表的构成:派生类的虚表 = 从基类继承的虚函数地址 + 被重写函数的地址被覆盖+ 自己新增的虚函数地址。(表尾要不要放0x00000000之类的终止标记,是各编译器自己的安排——后面多继承的实测里,第二张表的越界槽位读到的就是 0。属实现细节,不用背。)

运行时发生了什么?ptr->BuyTicket()的步骤:

  1. 取ptr指向对象头部的__vfptr;
  2. __vfptr找到这张虚表;
  3. 取表里对应位置的函数地址;
  4. call这个地址。

满足多态条件的调用叫动态绑定(运行时才定地址,汇编里是"间接 call");不满足的(比如非虚函数、值传递对象调用)叫静态绑定(编译时就写死call 函数地址)。

【面试】多态调用为什么比普通调用"慢一点点"?——多了一次取指针、查表、间接跳转;这也解释了为什么 C++ 不把所有函数都设成虚函数。

先回答一个追问题:虚表存在哪?C++ 标准说"没规定"(这是编译器实现细节),那就实测看看——顺带把栈、静态区、堆、常量区一起打出来对比:

栈 : 0x3daadff74c 静态区: 0x7ff7975fa000 堆 : 0x159268513d0 常量区: 0x7ff7975fe625 虚表 : 0x7ff7976096c0(数值上贴近常量区——C++ 标准没规定虚表存哪,这是编译器实现细节)

栈和堆各占一段,虚表地址贴着常量区/静态数据段——所以通常说它放在"只读数据段"。记住一句就够:虚表不在栈上、也不在堆上。

再分清一对东西:虚函数本体是一段指令,编译后存在代码段;虚表是数据,存的是这些指令的地址——代码和数据,别混。

多继承下的虚函数表:一个对象,两张表

继承篇聊过多继承:class MDerive : public MBase1, public MBase2。当两个基类都有虚函数时,对象里就会塞两个__vfptr——每份基类子对象各带一张虚表。

探针安排(安全读取两张虚表的槽位地址):

sec7::MBase2*pb2=&d;// 隐式转换:编译器算好 Base2 子对象偏移void*vptr1=vfptr_of(&d);// Base1 部分的虚表void*vptr2=vfptr_of(pb2);// Base2 部分的虚表void**vt1=(void**)vptr1;void**vt2=(void**)vptr2;

实测输出:

vptr1(Base1 部分)= 0x7ff7976096e0 vptr2(Base2 部分)= 0x7ff797609708,偏移 16 字节 两张虚表地址不同: 1 vt1: slot0=0x7ff797598650 slot1=0x7ff7975984a0 slot2=0x7ff7975986e0 vt2: slot0=0x7ff7975f7520 slot1=0x7ff797598530 slot2=0 vt1[0] == vt2[0] ? 0(第二张表里的覆盖函数常是 thunk:先修正 this 偏移再跳转) vt1[1] != vt2[1] ? 1(两边的 func2 各留各的版本) MDerive::func1 MDerive::func1

地址里读出三条结论:

  1. 两张表分家:Base1、Base2 子对象各带一张虚表,Base2 那份在对象里还占了 16 字节的偏移;
  2. 重写全覆盖,但入口形态不同:func1被 Derive 重写后,两张表的第一槽都成了"跑向MDerive::func1的入口"——第一张表里是直接地址,第二张表里是thunk(一小段先修正this偏移、再跳转的代码:MBase2*指向的是对象中间,要转成MDerive*得先把这 16 字节的偏移减回去),所以两个槽的地址不一样,但调用效果完全一致:经MBase1*或MBase2*调func1,都输出MDerive::func1;
  3. 新虚函数只挂主表:Derive 新增的func3只出现在第一张表(vt1[2]有值),第二张表对应位置是 0。

【面试】多继承对象的内存布局 + 虚表安排,是"多态 × 多继承"的交叉考点,记一条线:每个多态基类子对象带一张表;重写在各自的表里落地;新增虚函数挂主表。

七、三个经典坑

7.1 默认参数坑:输出到底是几?

这道题流传很广,实测安排上。基类虚函数默认参数= 1,派生类重写时默认参数= 0,问p->test()输出什么?

classA{public:virtualvoidfunc(intval=1){cout<<"A->"<<val<<endl;}virtualvoidtest(){func();}};classB:publicA{public:voidfunc(intval=0)override{cout<<"B->"<<val<<endl;}};

实测三条调用,对照着看:

B->1 ← p->test()(经典题答案:B 的实现 + A 的默认参数) B->0 ← b.func()(直接调用,默认参数看 B 自己的) B->1 ← A* pa = &b; pa->func()(多态调用,默认参数看编译时类型 A)

【必背】一句话拆解:函数体走动态绑定(运行时看对象,调的是 B 的func),默认参数走静态绑定(编译时看指针类型,用的是 A 的val = 1)。两套机制拧在一起,就拧出了这个"B->1"。

7.2 虚析构:delete 基类指针,谁在泄漏?

上一篇埋的扣子(析构函数名被特殊处理成 destructor)在这里收网。

派生类 B 里有个_p指向堆内存、需要在析构里释放。现在用基类指针 new 一个 B,再 delete:

sec5::A_nv*p1=newsec5::B_nv;// ~A 不虚deletep1;sec5::A_v*p2=newsec5::B_v;// ~A 加了 virtualdeletep2;

实测对照输出:

--- 反例:~A 不虚,delete 基类指针 --- ~A_nv() (~B_nv() 根本没被调用:_p 指向的堆内存泄漏) --- 正例:~A 加 virtual,delete 基类指针 --- ~B_v()->delete:0x2408bd813f0 ~A_v() (先析构派生、再析构基类,资源正确释放)

析构不虚时,delete p1只调了~A,~B从头到尾没执行——_p的内存泄漏了,而且不报错、不崩溃,跑测试都未必发现。这也是"基类析构建议一律加 virtual"的原因:delete基类指针 + 虚析构 = 多态析构(先派生、后基类,正好是继承篇里那条析构顺序)。

7.3 构造 / 析构中调用虚函数:不构成多态

classBase{public:Base(){func();}// 构造里调虚函数virtualvoidfunc(){cout<<"Base::func(虚表还没切到派生类)"<<endl;}};classDerived:publicBase{public:voidfunc()override{cout<<"Derived::func"<<endl;}};

实测:

Base 构造中调用 func():Base::func(虚表还没切到派生类) Derived::func

构造派生类时,先跑基类构造——这期间对象的虚表还没"切"到派生类版本,func()只会调到Base::func;等对象完整构造完,再调用才是Derived::func。析构阶段同理(先还原成基类虚表)。所以构造和析构函数里调用虚函数,多态不生效——想用派生类版本,只能老老实实构造完再调。

【必背】三个坑一句话:默认参数静态绑定(B->1);基类析构要 virtual;构造/析构里调虚函数不构成多态。

八、速记卡

考前扫一眼,面试不卡壳:

九、经典题集

五道题覆盖这一章的"事故多发地段"。先自己默答,再看对照:

题 1(选择题):下面程序输出什么?

classA{public:virtualvoidfunc(intval=1){cout<<"A->"<<val<<endl;}virtualvoidtest(){func();}};classB:publicA{public:voidfunc(intval=0)override{cout<<"B->"<<val<<endl;}};intmain(){B*p=newB;p->test();}

A. A->0 B. B->1 C. A->1 D. B->0 E. 编译出错 F. 以上都不正确

答案

B. B->1。p->test()里的this是A*类型,func()满足多态 → 函数体走 B 的实现(动态绑定);默认参数是静态绑定,取编译时类型 A 的val = 1。所以是"B 的实现 + A 的参数"。完整实测(含另两条调用对照)见 7.1。

题 2(选择题):编译为 32 位程序,sizeof(b)的结果是?

classBase{public:virtualvoidFunc1(){cout<<"Func1()"<<endl;}protected:int_b=1;char_ch='x';};intmain(){Base b;cout<<sizeof(b)<<endl;}

A. 编译报错 B. 运行报错 C. 8 D. 12

答案

D. 12。32 位下:vfptr 4 字节 + int 4 字节 + char 1 字节 + 对齐填充 3 字节 = 12。同理,x64 下我们用同一份代码实测是16 字节(vfptr 8 + int 4 + char 1 + 填充 3)——这类题的答案跟着平台和指针大小走。再附两个变体实测:{virtual + 3 个 int}= 24 字节;{virtual + 1 个 char}= 16 字节(哪怕只有 1 字节数据,也要按 8 字节对齐)。

题 3(判断题):下面这个值传递版本是不是多态?输出什么?

voidFunc(Person ptr)// 注意:值传递{ptr.BuyTicket();}intmain(){Person ps;Student st;Func(ps);Func(st);}
答案

不是多态,输出两行"买票-全价"。值传递把st切片成了 Person 对象,函数里的ptr已经不含 Student 部分——三条件里"基类指针/引用"这一条被破坏,静态绑定到Person::BuyTicket。对照实验见第二节。

题 4(判断题):下面两句delete分别会调用哪些析构函数?

classA{public:~A(){cout<<"~A()"<<endl;}// 没有 virtual};classB:publicA{public:~B(){delete[]_p;}protected:int*_p=newint[10];};A*p=newB;deletep;
答案

只输出~A()——析构不虚,delete p按基类析构调用,~B()根本不执行,_p指向的堆内存泄漏。把~A加上virtual后输出变成~B()(先释放资源)再~A()。这就是"基类析构建议 virtual"的由来,实测对照见 7.2。

题 5(判断题):构造Derived d的过程中,基类构造函数里调用的虚函数走哪个版本?

classBase{public:Base(){func();}virtualvoidfunc(){cout<<"Base::func"<<endl;}};classDerived:publicBase{public:voidfunc()override{cout<<"Derived::func"<<endl;}};intmain(){Derived d;}
答案

输出Base::func。构造派生类时先跑基类构造,这期间对象的虚表还没切到派生类版本,虚函数不构成多态;等对象完整构造完再调,才轮到Derived::func。析构阶段同理,实测见 7.3。

【一句话总结】五道题拉到底,就是两条线:多态三条件(缺一条就静态绑定)和虚表 / 对象模型(谁的表、谁的 this)。


附录 A:完整代码仓库

完整工程(单文件版,全部实验一次跑完)在 Gitee:

https://gitee.com/BaiYang_SQ/poly-sim

仓库文件说明
poly_sim.cpp单文件:全部实验;章节 ↔ 函数名对照见仓库 README 导览表
output.txt全部实验的真实运行输出(含虚析构对照、sizeof、vfptr 数据)
build.batWindows 一键编译 + 运行 + 刷新 output.txt
README.md一分钟上手 + 代码导览

面试前手写练手,照这个骨架压考点(完整版见仓库):

// 多态·考点骨架(完整可跑版见 Gitee 仓库)#include<iostream>usingnamespacestd;classPerson{public:virtualvoidBuyTicket(){cout<<"全价"<<endl;}virtual~Person(){}// 基类析构加 virtual};classStudent:publicPerson{public:voidBuyTicket()override{cout<<"打折"<<endl;}};voidFunc(Person&p){p.BuyTicket();}// 指针/引用 + 虚函数 + 重写 = 多态intmain(){Person ps;Student st;Func(ps);// 全价Func(st);// 打折Person*p=newStudent;deletep;// 多态析构:先 ~Student 再 ~Personreturn0;}

好了,多态这篇到此完结!下一站——二叉搜索树(预告:插入查找一条龙、删除的四种情况,以及为什么 map / set 的底层都要给它留位置)。如果对你有帮助,不要忘记点赞三连一波哦,我是白杨,我们下期见。

相关新闻

AI手机SDK源码二开全指南:从架构拆解到私域落地避坑

AI手机SDK源码二开全指南:从架构拆解到私域落地避坑

这两年“AI手机SDK”这个词在私域运营和销售管理圈子里特别火&#xff0c;尤其是打上“AI销冠手机”“AI员工智能营销手机”标签的方案&#xff0c;几乎每个做客户运营的团队都来问过一嘴。说直白点&#xff0c;这类SDK就是把大模型能力封装成手机端可调用的组件&#xff0c;让…

2026/10/20 5:18:03 阅读更多 →
大模型选型双维度框架:从模型能力到应用落地的实战指南

大模型选型双维度框架:从模型能力到应用落地的实战指南

1. 从"模型/应用"双维度切入&#xff1a;为什么这个分类框架比单纯罗列模型更有用大多数人整理大模型资料时&#xff0c;习惯按厂商或者按发布时间拉一张长表&#xff0c;结果就是"知道有哪些模型&#xff0c;但不知道什么场景该用哪个"。我最初也这么干过…

2026/10/20 5:18:03 阅读更多 →
转行面试PPT怎么做?用AI高效制作面试PPT的完整实操指南

转行面试PPT怎么做?用AI高效制作面试PPT的完整实操指南

1. 转行面试为什么要死磕一份PPT转行这件事&#xff0c;最难的从来不是学新技能&#xff0c;而是怎么把过去那些看起来"不相关"的经历&#xff0c;翻译成目标岗位能听懂的语言。我见过太多人简历写得还行&#xff0c;一到面试就卡壳&#xff0c;尤其是被要求"做…

2026/10/20 5:18:03 阅读更多 →

最新新闻

从单体到弹性扩展:高并发架构改造的完整实战复盘

从单体到弹性扩展:高并发架构改造的完整实战复盘

1. 项目背景与架构需求拆解1.1 最初的系统&#xff0c;和它一步步暴露出的问题先说下背景。这个代号为"arch-system"的架构改造项目&#xff0c;源于团队负责的一条核心业务链路。最初的系统是典型的三层单体&#xff1a;一个应用服务承载所有接口&#xff0c;MySQL …

2026/10/20 6:03:23 阅读更多 →
PPT-Master:用PowerPoint构建业务操作系统的方法论

PPT-Master:用PowerPoint构建业务操作系统的方法论

1. “PPT-Master”不是软件名&#xff0c;而是一类人的生存状态最近在几个技术协作群和设计交流社区里&#xff0c;“PPT-Master”这个词高频出现&#xff0c;但没人把它当产品名说——它更像一句带点自嘲又透着狠劲的行业暗号。我第一次听到是在某次跨部门复盘会上&#xff0c…

2026/10/20 6:03:23 阅读更多 →
图灵奖60年:人机环境系统智能的演进与落地

图灵奖60年:人机环境系统智能的演进与落地

六十年前&#xff0c;那个以一位计算机先驱命名的奖项第一次颁出时&#xff0c;大概没有人想到&#xff0c;它会成为一部无声的“智能观变迁史”。这六十年间&#xff0c;获奖成果背后的工作其实都在回答同一个问题&#xff1a;机器到底怎样才算有智能&#xff1f;答案从“会算…

2026/10/20 6:03:23 阅读更多 →
浙江高考VB排序系列:冒泡与选择排序代码实现及应试技巧

浙江高考VB排序系列:冒泡与选择排序代码实现及应试技巧

1. 从一道排序题说起&#xff1a;为什么VB排序值得单独拎出来讲每年到了技术科目备考的冲刺阶段&#xff0c;总有一批同学在排序算法这块反复栽跟头。选择题里问“冒泡排序第三轮结束后序列长什么样”&#xff0c;填空题里给一段残缺代码让你补全循环边界&#xff0c;上机操作题…

2026/10/20 6:03:23 阅读更多 →
图灵奖六十年:从机器能否思考到人机环境系统智能的跃迁

图灵奖六十年:从机器能否思考到人机环境系统智能的跃迁

图灵奖整整六十年了。作为计算机科学领域最负盛名的荣誉&#xff0c;它这些年一直被当作衡量学术巅峰的标尺。可如果你把六十年来历届获奖者所代表的方向串成一条时间线&#xff0c;会发现一个远比"学术成就"更有意思的东西正在浮现&#xff1a;整个领域对"智能…

2026/10/20 6:03:23 阅读更多 →
主动学习新思路:损失预测模块如何精准筛选高价值样本

主动学习新思路:损失预测模块如何精准筛选高价值样本

1. 为什么主动学习要换一种采样思路做深度学习项目&#xff0c;尤其在数据标注预算有限的时候&#xff0c;大家最先想到的办法多半是“先随便标一部分&#xff0c;训练一版模型&#xff0c;再用模型挑一些‘难样本’送给标注员”。这套逻辑本身没问题&#xff0c;难就难在“难样…

2026/10/20 6:02:23 阅读更多 →

日新闻

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/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/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/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 阅读更多 →