C++面向对象底层实现:从汇编视角解析虚函数、内存布局与性能优化
1. 项目概述当汇编遇见C面向对象如果你写过C也接触过汇编可能会觉得这是两个完全不同的世界。一个在抽象层面构建复杂的对象关系和继承树另一个则在寄存器、内存地址和指令的泥潭里摸爬滚打。但最近我在重构一个对性能要求极高的模块时遇到了一个有趣的问题一个看似简单的C虚函数调用在性能剖析工具里却成了一个热点。为了彻底搞懂它的开销我不得不一头扎进编译器生成的汇编代码里。这个过程让我意识到理解C面向对象特性在汇编层面的实现绝不是学院派的炫技而是解决实际性能瓶颈、进行深度优化甚至调试复杂内存问题的必备技能。这就像你开一辆自动挡的车C大部分时候很舒服。但一旦车子出了怪声或者你想把它改装得更快你就必须打开发动机盖看看里面的机械结构汇编到底是怎么工作的。“汇编与C面向对象编程入门”这个主题就是教你如何打开发动机盖看懂C的“自动挡”是如何通过一系列精密的“机械传动”实现的。无论是为了写出更高效的C代码还是为了在逆向工程中理解别人的程序亦或是单纯满足对计算机底层运行机制的好奇心这门“跨界”知识都价值连城。2. 核心思路从高级抽象到底层指令的映射理解C面向对象在汇编层面的实现核心思路在于建立一种“映射”关系。编译器就像一个翻译官它把我们用高级语法类、对象、继承、多态书写的意图翻译成处理器能够直接理解和执行的、由简单指令组成的序列。我们的目标就是学会解读这份“翻译稿”。2.1 核心概念映射关系在开始看代码之前我们先建立几个最基本的映射关系这是理解后续所有内容的基础对象 (Object) - 内存块 (Memory Block)一个C对象在内存中就是一块连续的区域。这块区域的大小由它的数据成员成员变量决定。一个class Point { int x; int y; }的对象在32位系统上很可能就是一块8字节两个int的内存。成员变量 (Member Variable) - 内存偏移量 (Offset)对象内部的成员变量是通过相对于对象起始地址this指针的一个固定偏移量来访问的。例如point.y在汇编中可能就是[this 4]假设x在偏移0占4字节。成员函数 (Member Function) - 普通函数 隐藏参数非虚的成员函数和普通函数几乎没有区别只是编译器默默地给它增加了一个名为this的隐藏参数指向调用该函数的对象地址。point.move(dx, dy)在底层被处理成类似于move(point, dx, dy)的形式。this指针 - 对象地址这是连接成员函数和对象数据的桥梁。在成员函数内部任何对成员变量的访问都会通过this指针加上对应的偏移量来完成。有了这个基础认知我们就可以开始拆解更复杂的特性了。整个探索过程可以遵循一个清晰的路径从最简单的结构体/类到带有成员函数的类再到继承最后到多态虚函数。每一步我们都会用C代码和对应的汇编代码以x86-64和ARMv8为例进行对照讲解。3. 从结构体到类内存布局的起点让我们从最简单的“纯数据”结构开始。这是理解对象内存布局最直观的起点。3.1 C结构体与类的内存等价性在C中struct和class在内存布局上本质是相同的默认的唯一区别是成员访问权限。我们先看一个简单的结构体。// C 代码 struct PlainData { int id; float score; char tag; };这个结构体在内存中是如何排列的呢这里就涉及到“内存对齐”这个关键概念。处理器并非能从任意地址高效地读取任意类型的数据。例如一个4字节的int型变量如果其起始地址是4的倍数那么一次内存访问就能读出来如果起始地址是奇数可能就需要两次访问然后拼接数据效率低下。编译器会自动进行内存对齐以优化性能。对于上面的PlainDataid(int, 4字节)假设从偏移0开始。score(float, 4字节)下一个可用偏移是4是4的倍数对齐放在偏移4。tag(char, 1字节)放在偏移8。总大小此时总共用了9字节。但为了满足整个结构体对齐的要求通常是其最大成员对齐值的倍数这里最大是4编译器会在末尾填充3个字节的“空洞”使总大小变为12字节且结构体地址本身也是4的倍数。用一段简化的伪汇编视角来看访问obj.score; 假设 obj 的地址在寄存器 rdi 中 mov eax, [rdi 4] ; 从 obj地址4偏移处读取4字节到eax这就是 score注意内存对齐与平台强相关。上述分析基于典型的32/64位系统。不同的CPU架构x86, ARM、不同的编译器GCC, Clang, MSVC以及不同的编译选项如#pragma pack都会影响最终的内存布局。在编写需要跨平台或与硬件直接交互的代码如网络协议、文件格式时必须明确考虑对齐问题否则会导致数据错乱。3.2 引入成员函数this指针的现身现在我们给这个结构体加上成员函数把它变成一个类。class DataWithFunc { private: int id; float score; public: void setScore(float s) { score s; } float getScore() const { return score; } };DataWithFunc的对象内存布局和PlainData完全一样仍然只包含id和score这两个数据成员。成员函数setScore和getScore并不存储在每一个对象里而是像普通函数一样在代码段只有一份。关键的变化在于调用方式。当我们写obj.setScore(95.5f);时编译器会做以下转换将obj的地址作为隐藏的第一个参数即this指针传递给setScore函数。在setScore函数内部所有对成员变量如score的访问都通过this指针加上偏移量来完成。让我们看看GCC编译器为setScore函数可能生成的x86-64汇编简化版; C: void DataWithFunc::setScore(float s) ; 参数rdi this指针, xmm0 浮点数参数 s DataWithFunc::setScore(float): movss DWORD PTR [rdi4], xmm0 ; 将 s 的值存储到 this4 的位置即 score ret看非常直接[rdi4]就是通过this指针访问score成员。getScore函数也类似是一条movss xmm0, DWORD PTR [rdi4]的指令。实操心得理解this指针是理解成员函数调用的基石。你可以把任何非静态成员函数调用在脑海里自动转换成“普通函数调用第一个参数是对象地址”。这在调试时非常有用当你看到汇编调用指令call时可以快速定位哪个寄存器或栈位置存放着this指针。4. 单继承的布局拼接与偏移继承是面向对象的重要特性它允许我们构建“是一个is-a”的关系。在内存中单继承的实现通常非常直观派生类对象包含一个完整的基类子对象然后再加上自己的数据成员。4.1 简单单继承的内存模型class Base { public: int base_data; void baseFunc() { /* ... */ } }; class Derived : public Base { public: int derived_data; void derivedFunc() { /* ... */ } };Derived对象在内存中的布局可以看作是[ Base 子对象部分 ] [ Derived 新增部分 ] [ base_data (4字节) ] [ derived_data (4字节) ]Derived对象的起始地址同时也是其内部Base子对象的起始地址。这意味着一个指向Derived对象的指针可以直接当作指向Base对象的指针来使用无需任何地址转换。这是C多态能工作的基础之一。访问过程在Derived::derivedFunc内部访问base_datathis指向Derived起始地址 偏移0。在Derived::derivedFunc内部访问derived_datathis 偏移4跳过Base部分。如果有一个Base* ptr指向一个Derived对象调用ptr-baseFunc()函数接收到的this指针仍然是Derived对象的起始地址它访问base_data的偏移量0是固定的因此能正确工作。4.2 继承链与内存布局继承可以有多层内存布局就是一层层拼接下去。class Level1 { int a; }; class Level2 : public Level1 { int b; }; class Level3 : public Level2 { int c; };Level3对象布局[a][b][c]。Level3*可以安全转换为Level2*或Level1*因为它们都指向同一个起始地址。注意事项继承中的访问控制不影响布局。private或protected继承在语法上改变了访问权限但在内存布局上基类子对象依然存在于派生类对象中只是派生类用户不能将其当作基类对象来使用。在汇编层面你依然能看到完整的内存拼接。5. 多继承的挑战多个基类子对象当派生类从多个不相关的基类继承时情况变得复杂因为一个派生类对象内部需要包含多个独立的基类子对象。5.1 多继承的内存布局class Base1 { public: int b1_data; }; class Base2 { public: int b2_data; }; class MultiDerived : public Base1, public Base2 { public: int md_data; };MultiDerived对象的内存布局可能是[ Base1 子对象 ] [ Base2 子对象 ] [ MultiDerived 新增部分 ] [ b1_data ] [ b2_data ] [ md_data ]现在MultiDerived对象内部有两个不同的“起始点”地址A整个对象的起始也是Base1子对象的起始。地址B 地址A sizeof(Base1)这是Base2子对象在MultiDerived对象内的起始地址。5.2 指针调整static_cast与dynamic_cast的幕后工作这是多继承最需要理解的一点。当你进行指针转换时编译器可能需要自动调整指针的值。MultiDerived md; Base1* pb1 md; // 正确pb1 指向地址A。无需调整。 Base2* pb2 md; // 正确但这里发生了隐式调整。 // md 是地址A但pb2需要指向地址B。 // 编译器在背后给 md 加上了 sizeof(Base1) 的偏移。让我们看看第二行代码可能对应的汇编逻辑概念性lea rbx, [md] ; rbx md (地址A) mov [pb1], rbx ; pb1 rbx (地址A) 无需调整 lea rbx, [md] ; rbx md (地址A) add rbx, 4 ; rbx rbx sizeof(Base1) 地址B mov [pb2], rbx ; pb2 rbx (地址B) 发生了调整从Base2*转换回MultiDerived*时编译器则需要减去同样的偏移量。这种调整在static_cast和dynamic_cast中都会发生。常见问题与排查多继承下的指针比较。一个经典的坑是(void*)pb1和(void*)pb2的值是不相等的因为它们指向的是同一个MultiDerived对象内部的不同子对象。如果你需要判断两个基类指针是否指向同一个完整对象应该使用dynamic_cast或比较std::addressof转换后的结果而不是直接比较void*。6. 虚函数与多态运行时绑定的核心机制虚函数是C实现运行时多态动态绑定的基石。这是面向对象编程中最精妙也在汇编层面最值得探究的部分。6.1 虚函数表vtable与虚函数表指针vptr编译器如何知道在运行时该调用哪个函数呢答案是虚函数表Virtual Table, vtable。每个包含虚函数的类或从包含虚函数的类派生而来的类编译器都会为它创建一个唯一的vtable。这个表存储在程序的只读数据段如.rodata。vtable本质上是一个函数指针数组。数组的每一项指向该类的一个虚函数的具体实现。每个该类对象的内存布局最前端在考虑对齐之前编译器会插入一个隐藏的指针成员称为虚函数表指针vptr。vptr指向该对象所属类的vtable。class Shape { public: virtual void draw() const 0; // 纯虚函数 virtual double area() const { return 0.0; } virtual ~Shape() {} // 虚析构函数至关重要 }; class Circle : public Shape { public: void draw() const override { /* 画圆 */ } double area() const override { return 3.14 * radius_ * radius_; } private: double radius_; };Shape类有自己的vtable假设包含两个条目Shape::draw,Shape::areadraw是纯虚可能指向一个报错函数或空位。Circle类也有自己的vtable条目是Circle::draw,Circle::area。当一个Circle对象被创建时它的构造过程在汇编层面会做一件重要的事将它的vptr设置为Circle::vtable的地址。6.2 虚函数调用的汇编拆解这是最激动人心的部分。看看shapePtr-draw()这个调用是如何在汇编中实现的。Shape* shapePtr new Circle(); shapePtr-draw(); // 动态绑定调用 Circle::draw() delete shapePtr;对应的x86-64汇编核心逻辑可能如下高度简化省略了构造和析构细节; shapePtr-draw() 的调用过程 mov rax, QWORD PTR [rbx] ; rbx 保存 shapePtr。将对象头8字节即vptr加载到 rax。 ; 现在 rax 中保存的是 Circle 的 vtable 地址。 mov rax, QWORD PTR [rax] ; 从 vtable 的第一个偏移处通常是0加载函数指针。 ; 这得到了 Circle::draw 的实际地址。 call rax ; 调用该函数。此时 rbx (this指针) 作为隐含参数已就绪。这个过程可以分解为取 vptr通过对象地址this拿到vptr。查 vtable通过vptr加上固定的偏移量由虚函数在类声明中的顺序决定找到目标虚函数在表中的条目。跳转执行从该条目中取出函数地址然后跳转过去执行。为什么是运行时绑定因为shapePtr指向的对象类型在编译时是未知的它可能是Shape*,Circle*,Square*等只有到了运行时通过读取具体对象的vptr才能知道该去哪个vtable里找函数地址。这就是多态的本质。6.3 虚析构函数的重要性为什么基类的析构函数必须是虚的考虑上面的例子如果Shape的析构函数不是虚的那么delete shapePtr;只会调用Shape::~Shape()而不会调用Circle::~Circle()。这可能导致Circle中分配的资源比如radius_如果是指针的话泄露。在汇编层面虚析构函数意味着vtable中包含析构函数的条目。delete一个指向派生类对象的基类指针时会通过vptr找到派生类的析构函数并调用它。派生类的析构函数执行完后会自动调用基类的析构函数这是编译器生成的代码保证的。实操心得通过汇编理解开销。虚函数调用比普通成员函数调用多两次内存访问取vptr取函数地址和一次间接调用。在性能极其敏感的循环或底层代码中虚函数调用可能成为瓶颈。这时可以考虑使用CRTP奇异递归模板模式等静态多态技术来消除运行时开销或者将虚函数调用提到循环外部。7. 菱形继承与虚继承解决歧义与共享基类这是C多重继承中最复杂的部分引入了“虚继承”的概念来解决“菱形继承”问题。7.1 菱形继承的问题class Base { int data; }; class Middle1 : public Base { /* ... */ }; class Middle2 : public Base { /* ... */ }; class Bottom : public Middle1, public Middle2 { /* ... */ };这种继承结构像一个菱形。Bottom对象内部将包含两份Base子对象一份来自Middle1一份来自Middle2。这会导致歧义Bottom对象中访问Base::data时编译器不知道你要访问哪一份。空间浪费存储了两份相同的基类数据。逻辑错误如果Base代表一个“公共状态”我们通常希望Bottom只继承一份。7.2 虚继承的解决方案使用virtual关键字进行继承可以指定“虚基类”让派生类共享同一个基类子对象。class Base { int data; }; class Middle1 : virtual public Base { /* ... */ }; // 虚继承 class Middle2 : virtual public Base { /* ... */ }; // 虚继承 class Bottom : public Middle1, public Middle2 { /* ... */ };现在Bottom对象内部只包含一份Base子对象。Middle1和Middle2中不再直接包含Base子对象而是包含一个指向共享Base子对象的指针或偏移量信息。7.3 虚继承在汇编层的实现简析虚继承的实现比普通继承复杂得多通常通过以下一种或多种机制虚基类表指针vbptr类似vptr每个虚继承的类或包含虚基类的类的对象中会有一个指向“虚基类表”的指针。表中记录了到各个虚基类子对象的偏移量。在派生类构造函数中调整最派生类如Bottom的构造函数负责直接初始化虚基类Base。Middle1和Middle2的构造函数会通过某种机制如vbptr找到共享的Base子对象而不是自己初始化一个。访问虚基类成员data的汇编代码会多一次间接寻址先通过vbptr找到虚基类表再从表中查到Base子对象的偏移量最后通过this指针加上这个偏移量来访问data。这比直接访问多了一次内存读取开销更大。注意事项慎用虚继承。虚继承解决了菱形继承问题但带来了额外的运行时开销和复杂性。它破坏了“派生类对象包含基类子对象”的简单内存模型。在实际项目中除非确有必要如模拟接口继承否则应尽量避免使用多重继承更应谨慎使用虚继承。组合Composition通常是更好的选择。8. 实战调试与性能分析中的应用理解了上述原理我们就能在实战中解决一些棘手问题。8.1 使用调试器查看对象内存和vtable在GDB或LLDB中你可以直接检查对象的内存布局。(gdb) p /x obj # 以十六进制打印对象 (gdb) x /4gx obj # 查看对象头8字节可能是vptr (gdb) info vtbl obj # 有些调试环境支持直接查看虚表非标准命令 (gdb) p *(void**)obj # 强制将对象首地址解释为指针打印vptr值 (gdb) x/8a vptr_value # 查看vptr指向的虚函数表内容通过对比vptr的值和预期类的vtable地址可以判断对象是否被错误构造或内存被破坏。8.2 分析性能热点虚函数调用开销使用像perf(Linux) 或 VTune (Windows/Linux) 这样的性能剖析工具。如果你发现某个虚函数调用占用了大量CPU时间可以确认必要性这个调用路径是否必须使用多态能否用模板、策略模式或std::variant在编译期确定类型减少调用频率能否将虚函数调用移出最内层循环使用final如果某个派生类的虚函数不会被进一步重写将其标记为final。这虽然不一定能改变运行时间接调用因为通过基类指针调用仍需查表但能给编译器更多优化提示有时可能触发去虚拟化devirtualization优化。8.3 排查内存问题对象切片Object Slicing对象切片是C新手常犯的错误理解内存布局后就能明白其根源。class Base { /* 有数据成员 */ }; class Derived : public Base { /* 有更多数据成员 */ }; Derived d; Base b d; // 对象切片发生当用一个派生类对象赋值给一个基类对象时只会拷贝派生类对象中的基类子对象部分派生类独有的部分被“切掉”了。在汇编层面这就是一个从源地址d到目标地址b的、长度为sizeof(Base)的内存拷贝memcpy。任何对b的虚函数调用都将使用Base的vtable与原来的Derived对象再无关系。排查技巧当你发现多态行为异常特别是通过基类对象而非指针或引用操作时没有表现出多态性首先要怀疑是否发生了对象切片。确保多态操作总是通过指针Base*或引用Base进行。9. 不同编译器与架构的差异虽然C标准规定了语言的行为但底层实现如内存布局、vtable结构、调用约定是编译器相关的。了解主要差异有助于跨平台开发和调试。9.1 MSVC、GCC/Clang的差异vptr位置大多数编译器将vptr放在对象头部起始地址。这是主流做法GCC, Clang, MSVC。但在某些情况下或历史版本中可能存在差异。多重继承的vtable对于多继承派生类的vtable可能是一个拼接起来的大表也可能包含多个vtable指针。GCC和Clang通常为每个有虚函数的基类在对象中放置一个vptr如果基类没有虚函数则可能不放置。MSVC的实现也类似但具体布局细节可能不同。RTTI运行时类型信息typeid和dynamic_cast依赖的信息通常与vtable关联在一起但存储方式各编译器不同。9.2 x86-64 vs ARMv8 汇编视角调用约定x86-64 (System V ABI Linux/macOS)前6个整型/指针参数用寄存器rdi, rsi, rdx, rcx, r8, r9传递。this指针通常放在rdi中。返回值在rax。x86-64 (Microsoft x64 ABI Windows)前4个整型/指针参数用rcx, rdx, r8, r9传递。this指针放在rcx中。ARM64 (AAPCS64)前8个整型/指针参数用寄存器x0-x7传递。this指针通常放在x0中。返回值在x0。虚函数调用原理相同但指令和寄存器不同。x86-64 (GCC):; rbx object pointer mov rax, [rbx] ; load vptr call [rax] ; call first virtual functionARM64 (GCC/Clang):; x0 object pointer (this) ldr x8, [x0] ; load vptr into x8 ldr x8, [x8] ; load function pointer from vtable blr x8 ; branch with link to register (call)内存访问ARM架构对内存对齐的要求可能比x86更严格未对齐访问会导致硬件异常。在编写涉及直接内存操作的跨平台代码时要特别注意。理解这些差异能帮助你在看反汇编代码、分析core dump或进行跨平台性能调优时快速适应不同的环境。最终掌握汇编与C面向对象的关系就像是获得了透视高级语言的能力让你能更自信地编写高效、健壮且易于调试的代码。

相关新闻

Python自动化处理无标题文档的实用方案

Python自动化处理无标题文档的实用方案

1. 项目概述作为一名从业多年的内容创作者,我经常遇到一个令人头疼的问题——那些没有标题的项目文档。它们就像没有标签的档案盒,静静地躺在电脑文件夹里,日复一日地被遗忘。今天我想分享一套系统化的解决方案,帮助大家高效处理这…

2026/8/9 6:41:06 阅读更多 →
AI论文写作工具测评:提升本科生科研效率的9款神器

AI论文写作工具测评:提升本科生科研效率的9款神器

1. 项目概述:AI论文写作工具如何改变本科生科研起点 2026届本科生正面临一个全新的学术写作环境——AI辅助工具已经从简单的语法检查进化到能够深度参与论文构思、框架搭建甚至部分内容生成的智能伙伴。作为带过三届毕业设计的导师,我亲眼见证了学生们从…

2026/8/9 6:41:06 阅读更多 →
现代软件开发实战:从模块化设计到持续交付

现代软件开发实战:从模块化设计到持续交付

1. 项目概述"project - 2"这个看似简单的标题背后,实际上隐藏着一个典型的现代软件开发项目。作为一名经历过数十个项目的老兵,我见过太多类似命名的项目——它们往往代表着团队快速启动的需求,或是某个大型系统中的关键模块。这类…

2026/8/9 6:41:06 阅读更多 →

最新新闻

Windows系统管理全攻略:从架构解析到实战优化

Windows系统管理全攻略:从架构解析到实战优化

1. Windows系统课程全记录:从入门到进阶实战Windows作为全球使用最广泛的桌面操作系统,其学习价值不言而喻。作为一名IT培训师,我整理了这份完整的Windows系统课程记录,涵盖从基础操作到高级管理的全栈知识体系。不同于市面上零散…

2026/8/9 7:44:32 阅读更多 →
ADK Skill开发五大设计模式:构建健壮可维护的智能体技能

ADK Skill开发五大设计模式:构建健壮可维护的智能体技能

1. 项目概述:为什么ADK开发者需要关注Skill设计模式?如果你正在使用或探索ADK进行Agent开发,大概率已经体验过从零开始构建一个能稳定运行、逻辑清晰的Agent Skill(技能)的挑战。ADK,即Agent Development K…

2026/8/9 7:44:32 阅读更多 →
AI赋能青少年心理健康:技术挑战、伦理边界与工程实践

AI赋能青少年心理健康:技术挑战、伦理边界与工程实践

上周,我注意到一个消息,OpenAI 和美国心理学会(APA)宣布合作,要共同推进青少年心理健康领域的 AI 应用。这消息乍一看,像是又一个“AI赋能传统行业”的新闻,但如果你仔细想想,把“AI…

2026/8/9 7:44:32 阅读更多 →
Unity开发术语精讲:从核心概念到高效实践

Unity开发术语精讲:从核心概念到高效实践

1. 项目概述:一份Unity开发者的“命名宝典” 在Unity开发这条路上,我踩过不少坑,其中有一个看似不起眼却频繁绊倒新手甚至老手的“小石子”——命名。我说的不是变量命名规范,而是Unity引擎自身那些五花八门的术语、组件、API名称…

2026/8/9 7:44:32 阅读更多 →
设计师走访陌拜专业公司

设计师走访陌拜专业公司

作为设计师,你是不是也有过这样的经历:为了给项目找到独特的材料,带着图纸走进一家陌生的石材公司,却不知道从哪看起?别担心,这篇文章用问答形式,聊聊设计师走访专业石材公司那些事,…

2026/8/9 7:44:32 阅读更多 →
Unity游戏汉化终极指南:XUnity Auto Translator完整教程

Unity游戏汉化终极指南:XUnity Auto Translator完整教程

Unity游戏汉化终极指南:XUnity Auto Translator完整教程 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 你是否曾经因为语言障碍而无法畅玩心爱的Unity游戏?面对日语、英语或其他外…

2026/8/9 7:43:32 阅读更多 →

日新闻

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁 【免费下载链接】baidupankey 在线查询网盘提取码(维护中 rm repo) 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否曾经在深夜寻找一份重要资料&#x…

2026/8/9 0:01:47 阅读更多 →
如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/9 0:01:47 阅读更多 →
收藏!小白程序员轻松入门大模型,从Harness工程开始实践

收藏!小白程序员轻松入门大模型,从Harness工程开始实践

文章强调学习大模型不应只关注模型本身,而应重视模型外的系统搭建,即Harness。提出AgentModelHarness的实用公式,详细介绍Harness的四个层次:持久化层、执行层、控制层和观察与验证层。文章还探讨了上下文工程、工具设计、AGENTS.…

2026/8/9 0:03:48 阅读更多 →

周新闻

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁

5分钟告别提取码焦虑:baidupankey如何智能破解百度网盘资源锁 【免费下载链接】baidupankey 在线查询网盘提取码(维护中 rm repo) 项目地址: https://gitcode.com/gh_mirrors/ba/baidupankey 你是否曾经在深夜寻找一份重要资料&#x…

2026/8/9 0:01:47 阅读更多 →
如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南

如何快速生成中国车牌图片:Python开源工具完整指南 【免费下载链接】chinese_license_plate_generator 中国车牌生成器 项目地址: https://gitcode.com/gh_mirrors/ch/chinese_license_plate_generator 中国车牌生成器是一个基于Python的开源项目&#xff0c…

2026/8/9 0:01:47 阅读更多 →
收藏!小白程序员轻松入门大模型,从Harness工程开始实践

收藏!小白程序员轻松入门大模型,从Harness工程开始实践

文章强调学习大模型不应只关注模型本身,而应重视模型外的系统搭建,即Harness。提出AgentModelHarness的实用公式,详细介绍Harness的四个层次:持久化层、执行层、控制层和观察与验证层。文章还探讨了上下文工程、工具设计、AGENTS.…

2026/8/9 0:03:48 阅读更多 →

月新闻

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南

免费解锁百度网盘SVIP加速:macOS用户必备的下载提速终极指南 【免费下载链接】BaiduNetdiskPlugin-macOS For macOS.百度网盘 破解SVIP、下载速度限制~ 项目地址: https://gitcode.com/gh_mirrors/ba/BaiduNetdiskPlugin-macOS 还在为百度网盘macOS版的龟速下…

2026/8/8 17:02:44 阅读更多 →
终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换

终极ncmdump指南:3分钟实现网易云NCM音乐解密与格式转换 【免费下载链接】ncmdump 项目地址: https://gitcode.com/gh_mirrors/ncmd/ncmdump 还在为网易云音乐下载的NCM格式文件无法在其他播放器播放而烦恼吗?ncmdump解密工具帮你轻松解决这个困…

2026/8/9 0:45:04 阅读更多 →
HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

HarmonyOS 应用开发《掌上英语》第81篇: 智能体卡片:为英语学习 App 打造桌面级学习助手

AgentCard 智能体卡片:为英语学习 App 打造桌面级学习助手适用平台:HarmonyOS 7.0 (API 26 Beta)一、引言 HarmonyOS 7.0(API 26 Beta)新增了 AgentCard 智能体卡片能力,这是继 HMAF(鸿蒙智能体框架&#x…

2026/8/8 17:02:44 阅读更多 →