QT开发「1」 C++简要知识点
前言QT是基于C的跨平台开发框架所以在使用QT开发前要先有一定量的C基础其实不会问题也不大(╹ڡ╹ )这里先简单总结一些C独有的并且在QT中常用的C知识点。1.Class类C的class类里面主要包含成员变量属性成员函数方法并且还有public、private和protected的访问修饰符来修饰成员。下面的代码先写完后面再给例子看日志打印1.1 Class类创建class MyClass { public: int age() const { return my_age; } const std::string name() const { return my_name; } private: int my_age; std::string my_name; };这里定义了一个类包含私有成员变量和公共成员函数。然后在main函数中创建对象就能使用该类的成员函数。1.2初始化列表和委托构造函数class MyClass { public: // Default constructor MyClass(): MyClass(0, default){ std::cout [Constructor] age0, namedefault std::endl; } // Parameter constructor MyClass(int age, const std::string name): my_age(age), my_name(name){ std::cout [Constructor] age age , name name std::endl; } int age() const { return my_age; } const std::string name() const { return my_name; } std::string nameX() const { return my_name Hello; } void setName(const std::string name) { my_name name; } private: int my_age; std::string my_name; };初始化列表MyClass obj1(40, Stack);传入参数进行初始化确保某些成员的正确初始化。委托构造函数这里用 MyClass obj1()无参初始化时委托给MyClass(int age, const std::string name):函数进行初始化初值不会带来额外开销。1.3析构函数析构函数是C中一种特殊的成员函数用于在对象生命周期结束时自动释放资源。简单来说就是当对象结束时析构函数会执行。前缀为波浪号~无返回类型且不接受参数。class MyClass { public: // Default constructor MyClass(): MyClass(0, default){ std::cout [Constructor] age0, namedefault std::endl; } // Parameter constructor MyClass(int age, const std::string name): my_age(age), my_name(name){ std::cout [Constructor] age age , name name std::endl; } // Copy Constructor (Deep Copy) - copy data from another object // Copies member variables to create independent objects MyClass(const MyClass other) : my_age(other.my_age), my_name(other.my_name) { std::cout [CopyConstructor] Copied age my_age , name my_name std::endl; } // Move Constructor (Move Semantics) - steal resources from another object // Transfer data ownership instead of copying MyClass(MyClass other) noexcept : my_age(other.my_age), my_name(std::move(other.my_name)) { std::cout [MoveConstructor] Moved age my_age , name my_name std::endl; other.my_age 0; other.my_name ; // Clear source object } // Destructor - called when object is destroyed ~MyClass() { std::cout [Destructor] age my_age , name my_name std::endl; } int age() const { return my_age; } const std::string name() const { return my_name; } std::string nameX() const { return my_name Hello; } void setName(const std::string name) { my_name name; } private: int my_age; std::string my_name; };所以这个析构函数在对象生命周期结束时会打印[Destructor] age......2.对象C 中的对象是类的实例化实体通过类定义的数据类型包含成员变量属性和成员函数方法。2.1对象定义在栈区栈区Stack由编译器自动分配和释放存储局部变量、函数参数等。栈区内存分配速度快但空间有限。当函数调用结束时栈帧自动释放。特点内存分配连续效率高。空间有限通常几MB超出会导致栈溢出Stack Overflow。生命周期由作用域控制自动管理。#include App.h #include iostream class MyClass { public: // Default constructor MyClass(): MyClass(0, default){ std::cout [Constructor] age0, namedefault std::endl; } // Parameter constructor MyClass(int age, const std::string name): my_age(age), my_name(name){ std::cout [Constructor] age age , name name std::endl; } // Copy Constructor (Deep Copy) - copy data from another object // Copies member variables to create independent objects MyClass(const MyClass other) : my_age(other.my_age), my_name(other.my_name) { std::cout [CopyConstructor] Copied age my_age , name my_name std::endl; } // Move Constructor (Move Semantics) - steal resources from another object // Transfer data ownership instead of copying MyClass(MyClass other) noexcept : my_age(other.my_age), my_name(std::move(other.my_name)) { std::cout [MoveConstructor] Moved age my_age , name my_name std::endl; other.my_age 0; other.my_name ; // Clear source object } // Destructor - called when object is destroyed ~MyClass() { std::cout [Destructor] age my_age , name my_name std::endl; } int age() const { return my_age; } const std::string name() const { return my_name; } std::string nameX() const { return my_name Hello; } void setName(const std::string name) { my_name name; } private: int my_age; std::string my_name; }; int main() { //1.定义在栈区超出作用域自动销毁生命周期结束自动释放内存 std::cout 1. Stack Object Lifetime std::endl; { std::cout Entering scope std::endl; MyClass obj1(40, Stack); std::cout Exiting scope, obj1 auto destroyed: std::endl; } // - Scope ends, auto call destructor std::cout \n Program End std::endl; return 0; }这里在{}栈区里创建了一个对象终端打印 1. Stack Object Lifetime Entering scope [Constructor] age40, nameStack Exiting scope, obj1 auto destroyed: [Destructor] age40, nameStack[Constructor]是我们之前的初始化列表中的函数在这里初始化时被调用[Destructor]是我们之前的析构函数在对象生命周期结束后被调用q(≧▽≦q)2.2对象定义在堆区堆区由程序员手动分配和释放或通过智能指针管理空间较大但分配速度较慢。需使用new/delete或malloc/free操作。就是纯手动申请的(╹ڡ╹ )特点空间灵活仅受系统内存限制。分配和释放需手动管理易出现内存泄漏或野指针。生命周期由程序员控制。...... //2.定义在堆区需要手动释放内存生命周期结束手动释放内存 std::cout \n 2. Heap Object Lifetime std::endl; { std::cout Using new to create object std::endl; MyClass* obj2 new MyClass(45, Heap); std::cout Manual delete to destroy: std::endl; delete obj2; // - Manual call destructor } ......日志打印 2. Heap Object Lifetime Using new to create object [Constructor] age45, nameHeap Manual delete to destroy: [Destructor] age45, nameHeap把delete注释掉后日志 2. Heap Object Lifetime Using new to create object [Constructor] age45, nameHeap Manual delete to destroy:可以看到析构函数没有被调用也就意味着堆区的该成员没有被释放这样就会造成内存泄漏X﹏X2.3对象的使用对象的使用比较简单这里不过多赘述简单说一下\(︶*\))在创建对象时的不同导致使用对象的不同方法//3.如何访问成员 std::cout \n 3. Class Members std::endl; { MyClass obj3(50, MemberAccess); std::cout Accessing members: age obj3.age() , name obj3.name() std::endl; std::unique_ptr MyClass obj4 std::make_uniqueMyClass(55, UniquePtr); std::cout Accessing members via unique_ptr: age obj4-age() , name obj4-name() std::endl; } // - Scope ends, unique_ptr auto call delete to release memory2.4匿名对象匿名对象Anonymous Object是指未被命名的临时对象通常直接在表达式中创建并使用生命周期仅限于当前语句。匿名对象常用于简化代码或作为函数参数传递。也就是创建后就被销毁生命周期最短(∪.∪ )...zzz//4.匿名对象创建后立即销毁生命周期最短 std::cout \n 4. Anonymous Object std::endl; { // Anonymous object: MyClass(25, Anonymous) has no variable name // Created immediately destroyed, shortest lifetime MyClass(25, Anonymous); // - Create now, destroy now std::cout Anonymous object destroyed\n std::endl; } 4. Anonymous Object [Constructor] age25, nameAnonymous [Destructor] age25, nameAnonymous Anonymous object destroyed可以看出创建完就嗝屁了~2.5深拷贝、浅拷贝在C中深拷贝Deep Copy和浅拷贝Shallow Copy是对象复制的两种方式主要区别在于对指针或动态分配内存的处理。浅拷贝仅复制对象的成员变量的值包括指针的地址指向同一块内存。原对象和拷贝对象的指针成员指向同一资源可能导致重复释放或内存泄漏。深拷贝不仅复制成员变量的值还为指针成员分配新的内存空间并复制其指向的内容。原对象和拷贝对象的指针成员指向独立的内存区域。所以一般使用是使用深拷贝。//• 深拷贝复制数据值 → 两个对象独立 //• 浅拷贝只复制指针 → 两个对象共享同一块内存 ❌(危险) std::cout 5. Deep Copy Demo std::endl; { MyClass obj1(30, Original); MyClass obj2 obj1; // Copy constructor performs deep copy std::cout obj1: obj1.age() , obj1.name() std::endl; std::cout obj2: obj2.age() , obj2.name() std::endl; std::cout obj1 address: obj1 , obj2 address: obj2 std::endl; std::cout \n--- Modifying obj1s name --- std::endl; obj1.setName(Modified); std::cout After obj1.setName(Modified): std::endl; std::cout obj1: obj1.age() , obj1.name() std::endl; std::cout obj2: obj2.age() , obj2.name() std::endl; std::cout \nKey point: obj2s name remains Original (deep copy guarantees independent strings) std::endl; std::cout If this were shallow copy, obj2 would also show Modified\n std::endl; } 5. Deep Copy Demo [Constructor] age30, nameOriginal [CopyConstructor] Copied age30, nameOriginal obj1: 30, Original obj2: 30, Original obj1 address: 000000C8A58FF448, obj2 address: 000000C8A58FF498 --- Modifying obj1s name --- After obj1.setName(Modified): obj1: 30, Modified obj2: 30, Original指针成员指向的地址独立深拷贝修改obj1的nameobj2不会被修改。2.5对象移动移动语义是C11引入的重要特性旨在通过转移资源所有权而非复制数据来提升性能。其核心思想是将资源如堆内存从临时对象右值高效转移到目标对象避免不必要的深拷贝。//移动语义资源转移 → 源对象变为空 std::cout 6. Move Semantics Demo std::endl; { MyClass obj3(35, Source); std::cout \n--- Executing Move Constructor --- std::endl; MyClass obj4 std::move(obj3); // - Call move constructor, obj3 resources stolen std::cout After move obj3: obj3.age() , name obj3.name() (cleared) std::endl; std::cout After move obj4: obj4.age() , obj4.name() std::endl; std::cout Key point: Move semantics steals source object resources, source becomes empty\n std::endl; } 6. Move Semantics Demo [Constructor] age35, nameSource --- Executing Move Constructor --- [MoveConstructor] Moved age35, nameSource After move obj3: 0, name (cleared) After move obj4: 35, Source Key point: Move semantics steals source object resources, source becomes empty [Destructor] age35, nameSource [Destructor] age0, name Program End 可以看到使用move后obj3已经被销毁了不过没有调用析构函数o((ω ))o3.多态多态Polymorphism是面向对象编程的三大特性之一封装、继承、多态它允许不同类的对象对同一消息做出不同的响应。简单来说就是“一个接口多种实现”。不是我就问了一下多态是啥这agent直接给我写完了Σ(っ °Д °;)っ3.1 多态的基本概念在C中多态主要通过虚函数virtual function实现。当基类指针或引用指向派生类对象时通过虚函数可以调用到派生类中重写的函数而不是基类的版本。静态多态编译时多态通过函数重载和运算符重载实现在编译时确定调用哪个函数。动态多态运行时多态通过虚函数和继承实现在运行时根据对象的实际类型确定调用哪个函数。3.2 虚函数与纯虚函数虚函数使用virtual关键字声明允许在派生类中被重写。纯虚函数在声明时加上 0表示该函数没有实现包含纯虚函数的类称为抽象类不能实例化。#include iostream // 基类 class Animal { public: // 虚函数 virtual void makeSound() { std::cout Animal makes a sound std::endl; } // 纯虚函数 - 抽象类 virtual void move() 0; virtual ~Animal() {} // 虚析构函数 }; // 派生类 Dog class Dog : public Animal { public: void makeSound() override { std::cout Dog barks: Woof! Woof! std::endl; } void move() override { std::cout Dog runs on four legs std::endl; } }; // 派生类 Cat class Cat : public Animal { public: void makeSound() override { std::cout Cat meows: Meow~ std::endl; } void move() override { std::cout Cat walks gracefully std::endl; } }; int main() { // 基类指针指向派生类对象 Animal* animal1 new Dog(); Animal* animal2 new Cat(); // 多态调用 animal1-makeSound(); // 输出: Dog barks: Woof! Woof! animal1-move(); // 输出: Dog runs on four legs animal2-makeSound(); // 输出: Cat meows: Meow~ animal2-move(); // 输出: Cat walks gracefully delete animal1; delete animal2; return 0; }3.3 多态的优势代码复用通过基类接口统一处理不同派生类对象扩展性好新增派生类不需要修改现有代码接口统一提供一致的编程接口运行时灵活性程序运行时可以动态决定调用哪个函数3.4 QT中的多态应用在QT框架中多态被广泛应用。比如事件处理机制#include iostream #include vector #include thread #include chrono #include random class Event { public: enum Type { MouseEvent, KeyEvent, TouchEvent, Max_Type }; Event(Type type) : type(type) {} Type getType() const { return type; } private: Type type{}; }; class Object { friend class Application; public: void setName(const std::string name) { this-name name; } const std::string getName() const { return name; } protected: // 虚函数 - 事件处理 virtual bool event(Event* ev) { return false; } private: std::string name; }; class Widget : public Object { public: protected: // 重写基类的虚函数 bool event(Event* ev) override { switch (ev-getType()) { case Event::MouseEvent: std::cout Widget received MouseEvent std::endl; break; case Event::KeyEvent: std::cout Widget received KeyEvent std::endl; break; case Event::TouchEvent: std::cout Widget received TouchEvent std::endl; break; default: std::cout Widget received unknown event std::endl; break; } return true; } }; class Button : public Widget { protected: bool event(Event* ev) override { if (ev-getType() Event::MouseEvent) { std::cout Button clicked! Handling MouseEvent specially. std::endl; return true; } // 其他事件交给父类处理 return Widget::event(ev); } }; class Application : public Object { public: Application(int argc, char* argv[]) { std::cout Application created with argc arguments. std::endl; } int exec() { std::default_random_engine eng(std::random_device{}()); for (int i 0; i 10; i) { // 生成随机事件 Event ev(Event::Type(eng() % Event::Max_Type)); // 多态调用通过基类指针调用不同派生类的event函数 for (auto obj : objects) { obj-event(ev); } std::this_thread::sleep_for(std::chrono::milliseconds(300)); } return 0; } void addObject(std::shared_ptrObject obj) { objects.push_back(obj); } private: std::vectorstd::shared_ptrObject objects; }; int main(int argc, char* argv[]) { Application app(argc, argv); // 添加不同类型的对象 app.addObject(std::make_sharedWidget()); app.addObject(std::make_sharedButton()); app.addObject(std::make_sharedWidget()); app.addObject(std::make_sharedButton()); // 运行应用程序演示多态 return app.exec(); }3.5 运行结果示例Application created with 1 arguments. Widget received MouseEvent Button clicked! Handling MouseEvent specially. Widget received MouseEvent Button clicked! Handling MouseEvent specially. Widget received KeyEvent Button received KeyEvent Widget received KeyEvent Button received KeyEvent Widget received TouchEvent Button received TouchEvent Widget received TouchEvent Button received TouchEvent Widget received MouseEvent Button clicked! Handling MouseEvent specially. Widget received MouseEvent Button clicked! Handling MouseEvent specially.3.6 多态使用注意事项虚析构函数如果基类有虚函数应该将析构函数也声明为虚函数确保正确释放派生类资源override关键字C11引入明确表示重写基类虚函数增加代码可读性和安全性final关键字C11引入表示该函数不能在派生类中被进一步重写抽象类包含纯虚函数的类不能实例化只能作为接口使用多态是C面向对象编程的核心特性在QT框架中被大量使用。理解多态有助于编写更灵活、可扩展的代码特别是在GUI编程和事件处理中尤为重要。结语所以要学习QT不需要特别会C但需要了解一些在QT中常用的C基本知识✪ ω ✪PS终于写完了最近在用AI写一个QT项目目前AI的UI设计和整体架构做得都挺不错的不过具体实现一些复杂的功能表现还不是很好有没有大佬教一下UwU

相关新闻

Windows直装安卓应用3分钟攻略:APK Installer轻量安装器完全指南

Windows直装安卓应用3分钟攻略:APK Installer轻量安装器完全指南

Windows直装安卓应用3分钟攻略:APK Installer轻量安装器完全指南 【免费下载链接】APK-Installer An Android Application Installer for Windows 项目地址: https://gitcode.com/GitHub_Trending/ap/APK-Installer 先甩一个数据:为什么很多人弃用…

2026/10/15 17:02:19 阅读更多 →
网盘下载老是被限速?这款开源油猴脚本免费帮你本地解析直链,9大平台通用

网盘下载老是被限速?这款开源油猴脚本免费帮你本地解析直链,9大平台通用

网盘下载老是被限速?这款开源油猴脚本免费帮你本地解析直链,9大平台通用 【免费下载链接】Online-disk-direct-link-download-assistant 一个基于 JavaScript 的网盘文件下载地址获取工具。基于【网盘直链下载助手】修改 ,支持 百度网盘 / 阿…

2026/10/17 21:09:10 阅读更多 →
C++STL关联容器超全详解:set/map/unordered_map底层原理、红黑树哈希表深度对比、去重机制、性能坑点与工程选型

C++STL关联容器超全详解:set/map/unordered_map底层原理、红黑树哈希表深度对比、去重机制、性能坑点与工程选型

一、前言:为什么关联容器是面试压轴考点?在上一篇 我们彻底吃透了 vector / list / string 序列式容器。序列容器的特点是:元素按插入顺序存储,依靠位置查找。但在真实业务开发和算法刷题中,有两类高频场景是序列容器完…

2026/10/15 17:27:56 阅读更多 →

最新新闻

ROS2小车网页直控:单进程WebSocket桥接实现

ROS2小车网页直控:单进程WebSocket桥接实现

/* 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 2:16:57 阅读更多 →
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/18 2:16:57 阅读更多 →
Linux下HTTP排障全指南:从报文解剖到抓包实战

Linux下HTTP排障全指南:从报文解剖到抓包实战

先交代一个背景。很多年前我开始接手某台业务服务器的接口排查,遇到超时第一反应是去盯 TCP 层,看到一堆重传就怀疑机房网络,折腾一晚上毫无进展。后来旁边一位老同事用 tcpdump 按端口抓了完整会话,才发现问题出在一行 Connectio…

2026/10/18 2:16:57 阅读更多 →
氮化镓快充直驱控制器:8大厂商布局与核心技术解析

氮化镓快充直驱控制器:8大厂商布局与核心技术解析

/* 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 2:16:57 阅读更多 →
纯HTML Canvas图表插件:折线图、柱状图与饼图一次实现

纯HTML Canvas图表插件:折线图、柱状图与饼图一次实现

简介:统计折线图、柱状图与饼图一体的纯HTML图表插件,面向Web前端初学者、需要快速产出数据报表的开发者。压缩包共1个文件,为单个HTML文件,大小约3KB,浏览器直接打开即可体验。插件以原生HTML/CSS/JS结合第三方图表库…

2026/10/18 2:16:57 阅读更多 →
2009数模A题血管应力仿真MATLAB工程复现指南

2009数模A题血管应力仿真MATLAB工程复现指南

简介:本资源为2009年全国大学生数学建模竞赛A题参赛论文及配套源码,面向数学建模初学者、备赛学生与高校指导教师,聚焦实际工程问题的建模求解全流程实践。压缩包共9个文件,含5个txt文本(记录数据处理、评价结果、模型…

2026/10/18 2:15:57 阅读更多 →

日新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/18 0:00:55 阅读更多 →

月新闻

我发现了一个新思路:用 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/18 0:55:02 阅读更多 →
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/17 17:22:31 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →