孙鑫VC++学习笔记复盘:从SDK到MFC的CWnd与CDC实战梳理(TaoToken)
1. 从 SDK 到 MFC孙鑫 VC 前五讲到底在讲什么如果你刚开始接触 Windows 下的 C 开发大概率会经历一个很拧巴的阶段能看懂WinMain里的消息循环但一打开 MFC 生成的工程就不知道从哪下手知道CWnd是窗口类却说不清它和 SDK 里那个HWND到底什么关系写了个OnLButtonDown结果画线画不出来或者画完程序一关就报内存泄漏。孙鑫的 VC 教程前五讲恰好就是冲着这些拧巴点去的——它没有一上来就丢给你一堆类库文档而是先用 SDK 把窗口的“手工活”走一遍再告诉你 MFC 把这些手工活封装成了什么。这篇复盘面向的是刚接触 Windows 开发的 C 程序员目标很明确把 SDK 创建窗口的四步、MFC 中CWnd与CDC的核心用法、消息映射的三处修改、以及绘图上下文的获取与释放串成一条能动手复现的路径。我会给出可复制的 MFC 最小工程配置、逐讲验证清单以及我在对照笔记复现时踩过的坑。你不需要先看完五讲视频跟着下面的步骤走每个示例都能自己跑起来。核心检索词先摆在这里VC、MFC、SDK、CWnd、CDC。这五个词基本覆盖了前五讲的全部内容。SDK 是“手动挡”MFC 是“自动挡”CWnd是窗口对象的 C 封装CDC是绘图上下文的封装。理解这四者的关系前五讲就通了。我试过把 SDK 的CreateWindow和 MFC 的CWnd::Create放在一起对照发现 MFC 并没有变魔术它只是把WNDCLASS注册、窗口过程回调、消息分发这些步骤藏进了AfxWinMain和消息映射表里。你看到的BEGIN_MESSAGE_MAP/END_MESSAGE_MAP本质就是一张“消息 ID → 成员函数”的查找表。下面从 SDK 的窗口四步开始一步步过渡到 MFC。2. SDK 窗口创建四步与 MFC 最小工程配置2.1 SDK 创建窗口的四个步骤孙鑫第一讲里反复强调一个完整的 SDK 窗口要经过四步设计窗口类、注册窗口类、创建窗口、显示窗口。这四步在代码里对应WNDCLASS结构体、RegisterClass、CreateWindow、ShowWindowUpdateWindow。很多人卡在第一步因为WNDCLASS有十个字段漏一个就注册失败。#include windows.h #include stdio.h LRESULT CALLBACK MyProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmd, int nShow) { WNDCLASS wndcls; wndcls.style CS_HREDRAW | CS_VREDRAW; wndcls.lpfnWndProc MyProc; // 窗口过程函数名即函数指针 wndcls.cbClsExtra 0; wndcls.cbWndExtra 0; wndcls.hInstance hInstance; wndcls.hIcon LoadIcon(NULL, IDI_APPLICATION); wndcls.hCursor LoadCursor(NULL, IDC_ARROW); wndcls.hbrBackground (HBRUSH)GetStockObject(WHITE_BRUSH); wndcls.lpszMenuName NULL; wndcls.lpszClassName MyWndClass; if (!RegisterClass(wndcls)) return 0; // 第二步注册 HWND hwnd CreateWindow(MyWndClass, SDK Window, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 600, 400, NULL, NULL, hInstance, NULL); ShowWindow(hwnd, nShow); // 第四步显示 UpdateWindow(hwnd); MSG msg; while (GetMessage(msg, NULL, 0, 0)) { TranslateMessage(msg); DispatchMessage(msg); } return msg.wParam; } LRESULT CALLBACK MyProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { case WM_CHAR: break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hWnd, uMsg, wParam, lParam); } return 0; }这里有个细节值得单独拎出来wndcls.lpfnWndProc MyProc;这一行函数名代表函数代码的首地址可以直接赋给函数指针。孙鑫在笔记里专门提了这一点因为后面 MFC 的消息映射就是靠类似的机制把消息和成员函数关联起来的。2.2 MFC 最小工程配置从 SDK 切到 MFC第一步是建一个能跑的最小工程。我用的是 Visual Studio新建“MFC 应用程序”选择“单文档”其余默认。生成后你会看到stdafx.h新版叫pch.h里包含了afxwin.h而afxwin.h里又隐式包含了windows.h。这就是为什么 MFC 工程里不用再手动#include windows.h。如果你不想用向导想手动配一个最小 MFC 工程核心配置如下// pch.h #pragma once #include afxwin.h // MFC 核心含 CWnd、CDC、CString 等 #include afxext.h// MyApp.h #pragma once #include pch.h class CMyApp : public CWinApp { public: virtual BOOL InitInstance(); }; class CMyFrame : public CFrameWnd { public: CMyFrame(); };// MyApp.cpp #include pch.h #include MyApp.h CMyApp theApp; // 全局对象WinMain 之前就被构造 BOOL CMyApp::InitInstance() { CMyFrame* pFrame new CMyFrame(); m_pMainWnd pFrame; pFrame-ShowWindow(SW_SHOW); pFrame-UpdateWindow(); return TRUE; } CMyFrame::CMyFrame() { Create(NULL, MFC 最小窗口, WS_OVERLAPPEDWINDOW, CRect(0, 0, 600, 400)); }这段代码里最关键的是CMyApp theApp;这个全局对象。孙鑫第三讲提到在WinMain之前theApp已经被构造并初始化由于子类构造函数执行前父类构造函数先执行所以CWinApp的构造函数先跑theApp对象产生后AfxWinMain里的pThread和pApp才有内容。这就是 MFC 程序启动的隐藏入口。注意CMyFrame的Create第二个参数是窗口标题第四个参数是CRect。如果你漏了WS_VISIBLE窗口不会自动显示必须手动调ShowWindow。2.3 CWnd 与 HWND 的关系所有从CWnd派生的类都有m_hWnd句柄。CWnd是对HWND的 C 封装m_hWnd就是它内部持有的窗口句柄。你可以用FromHandle从句柄反查对象指针CWnd* pWnd CWnd::FromHandle(hwnd);这个函数在第四讲里被专门提到因为后面画线时经常需要从HWND拿到CWnd*。FromHandle是静态成员函数可以由类名直接调用也可以由对象调用。静态成员函数不能访问非静态成员变量静态成员变量必须在类的外部初始化——这些是第二讲里关于静态成员的要点在 MFC 里到处都能碰到。3. 消息映射三处修改与 CDC 绘图上下文配置3.1 消息映射的三处修改孙鑫第四讲的核心是消息映射。在 MFC 里加一个鼠标左键按下的响应要在三个地方改代码。以单文档的 View 类为例第一处头文件里声明消息处理函数// MyView.h protected: //{{AFX_MSG(CMyView) afx_msg void OnLButtonDown(UINT nFlags, CPoint point); afx_msg void OnLButtonUp(UINT nFlags, CPoint point); afx_msg void OnMouseMove(UINT nFlags, CPoint point); //}}AFX_MSG DECLARE_MESSAGE_MAP()第二处cpp 文件的BEGIN_MESSAGE_MAP和END_MESSAGE_MAP之间加宏// MyView.cpp BEGIN_MESSAGE_MAP(CMyView, CView) //{{AFX_MSG_MAP(CMyView) ON_WM_LBUTTONDOWN() ON_WM_LBUTTONUP() ON_WM_MOUSEMOVE() //}}AFX_MSG_MAP END_MESSAGE_MAP()第三处实现函数void CMyView::OnLButtonDown(UINT nFlags, CPoint point) { m_ptOrigin m_ptOld point; m_bDraw TRUE; CView::OnLButtonDown(nFlags, point); }这三处缺一不可。我踩过的坑是只加了头文件声明和函数实现忘了在BEGIN_MESSAGE_MAP里加ON_WM_LBUTTONDOWN()结果鼠标点下去毫无反应编译也不报错。消息映射表是运行时查找的漏了宏就等于没注册。3.2 CDC 绘图上下文的四种获取方式第四讲另一个重点是画线。孙鑫列了四种方法从 SDK 的HDC到 MFC 的CDC、CClientDC、CWindowDC。这四种方式的区别和释放规则是新手最容易搞混的地方。第一种直接用 SDK 的HDCHDC hdc ::GetDC(m_hWnd); MoveToEx(hdc, m_ptOrigin.x, m_ptOrigin.y, NULL); LineTo(hdc, point.x, point.y); ::ReleaseDC(m_hWnd, hdc); // 必须成对第二种用CDC*CDC* pDC GetDC(); pDC-MoveTo(m_ptOrigin); pDC-LineTo(point); ReleaseDC(pDC); // 必须成对第三种用CClientDC构造时自动获取 DC析构时自动释放CClientDC dc(this); dc.MoveTo(m_ptOrigin); dc.LineTo(point); // 不需要 ReleaseDC第四种用CWindowDC可以覆盖整个窗口区域甚至用GetDesktopWindow()在整个屏幕上画CWindowDC dc(GetDesktopWindow()); dc.MoveTo(m_ptOrigin); dc.LineTo(point);关键差异在于GetDC/ReleaseDC和BeginPaint/EndPaint必须成对使用否则内存泄漏而CClientDC和CWindowDC在析构函数里自动调用ReleaseDC不需要手动释放。孙鑫在笔记里专门用“必须成对使用”标注了前两种就是因为这是最常见的泄漏点。3.3 可复制的 settings 片段如果你用 VS Code 配合 Cline 或 Claude Code 来辅助写 MFC 代码可以在项目根目录放一个.vscode/settings.json把 MFC 的头文件路径和库路径配进去避免 IntelliSense 报红{ C_Cpp.default.includePath: [ ${workspaceFolder}/**, C:/Program Files/Microsoft Visual Studio/2022/Community/VC/Tools/MSVC/14.38.33130/atlmfc/include, C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/um, C:/Program Files (x86)/Windows Kits/10/Include/10.0.22621.0/shared ], C_Cpp.default.defines: [ _AFXDLL, WIN32, _WINDOWS ], C_Cpp.default.cppStandard: c17 }路径里的 MSVC 版本号和 Windows Kits 版本号要换成你本机实际安装的。_AFXDLL这个宏必须定义否则afxwin.h里的很多声明会走静态链接分支和动态库版本对不上。如果你用 Cline 的 MCP 配置来管理模型接入可以在cline_mcp_settings.json里写{ mcpServers: { taotoken: { url: https://taotoken.net/api, env: { API_KEY: 你的Key, BASE_URL: https://taotoken.net/api, MODEL_ID: claude-sonnet-4-20250514 } } } }Base URL、Key、Model ID 这三件套要写全缺一个就连不上。Key 在控制台的 API Keys 页面生成模型 ID 可以在模型对话页面确认当前可用的版本。4. 验证请求与逐讲复现清单4.1 验证 MFC 工程能跑起来配好工程后先验证最小窗口能显示。编译运行如果窗口没出来按这个顺序查先看InitInstance里有没有ShowWindow(SW_SHOW)和UpdateWindow()。再看CMyFrame的Create有没有传WS_OVERLAPPEDWINDOW。最后看theApp是不是全局对象——如果写成了局部变量WinMain之前不会被构造整个 MFC 启动链就断了。窗口出来后在 View 里加鼠标响应验证消息映射三处都改对了。点一下鼠标如果断点能进OnLButtonDown说明映射表生效。4.2 逐讲验证清单第一讲验证点SDK 窗口四步是否完整LoadCursor、MessageBox的声明是否来自windows.hsprintf是否包含stdio.hGetDC/ReleaseDC是否成对。第二讲验证点结构体和类定义末尾的分号是否漏#include xxx.h和#include xxx.h的查找路径差异引用传参的交换函数是否写对头文件卫士#ifndef/#define/#endif是否加了。第三讲验证点theApp全局对象是否在WinMain之前构造CSingleDocTemplate是否把 Doc、Frame、View 三个类关联起来在CMainFrame::OnCreate里创建的CButton对象是否是成员变量而非局部变量。第四讲验证点消息映射三处是否齐全四种画线方式是否都能画出线CPen选中后是否恢复旧画笔CBrush::FromHandle的静态调用是否写对。第五讲验证点CreateSolidCaret和ShowCaret是否配对GetTextMetrics拿到的字体高度是否用于换行路径层BeginPath/EndPath/SelectClipPath是否保护了先前的文本SetTimer回调函数里获取 View 指针的链路是否通。4.3 用模型对话验证代码理解如果你对某段代码的语义不确定可以把代码贴到模型对话页面让模型解释CWnd::Create的参数含义或者对比CClientDC和CWindowDC的裁剪区域差异。验证模型是否理解正确可以问它“GetDC和CClientDC在释放时机上有什么不同”看它能不能说出析构自动释放这个点。对于长期要写 MFC 或 Windows 桌面开发的人可以考虑用 Coding Plan 来管理日常的代码补全和重构任务把模型接入配好之后写消息映射和 CDC 绘图这类重复性代码会快很多。5. 本篇常见报错排查5.1 401 与 local proxy failed如果你在配置模型接入时遇到 401先检查 Key 是否复制完整有没有多余空格。Base URL 要写https://taotoken.net/api不要多加斜杠或路径。如果报local proxy failed通常是本地代理配置和实际网络环境不匹配检查cline_mcp_settings.json里的url字段是否指向了正确的 API 地址。5.2 reading choices 报错这个报错一般出现在模型返回格式和客户端解析格式对不上时。检查MODEL_ID是否写成了客户端不认识的名称。模型对话页面里能正常对话的模型 ID才是当前可用的。如果换了模型 ID 后报reading choices换回默认的再试。5.3 OAuth 相关报错Claude Code 接入时如果走 OAuth 流程报错先确认是不是混用了 API Key 和 OAuth 两种认证方式。用 API Key 接入时不需要再走 OAuth 授权用 OAuth 时Key 字段留空。两种方式选一种不要同时配。5.4 MFC 编译报错 afxwin.h 找不到这是头文件路径没配。检查 VS 安装时有没有勾选“MFC 组件”。如果装了但 IntelliSense 报红在.vscode/settings.json的includePath里补上atlmfc/include路径。编译能过但编辑器报红是 IntelliSense 的路径问题不影响实际编译。5.5 画线画不出来按这个顺序查OnLButtonDown有没有进m_bDraw有没有置 TRUEOnMouseMove里有没有判断m_bDrawCClientDC有没有构造成功MoveTo和LineTo的坐标是不是同一个点。如果用的是CDC*方式检查ReleaseDC有没有漏漏了会导致后续 DC 获取失败。5.6 内存泄漏GetDC/ReleaseDC、BeginPaint/EndPaint、SelectObject选入后有没有选回旧对象这三处是泄漏高发区。CPen、CBrush、CFont选入 DC 后一定要用SelectObject(pOldPen)恢复。CClientDC和CWindowDC不需要手动释放但CDC*方式必须成对。6. 把 SDK 的手动挡换成 MFC 的自动挡前五讲走下来最值得记住的一句话是MFC 没有替代 SDK它只是把 SDK 的手工步骤封装成了类。CWnd封装了HWND和窗口过程CDC封装了HDC和绘图函数消息映射表封装了switch-case消息分发。你理解了 SDK 的四步再看 MFC 的InitInstance和BEGIN_MESSAGE_MAP就不会觉得它是黑盒。如果你要动手复现建议按这个顺序先用 SDK 写一遍窗口四步确保能跑再用 MFC 向导建一个单文档工程在 View 里加鼠标响应和画线最后把四种画线方式都试一遍对比释放规则。每讲验证清单里的点过一遍打个勾。模型接入方面API Key 在控制台的 API Keys 页面生成接入文档在文档页有完整的 Base URL 和参数说明。遇到 401 或local proxy failed先查 Key 和 URL再查模型 ID。需要长期写代码的Coding Plan 比按次调用更省心。

相关新闻

我做了一个 Cursor 红绿灯:让 AI Agent 的状态一眼可见

我做了一个 Cursor 红绿灯:让 AI Agent 的状态一眼可见

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

2026/10/10 20:41:26 阅读更多 →
BTCV腹部14器官分割:临床级三维标注与手术导航实践

BTCV腹部14器官分割:临床级三维标注与手术导航实践

简介:本资源是面向医学图像分割研究者与AI医疗方向初学者的高质量腹部多器官2D切片数据集,基于权威BTCV(Abdominal Multi-Organ Segmentation)基准构建,专为CT影像语义分割模型训练、验证与可视化分析提供支持。资源包…

2026/10/10 20:40:26 阅读更多 →
IT售前PPT实战方法论:技术逻辑到决策语言的转化

IT售前PPT实战方法论:技术逻辑到决策语言的转化

简介:本资源是一套系统化、实战导向的《IT售前工程师修炼》原创PPT课件,专为IT售前人员、拟转型售前的IT从业者、项目管理人员及IT销售人员设计,帮助其构建结构化售前知识体系、掌握核心方法论并完成职业路径规划。内容覆盖IT售前概述、金字塔…

2026/10/10 20:40:26 阅读更多 →

最新新闻

647回文子串与516最长回文子序列:区间DP两种典型玩法全解析

647回文子串与516最长回文子序列:区间DP两种典型玩法全解析

各位打卡代码随想录的伙计们,第四十五天来了。今天这两道题——647 回文子串、516 最长回文子序列——看起来名字只差两个字,实际上一个是把字符串切成一段段判断"是不是回文",另一个是允许跳跃地凑出"最长回文有多长"。…

2026/10/10 21:19:06 阅读更多 →
用幸运大转盘项目掌握Python循环:for、while与range实战

用幸运大转盘项目掌握Python循环:for、while与range实战

1. 项目拆解:为什么用“幸运大转盘”讲循环在Python入门的教学顺序里,循环永远是绕不过去的坎。很多初学者学完变量、条件判断之后,一到for和while就开始晕:for循环里那个range到底怎么数数?while循环什么时候停&#…

2026/10/10 21:19:06 阅读更多 →
mcp-brasil快速上手指南:2分钟接入Claude/Cursor,免费解锁66个巴西公共数据API

mcp-brasil快速上手指南:2分钟接入Claude/Cursor,免费解锁66个巴西公共数据API

【免费下载链接】mcp-brasil MCP Server para 70 APIs pblicas brasileiras 项目地址: https://gitcode.com/gh_mirrors/mc/mcp-brasil 点击查看 免费下载 mcp-brasil 是一个开源的 MCP Server,一个命令即可把 66 个无需密钥的巴西公共数据 API&#xf…

2026/10/10 21:19:06 阅读更多 →
AI 齐套性专家 | 实战案例成果演示

AI 齐套性专家 | 实战案例成果演示

#玄宿科技#AI齐套性交付#案例分享#自主协同软件#GJB438B#Qt开发#交付文档上一期我们演示了 AI 齐套性交付专家的完整生成过程,但所用案例与真实业务贴合度不够。本期我们换了一个完全贴合业务的方向,重新生成了一整套成果:原型图、可交互程序…

2026/10/10 21:19:06 阅读更多 →
OOOSplat 处理流水线全解:FFmpeg、COLMAP 与 Brush 三大引擎如何协同工作

OOOSplat 处理流水线全解:FFmpeg、COLMAP 与 Brush 三大引擎如何协同工作

桌面应用图形学3D渲染计算机视觉 【免费下载链接】ooosplat A local desktop app that turns videos and images into 3D Gaussian Splats in one click. 项目地址: https://gitcode.com/gh_mirrors/oo/ooosplat 点击查看 免费下载 OOOSplat 是一款把视频和图片序列…

2026/10/10 21:19:06 阅读更多 →
零基础 30 分钟出片:用万相 Animate 把角色设定变成可复用的动画素材

零基础 30 分钟出片:用万相 Animate 把角色设定变成可复用的动画素材

零基础 30 分钟出片:用万相 Animate 把角色设定变成可复用的动画素材 【免费下载链接】Wan2.2-Animate-2-14B 项目地址: https://ai.gitcode.com/hf_mirrors/Wan-AI/Wan2.2-Animate-2-14B 做一条角色动画,过去意味着什么?画角色设定、…

2026/10/10 21:18:06 阅读更多 →

日新闻

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

卫星轨道分类全解析:从LEO到GEO的选型逻辑与工程实践

1. 从“卫星轨道分类”这个标题说起:为什么值得花时间搞懂第一次接触“卫星轨道分类”这个概念,很多人会觉得它离自己很远——不就是天上的星星怎么转吗?但如果你正在做航天任务规划、遥感数据接收、星座设计,甚至只是准备一场航天…

2026/10/10 0:00:39 阅读更多 →
Spring AOP 核心原理与实战:从概念到日志切面落地

Spring AOP 核心原理与实战:从概念到日志切面落地

1. 从一个真实痛点说起:为什么你的代码里到处都是重复逻辑刚入行那会儿,我写过一个用户管理模块,注册、登录、改密码、注销四个接口。每个接口里都塞了几乎一样的日志打印、参数校验、事务开启和提交。当时觉得没什么,能跑就行。直…

2026/10/10 0:00:40 阅读更多 →
Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

Python招聘数据采集与分析可视化:从采集清洗到薪资技能城市可视化全链路

简介:这是一套面向计算机相关专业学生与项目实战学习者的Python数据采集与分析可视化完整项目,以Boss直聘岗位数据为对象,适合用作毕业设计、课程设计或期末大作业。资源包共38个文件,约246KB,以13个py源码文件为核心&…

2026/10/10 0:00:40 阅读更多 →

周新闻

KT148A语音芯片外挂8002D功放的工程实践指南

KT148A语音芯片外挂8002D功放的工程实践指南

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

2026/10/10 11:14:25 阅读更多 →
LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

LLC谐振变换器增益公式推导:从FHA等效到完整归一化表达式

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

2026/10/10 1:36:08 阅读更多 →
ARM架构深度解析:从RISC设计理念到交叉编译实战

ARM架构深度解析:从RISC设计理念到交叉编译实战

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

2026/10/10 11:14:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/10 5:23: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/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练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/10 10:38:42 阅读更多 →