当前位置: 首页 > article >正文

【C++编程】类和对象(一)---(类的初识引入以及定义 | 类的访问限定符及封装特性 | 类的作用域 | 类的实例化以及类对象模型 | this指针)

目录前言一、面向过程和面向对象初步认识二、类的引入三、类的定义四、类的访问限定符及封装4.1 访问限定符4.2 封装五、类的作用域六、类的实例化七、类对象模型7.1 如何计算类对象的大小7.2 类对象的存储方式7.3 结构体内存对齐规则八、this指针8.1 this指针的引出8.2 this指针的特性8.3 C语言和C实现Stack的对比九、总结前言上一篇【C编程】C入门三---内联函数 | auto关键词(C11) | 范围for循环(C11) | 指针空值nullptr(C11)-CSDN博客这个是C入门篇最后一篇接下来就是进入相当关键、核心的章节类和对象篇。整个篇章会相对难理解并且需要联系在一起所以需要每一步的知识点都要吃透。一、面向过程和面向对象初步认识C语言是面向过程的关注的是过程分析出求解问题的步骤通过函数调用逐步解决问题。C是基于面向对象的关注的是对象将一件事情拆分成不同的对象靠对象之间的交互完成。在深度理解面向对象这个东西还需要后面不断的学习这里再举个相对容易理解的例子比如说有一个外卖系统在面向过程思维中可以分为上架、点餐、派单、送餐这几个过程步骤。外卖会根据这些步骤写对应的函数。在面向对象思维中呢它会更加注重的是参与到这个过程的对象。比如在这个外卖系统中有商家、骑手、用户这3个对象 并且会更加注重这些对象之间的关系。根据这些来写对应的类来描述该对象的状态比如我们点完外卖时可以看地图中骑手的位置商家的位置以及我们用户的位置等这些关系。这就更像现实世界类和对象映射到虚拟计算机系统。可以认为面向对象是比面向过程更高级的语言。二、类的引入C语言结构体中只能定义变量。但是在C中结构体内不仅可以定义变量也可以定义函数。这是因为在C中结构体struct已经被升级成类了。比如在之前在数据结构与算法的基础篇C语言中的栈栈结构实现解析后进先出LIFO原理_栈后进先出-CSDN博客我们用C用struct类实现是怎样的呢这里需要注意一个点就是C是兼容C语言的所以struct以前的用法都可以用同时struct升级成了类。#includeiostream typedef int DataType; struct Stack { //定义变量 DataType* a; // 数组实现栈 int top; // 维护栈顶 int capacity; // 容量 }; int main() { // C兼容C语言所以struct以前的用法都可以继续用 struct Stack st1; // 比如在C语言中定义结构体strcut需要这样定义 // 同时struct升级成了类 Stack st2; // 类名可以直接用 return 0; }然后最大的改变就是在C类中是分为成员变量以及成员函数的。也就是可以在类中定义函数。#includeiostream typedef int DataType; struct Stack { // 成员函数 void Init(int defaultCapacity 4) { a (DataType*)malloc(sizeof(DataType) * defaultCapacity); if (a nullptr) { perror(malloc fail:); return; } top 0; capacity defaultCapacity; } void Push(int x) { // 扩容... a[top] x; } int Top() { return a[top - 1]; } void Destroy() { if (a) { free(a); a nullptr; capacity 0; top 0; } } // 成员变量 DataType* a; // 数组实现栈 int top; // 维护栈顶 int capacity; // 容量 }; int main() { struct Stack st1; st1.Init(20); Stack st2; st2.Init(); st2.Push(1); std::cout st2.Top() ; st2.Push(2); std::cout st2.Top() ; st2.Push(3); std::cout st2.Top() ; st1.Destroy(); st2.Destroy(); return 0; }从上图有一个注意点是成员变量写在哪有没有影响呢是没有的你可以将定义成员变量写在类中的前面或者夹在两个成员函数的中间或者最后面因为类是一个整体。我们还可以从上图可以看到我们在定义函数时可以不用像之前一样函数名前面加一个Stack比如STInit来区分了。如果要实现队列我们可以再创建一个队列的类struct Queue { void Init(); }; struct Stack { void Init(); };这样就可以区分哪个类需要哪个就在那个类定义哪一个。因为类也是一个作用域类域所以可以在不同作用域中实现同名函数。但是比起上面用struct来实现类在C中更喜欢用class来代替。三、类的定义class className { // 类体由成员函数和成员变量组成 }; // 一定要注意后面的分号class为定义类的关键字className为类的名字{}中为类的主体注意类定义结束时后面分号不能省略。类体中内容称为类的成员类中的变量称为类的属性或成员变量类中的函数称为类的方法或者成员函数。类的两种定义方式声明和定义全部放在类体中需注意成员函数如果在类中定义编译器可能会将其当成内联函数处理。类声明放在.h文件中成员函数定义放在.cpp文件中注意成员函数名前需要加 (类名::)由于在类里面的定义的函数如果较短会进行内联函数处理所以可以函数实现很简单的话可以直接在类中定义如果复杂的话就可以让声明与定义分离来增加可读性。一般情况下是采用第二种方式会更加正式并且更加工程化。不过我这里为了更好的演示就先采用第一种。成员变量命名规则的建议class Date { public: void Init(int year) { // 这里的year到底是成员变量还是函数形参 year year; } private: int year; };其中public和private下面会讲我们先观察到如果在类中的成员变量和成员函数的形参中变量名相同的话会比较难区分。所以我们一般会在成员变量前加一个_来表示是成员变量比如_year。可以写成任何形式目的只是为了做区分然后提高可读性。class Date { public: void Init(int year) { _year year; } private: int _year; };四、类的访问限定符及封装4.1 访问限定符我们会发现class和struct都是类但是在上面用struct实现的类变成class会出现报错这是因为权限的问题因为C要兼容C语言所以为了不用C的语法能正常使用所以struct默认都为public公有而class默认都为private私有其他作用域不能进行访问这又和C实现封装特性有一定的关系。C实现封装的方式用类将对象的属性与方法结合在一块让对象更加完善通过访问权限选择性的将其接口提供给外部的用户使用。【访问限定符说明】public修饰的成员在类外可以直接被访问。protected和private修饰的成员在类外不能直接被访问此处protected和private是类似的在继承中这两个限定符才有差异。访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现为止。如果后面没有访问限定符作用域就到 } 即类结束。class的默认访问权限为privatestruct为public因为struct要兼容C注意访问限定符只在编译时有用当数据映射到内存后没有任何访问限定符上的区别。即访问限定符是编译期约束运行时没有“访问权限检查”的开销。所以在这里我们就知道在C中struct和class的区别为C需要兼容C语言所以C中struct可以当成结构体使用。另外C中struct还可以用来定义类。和class定义类是一样的区别是struct定义的类默认访问权限是public公有class定义的类默认访问权限是private私有。注意在继承和模板参数列表位置struct和class也有区别后面再进行详解。4.2 封装面向对象的三大特性封装、继承、多态。封装将数据和操作数据的方法进行有机结合隐藏对象的属性和实现细节仅对外公开接口来和对象进行交互。封装本质上是一种管理让用户更方便使用类。比如对于电脑这样一个复杂的设备提供给用户的就只有开关机键、通过键盘输入显示器USB插孔等让用户和计算机进行交互完成日常事务。但实际上电脑真正工作的却是CPU、显卡、内存等一些硬件元件。对于计算机使用者而言不用关心内部核心部件比如主板上线路是如何布局CPU内部是如何设计的等用户只需要知道怎么开机、怎么通过键盘和鼠标与计算机进行交互即可。因此计算机厂商在出厂时在外部套上壳子将内部实现细节隐藏起来仅仅对外提供开关机、鼠标以及键盘插孔等让用户可以与计算机进行交互即可。在C语言中实现封装可以通过类将数据以及操作数据的方法进行有机结合通过访问权限来隐藏对象内部实现细节控制哪些方法可以在类外部直接被使用。我们在代码上怎么实现这个封装呢也就是在类中访问限定符将类中的成员变量用protected保护或者private私有进行保护然后将类中的方法成员函数用public公有就是实现了封装。五、类的作用域类定义了一个新的作用域类的所有成员都在类的作用域中。在类体外定义成员时需要使用 :: 作用域操作符指明成员属于哪个类域。class Person { public: void PrintPersonInfo(); private: char _name[20]; char _gender[3]; int _age; }; // 这里需要指定PrintPersonInfo是属于Person这个类域 void Person::PrintPersonInfo() { std::cout _name _gender _age std::endl; }在不同的文件中定义函数也是这样实现在上面有提过。六、类的实例化用类类型创建对象的过程称为类的实例化类是对对象进行描述的是一个模型一样的东西限定了类有哪些成员定义出一个类并没有分配实际的内存空间来存储它比如入学时填写的学生信息表表格就可以看成是一个类来描述具体学生信息。一个类可以实例化出多个对象实例化出的对象占用实际的物理空间用于存储类成员变量Person是类名而非对象是不具有空间的非静态成员变量_age必须通过类的对象、指向对象的指针或引用来访问不能直接通过类名操作。做个比方。类实例化出对象就像现实中使用建筑设计图建造出房子类就像是一个蓝图设计图只设计出需要什么东西但是并没有实体的建筑存在同样类也只是一个设计实例化出的对象才能实际存储数据占用物理空间所以在类中的成员变量是定义还是声明呢在变量中定义和声明的区别就是是否开空间所以在类中的写的成员函数都是声明它是不占空间的因为它只是一个“图纸”是不能住人的。可以认为“类本身是一种数据类型int、double等在没有定义对象前是不占用内存空间的定义对象的时候才会分配空间”。这里就是还有一个可能会让人导致疑惑的点int main() { Person p1; std::cout sizeof(Person) std::endl; std::cout sizeof(p1) std::endl; return 0; }我们会发现这两个计算的都是一样的那不是说类名没有空间吗为什么还是可以计算出来这个其实很简单把Person类想成一个设计图纸那里面设计的房子之类的不占用内存但是能不能根据你这个图纸计算你要建造房子对象的大小呢答案是可以的。而直接计算房子p1就更好理解了相对于拿的尺子直接计算这个房子有多大原理是一样的。七、类对象模型7.1 如何计算类对象的大小比如我们拿一个很简单的类来说class A { public: void PrintA() { std::cout _a std::endl; } private: char _a; };相比于之前C语言的结构体来说在C中类里面可以存在函数方法。那么问题就是类中既可以有成员变量又可以有成员函数那么一个类的对象中包含了什么如何计算一个类的大小先看结论结论一个类的对象中只包含成员变量所以计算一个类的大小就是根据C语言中结构体的内存对齐规则来计算大小的。即只算成员变量的大小根据内存对齐规则不考虑成员函数7.2 类对象的存储方式我们来思考一下为什么类创建的对象中只包含成员变量而不包含成员函数呢要明白这个问题先看下面这个案例class A { public: void PrintA() { std::cout _a std::endl; } private: char _a; }; int main() { A a1; A a2; A a3; a1.PrintA(); a2.PrintA(); a3.PrintA(); return 0; }类就拿我们上面写的简单的A类来演示。我们用A类创建了3个对象a1、a2、a3。那a1和a2以及a3中的变量_a是否一样肯定是不一样的因为我们就是要拿一个类来创建多个对象存储不同的数据。所以用类来创建对象时给每一个对象的成员变量分配内存空间是必要的。再看我们用对象a1、a2、a3分别都调用了PrintA函数方法那调用的这个PrintA函数是否一样呢我们发现只是传的值不同而已有必要在每一个创建的对象都创建这个函数吗是没必要的它们调用的都是同一份函数。如果每一个创建的对象中都保存这一份代码成员函数相同的代码保存多次会导致浪费空间。这里可以用一个抽象的例子来理解比如A类就是一个住宅区的设计蓝图那A类中的成员变量就是每一个房子的卧室、厨房等成员函数就是篮球场、健身房之类的。而用A类创建的对象就是一个家那每一家都要搞一个卧室的空间、厨房的空间等那篮球场和健身房需要每一家都有吗这是不需要的因为这样会很浪费。那成员函数到底去哪了呢所以在类中创建的对象是只保存成员变量成员函数存放在公共的代码区中。7.3 结构体内存对齐规则通过上面我们已经知道计算类对象和计算结构体的内存大小方式是一样的就只根据内存对齐规则来计算成员变量。这里我们来简单讲解一下结构体的内存对齐规则第一个成员在与结构体偏移量为0的地址处。其他成员变量要对齐到某个数字对齐数的整数倍的地址处。注意对齐数 编译器默认的一个对齐数 与 该成员大小的较小值。VS中默认的对齐数为8结构体总大小为最大对齐数所有变量类型最大者与默认对齐参数取最小的整数倍。如果嵌套了结构体的情况嵌套的结构体对齐到自己的最大对齐数的整数倍处结构体的整体大小就是所有最大对齐数含嵌套结构体的对齐数的整数倍。根据上面的结构体的内存对齐规则我们简单练习一下比如有下面这个类class A1 { public: void f1() {} private: int _a; char _ch; };根据规则2和3我们首先求出该类的最大对齐数成员变量的类型为int(4字节)、char(1字节) -- 得出其中最大值为int(4字节) -- 最后比较这个最大值和编译器默认对齐数VS默认对齐数为8的最小值 -- 得出该类的最大对齐数为4。我们就知道每一个成员变量要对齐4的整数倍的地址处并且结构体的总大小也要为4的整数倍。最后得出该类对象的大小为8字节。但是如果_a和_ch换个顺序的话存储内存会变的不一样并且在一些情况下类对象的大小也会发生变化。虽然此时类对象的大小仍为8字节但若成员变量更多时不同排列顺序可能导致对象大小发生变化。不过还有一个特殊情况// 类中仅有成员函数 class A1 { public: void f1() {} }; // 类中什么都没有---空类 class A2 { }; int main() { std::cout sizeof(A1) std::endl; // 1 std::cout sizeof(A2) std::endl; // 1 return 0; }在上面代码中没有成员变量的情况下为什么类对象的大小还为1字节呢我们可以这样理解即使这个房子没有东西但是我们还是要给它留一个位置因为它确实在这里有个房子只是没有东西而已。即没有成员变量的类对象需要1字节是为了占位表示对象存在不存储有效数据。那现在我们再思考一个问题为什么要内存对齐我原本5字节就能存储的为什么还要对齐导致用8字节来存储内存对齐主要是为了提升CPU访问内存的效率。因为CPU从内存读取数据时并不是一个字节一个字节地读而是一次取多个连续字节称为“一次内存操作”。在32位系统中CPU一次通常取4字节32位 ÷ 8 4字节在64位系统中一次取8字节64位 ÷ 8 8字节。拿我们上面的例子假设CPU一次读4字节的情况下我们在每一次操作_ch或者_a的数据时在内存对齐时每次都只需要进行一次内存操作就拿到了该数据但是在内存不对齐时我们发现_a的数据要进行两次内存操作才能读出完整的数据。总结就是内存对齐是“用空间换时间”虽然会浪费一些空间导致内存空间利用率下降但是换来的是CPU访问速度提升、缓存效率优化、并行访问更高效。八、this指针8.1 this指针的引出我们先来定义一个日期类Date#includeiostream class Date { public: void Init(int year, int month, int day) { _year year; _month month; _day day; } void Print() { std::cout _year - _month - _day std::endl; } private: int _year; // 年 int _month; // 月 int _day; // 日 }; int main() { Date d1, d2; d1.Init(2026, 1, 15); d2.Init(2026, 3, 4); d1.Print(); d2.Print(); return 0; }对于上面这个日期Date类有一个问题Date类中有Init 与 Print 两个成员函数函数体中没有关于不同对象的区分那当d1调用Init函数时该函数是如何知道应该设置d1对象而不是设置d2对象呢从上图也可以看到调用的是同一个函数地址也相同但是怎么做的区分呢既然可以排除函数重载而且函数本身和对象也不会有关系那能导致不同的结果的可能就只能往参数那里想了。C中通过引入this指针解决该问题即C编译器给每个“非静态成员函数”增加了一个隐藏的指针参数让该指针指向当前对象函数运行时调用该函数的对象在函数体中所以“成员变量”的操作都是通过该指针去访问。只不过所有的操作对用户是透明的即用户不需要来传递编译器自动完成。8.2 this指针的特性this指针的特性this指针的类型类类型* const即成员函数中不能给this指针赋值。只能在“成员函数”的内部使用。this指针本质上是“成员函数”的形参当对象调用成员函数时将对象地址作为实参传递给this形参。所以对象中不存储this指针。this指针是“成员函数”第一个隐含的指针形参一般情况由编译器通过ecx寄存器自动传递vs下面对this指针传递进行优化对象地址是放在ecxecx存储this指针的值不需要用户传递。所以根据上面我们可以得出在上述日期类的代码中会变成这里出现报错的原因就是this指针是隐含的指针形参用户不能自己去传递。并且可以发现在函数内部使用this-_year等操作不会报错。也就是说this不能在形参和实参显示传递但是可以在函数内部显示使用。还有一点就是要区别类类型* const和类类型 const*的效果就拿日期类来说即Date* const和Date const*。简单来说就是Date* const指的是this这个指针是不能更改的但是this指向的内容是可以更改的。比如Init函数中我们使用this-_year year;进行初始化也就是指向内容是可以更改的。然后Date const* 就是反的来了this这个指针可以更改比如将this nullptr;赋值空指针但是this它指向的内容是不可以更改的。简单来说就是看 const 在 * 的哪一边如果在右边比如 Date* const 就是说明这个this指针是不能指向其他地方但是其指向的内容可以修改如果在左边比图 Date const* 或者 const Date* 都是说明this指针是可以指向其他地方的但是指向的内容是常量也就是不能修改接下来思考一些问题比如this指针存在哪里的这里可能很多人误以为this指针应该就在对象里面其实不是的从上面计算类对象的大小时并没有额外计算this指针所以肯定不是在对象里面的。我们可以发现this是形参所以this指针是跟普通参数一样存在函数调用的栈帧里面。然后接下来再来看看下面两个代码会出现什么问题第一个class A { public: void Print() { std::cout Print() std::endl; } private: int _a; }; int main() { A* p nullptr; p-Print(); return 0; }上面这个程序运行会出现什么问题呢是编译报错还是运行崩溃又或者是正常运行呢首先其中没有语法错误之类的即使因为空指针的问题运行不了那也是运行的问题编译是不可能报错的所以就排除了。再来看怎样会运行崩溃面对空指针如果对空指针进行解引用的话就会运行崩溃这里我们看看该程序是否有解引用可以看到在汇编指令中并没有出现对对象p解引用的指令而是简单的将p用寄存器传递然后调用成员函数传空指针是不会出问题的。并且成员函数也不在对象中所以可以正常调用。总结就是p调用Print不会发生解引用因为Print的地址不在对象中。p会作为实参传递给this指针。然后this指针是空的但是函数内没有对this指针解引用。所以是可以正常运行的。第二个class A { public: void PrintA() { std::cout _a std::endl; } private: int _a; }; int main() { A* p nullptr; p-PrintA(); return 0; }通过第一个的问题应该知道这个程序会出现什么问题了可以从汇编指令中看到dword这个解引用的操作解引用一个空指针肯定会运行崩溃的。总结p调用PrintA不会发生解引用因为PrintA的地址不在对象中。p会作为实参传递给this指针。然后this指针是空的但是函数内访问_a本质是this-_a。也就是对this这个空指针进行了解引用发生运行崩溃。8.3 C语言和C实现Stack的对比C语言实现这里可以看我前面写过的博客栈结构实现解析后进先出LIFO原理_栈的进出原则-CSDN博客typedef int STDataType; typedef struct Stack { STDataType* a; int top; int capacity; }ST; //初始化 void STInit(ST* ps) { assert(ps); ps-a (STDataType*)malloc(sizeof(STDataType) * 4); if (ps-a NULL) { perror(malloc fail); return; } ps-capacity 4; ps-top 0; //top代表栈顶元素的下一个位置 //ps-top -1; //top代表栈顶元素 } //销毁栈 void STDestroy(ST* ps) { assert(ps); free(ps-a); ps-a NULL; ps-top 0; ps-capacity 0; } //入栈 void STPush(ST* ps, STDataType x) { assert(ps); if (ps-top ps-capacity) { STDataType* tmp (STDataType*)realloc(ps-a, sizeof(STDataType) * ps-capacity * 2); if (tmp NULL) { perror(realloc fail); return; } ps-a tmp; ps-capacity * 2; } ps-a[ps-top] x; ps-top; } //判断栈是否为空 bool STEmpty(ST* ps) { assert(ps); return ps-top 0; } //出栈 void STPop(ST* ps) { assert(ps); assert(!STEmpty(ps)); ps-top--; } //返回栈的元素个数 int STSize(ST* ps) { assert(ps); return ps-top; } //返回栈顶元素 STDataType STTop(ST* ps) { assert(ps); assert(!STEmpty(ps)); return ps-a[ps-top - 1]; } int main() { ST s; STInit(s); STPush(s, 1); STPush(s, 2); STPush(s, 3); STPush(s, 4); printf(%d\n, STTop(s)); printf(%d\n, STSize(s)); STPop(s); STPop(s); printf(%d\n, STTop(s)); printf(%d\n, STSize(s)); STDestroy(s); return 0; }可以看到在C语言实现时Stack相关操作函数有以下共性每个函数的第一个参数都是ST*函数中必须要对第一个参数检测因为该参数可能会为NULL函数中都是通过ST*参数操作栈的调用时必须传递ST结构体变量的地址结构体中只能定义存放数据的结构操作数据的方法不能放在结构体中即数据和操作数据的方式是分离开的而且实现上相当复杂涉及到大量指针操作稍不注意可能就会出错。C实现typedef int STDataType; class Stack { public: //初始化 void Init(int defaultCapacity 4) { _a (STDataType*)malloc(sizeof(STDataType) * defaultCapacity); if (_a nullptr) { perror(malloc fail); return; } _capacity defaultCapacity; _top 0; } //入栈 void Push(STDataType x) { if (_top _capacity) { STDataType* tmp (STDataType*)realloc(_a, sizeof(STDataType) * _capacity * 2); if (tmp NULL) { perror(realloc fail); return; } _a tmp; _capacity * 2; } _a[_top] x; _top; } //出栈 void Pop() { if (Empty()) return; _top--; } //判断栈是否为空 bool Empty() { return _top 0; } //返回栈的元素个数 int Size() { return _top; } //返回栈顶元素 STDataType Top() { return _a[_top - 1]; } //销毁栈 void Destroy() { free(_a); _a nullptr; _top 0; _capacity 0; } private: STDataType* _a; int _top; int _capacity; }; int main() { Stack s; s.Init(); s.Push(1); s.Push(2); s.Push(3); s.Push(4); std::cout s.Top() std::endl; std::cout s.Size() std::endl; s.Pop(); s.Pop(); std::cout s.Top() std::endl; std::cout s.Size() std::endl; s.Destroy(); return 0; }C中通过类可以将数据 以及 操作数据的方法进行完美结合通过访问权限可以控制那些方法在类外可以被调用即封装在使用时就像使用自己的成员一样更符合人类对一件事的认知。而且每个方法不需要传递ST*参数编译器编译之后该参数会自动还原即C中ST*参数是编译器维护的C语言中需用用户自己维护。在学完this指针后我们也会知道s.push(x)和STPush(s,x)没有本质上的区别因为s.push(x)其实也是要传递地址也就是也要两个参数但是是隐含的。所以从这里可以看出来C语言和C本质上就是手动挡和自动挡的区别C语言需要自己控制离合即变速箱但是容易操作不当导致熄火而C有电脑程序控制变速箱会更加方便不容易出错。更何况C还有一个封装的特性。九、总结本章详细讲解了类的认识以及类的访问限定符的封装特性、作用域、实例化、对象模型怎么计算大小也描述了怎么内存对齐最后就是this指针。这篇作为类和对象的开篇还是非常有价值的。后一两篇会着重讲解类的默认成员函数、以及运算符重载。内容非常重要并且需要C篇以及本章节的知识所以希望各位好好学习这方面的知识。写文不易希望各位给个三连~言已至此感谢各位读者花费时间阅读本人浅学才疏如有文笔拙劣之处还望见谅~

相关文章:

【C++编程】类和对象(一)---(类的初识引入以及定义 | 类的访问限定符及封装特性 | 类的作用域 | 类的实例化以及类对象模型 | this指针)

目录 前言 一、面向过程和面向对象初步认识 二、类的引入 三、类的定义 四、类的访问限定符及封装 4.1 访问限定符 4.2 封装 五、类的作用域 六、类的实例化 七、类对象模型 7.1 如何计算类对象的大小 7.2 类对象的存储方式 7.3 结构体内存对齐规则 八、this指针…...

EgoScale:利用多样化的自我为中心人类数据来扩展灵巧操作

26年2月来自NV、UC Berkeley和U Maryland的论文“EgoScale: Scaling Dexterous Manipulation with Diverse Egocentric Human Data”。 人类行为是学习物理智能最具可扩展性的数据来​​源之一,但如何有效地利用这些数据进行灵巧操作训练仍不明确。虽然以往的研究已…...

FreeRTOS的队列介绍以及怎么实现互斥访问,休眠唤醒以及保存数据(环形缓冲区)

前言前面介绍完了FreeRTOS的一些核心功能,如任务切换,创建任务等等,并将煮包从ARM内核以及内存的视角的相关思考进行了分享,从这里开始介绍FreeRTOS的另外一个板块,就是任务间通信机制,如队列、信号量、互斥…...

豆包 LintCode 2798 · Aop 简化日志

你想要解决的是 LintCode 2798 这个关于使用 AOP(面向切面编程)简化日志记录的问题,核心需求应该是通过 AOP 的方式,在不侵入业务代码的前提下,为方法添加统一的日志记录功能。 问题分析与实现思路 AOP 的核心思想是横切关注点,日志记录就是典型的横切关注点。我们可以…...

Arduino 第一部分

一.Arduino IDE界面和设置1.选择开发板型号和端口(1)首先将开发板通过USB线连接到电脑上。需要注意的是,USB线需要插牢,有时候USB线未插牢,开发板上的灯也会亮(2)选择开发板型号①可以通过上方的…...

一键脚本安装OpenClaw时遇到问题怎么办?

在使用一键脚本安装 OpenClaw 时遇到问题,核心解决思路是先定位报错类型,再按 “基础排查→针对性修复→替代方案” 的顺序解决,下面我会把新手最常遇到的问题、原因和具体解决方法都列出来,你可以对照排查。 一、先做 3 步基础排…...

OpenClaw + Google Chrome(deb)+ WSLg:可视化浏览器自动化与人工接管教程

目标:在 WSL2 Ubuntu WSLg 环境中,使用 OpenClaw 控制 Linux 浏览器 GUI(非无头),实现自动登录/浏览网页/操作网页,并在遇到验证码(扫码、滑块、人机验证)时支持人工直接接管浏览器…...

豆包 LeetCode 679.24点游戏 public boolean judgePoint24(int[] cards)

这题是经典回溯 四则运算枚举,直接给你能提交的 Java 代码。 思路 1. 把数组转成 double 列表,避免整数除法问题。 2. 每次任选两个数,做 - * / 六种运算: ab, a*b(交换律,只算一次)a-b, …...

python flask django网络在线选课成绩管理系统

目录系统架构设计数据库模型设计核心功能模块成绩管理模块系统安全措施部署方案测试计划开发路线图项目技术支持可定制开发之功能创新亮点源码获取详细视频演示 :文章底部获取博主联系方式!同行可合作系统架构设计 采用前后端分离架构,前端使…...

AAAI 2026 即插即用 | Transformer篇 | DHOGSA:新型自注意力!HOG先验引导特征精准聚焦边缘,PSNR猛涨!

VX: shixiaodayyds,备注【即插即用】,添加即插即用模块交流群。 文章目录 模块出处 模块介绍 模块提出的动机(Motivation) 适用范围与模块效果 模块代码及使用方式 模块出处 Paper:Gradient as Conditions: Rethinking HOG for All-in-one Image Restoration Code:https…...

【C++初阶】:C++入门相关知识(3):引用 inline内联函数 nullptr相关概念

🎈主页传送门:良木生香 🔥个人专栏:《C语言》 《数据结构-初阶》 《程序设计》《鼠鼠的C学习之路》 🌟人为善,福随未至,祸已远行;人为恶,祸虽未至,福已远离 前言:在上一篇文章中,我们学习了C的输入输出,缺省…...

C++继承、重载、多态相关问题(简单但通俗易懂)

第九章 组合与继承 一、比较 is-a 关系和 is-like-a 关系 1 is-a 关系 表示严格的继承关系。 含义:派生类是基类的一种特殊类型。例如: Dog is a Animal代码: class Animal{}; class Dog : public Animal{};特点: 派生类对象 可以…...

(其他)C1/C2驾照教程

目录1 科目二1.0 开车前检查1.1 倒车入库1.1.1 右倒库注意事项1.1.2 左倒库注意事项1.2 曲线行驶1.3 直角转弯1.4 侧方停车1.5 半坡起步1 科目二 本文介绍科目二的四个项目:倒车入库、曲线行驶、直角转弯、侧方停车。 1.0 开车前检查 调整座椅到合适的位置&#…...

[工具] 影子去除工具,可以批量去除影子,自动裁切透明,自动更新偏移坐标

影子去除工具,可以批量去除影子,自动裁切透明,自动更新偏移坐标一款专业的图片阴影去除工具,能够智能识别并去除图片中的阴影,还原物体真实颜色,广泛应用于照片修复、产品图处理、文档扫描优化等场景。 ##…...

代码随想录算法训练营day15| 110.平衡二叉树 (优先掌握递归)、 257. 二叉树的所有路径 (优先掌握递归)、 404.左叶子之和 (优先掌握递归)、 222.完全二叉树的节点个数(优先掌握

一、110.平衡二叉树 (优先掌握递归) 题目链接/文章讲解/视频讲解:https://programmercarl.com/0110.%E5%B9%B3%E8%A1%A1%E4%BA%8C%E5%8F%89%E6%A0%91.html 初见思路: 学习代码随想录之后:平衡二叉树:左右子…...

leetcode 1409. 查询带键的排列

Problem: 1409. 查询带键的排列 考虑到实际模拟的话太耗费时间了&#xff0c;所以用哈希表来表示 数字-索引&#xff0c;然后对每个查询&#xff0c;拿到相应数字对应的索引ind&#xff0c;并且修改在索引ind前面的数字的索引都1 Code class Solution { public:vector<int…...

一次线上事故,我学到了事件驱动架构的5个教训

凌晨3点17分&#xff0c;监控大屏突然一片血红。用户订单"成功"了&#xff0c;但库存没扣、支付没扣、物流没发...上百万的交易数据人间蒸发。排查结果让所有人傻眼&#xff1a;只是一个"无关紧要"的代码改动&#xff0c;让整个事件驱动系统安静地"死…...

JetBrains IDEs官宣 实验性 AI 功能:Recap 与 Insights 详解

前言 JetBrains IDEs 已经提供了丰富的 AI 功能&#xff0c;从代码自动补全到代码生成和解释。2026年3月&#xff0c;JetBrains 推出了两款主动式 AI 功能实验插件——Recap&#xff08;回顾&#xff09;和Insights&#xff08;洞察&#xff09;&#xff0c;为开发者带来全新的…...

【靶点筛选样本前处理①】细胞膜蛋白的全流程提取实操:标准化制备及验证

引言 在多组学与空间蛋白质组学研究中&#xff0c;依赖全细胞裂解液的蛋白分析范式已显现显著局限 —— 其不仅会稀释低丰度亚细胞定位蛋白&#xff0c;还会完全掩盖细胞内蛋白转位事件&#xff0c;高纯度的细胞亚组分提取&#xff0c;已成为Western Blot、免疫共沉淀&#xf…...

老码农和你一起学AI系列:语言模型采样方法

语言模型在生成文本时&#xff0c;每一步都会计算出下一个词的概率分布&#xff08;比如“吃”&#xff1a;0.4&#xff0c;“喝”&#xff1a;0.3&#xff0c;“玩”&#xff1a;0.2……&#xff09;。那么&#xff0c;具体选哪个词作为输出呢&#xff1f;这就涉及采样方法。根…...

CSDN一亿技术人员的千载难逢机遇:个人如何转型,平台如何进化

CSDN一亿技术人员的千载难逢机遇&#xff1a;个人如何转型&#xff0c;平台如何进化 2026年&#xff0c;中国技术圈正在经历一场前所未有的范式转移。 这不是一次技术迭代&#xff0c;不是一次框架升级&#xff0c;不是一次语言更替——而是一次权力结构的根本性重构。 当大…...

SRMAS工作室简介

小红书、抖音 搜‘科研连连看’ ‘srmas工作室’ SRMAS英文全称Smart Research Multi Agent System,是多智能体协作&#xff08;MAS&#xff09;驱动的专业生产力实验室.一 定位srmas工作室是一家专注于复杂逻辑自动化与多智能体协同的技术工作室。通过自研的可视化 Mul…...

经典2DMMORPG手游【石器时代H5内购版】服务端图文手工搭建教程

游戏截图搭建环境信息 系统&#xff1a;Centos 7.6 配置&#xff1a;2核4G内存 搭建资源获取 资源网站&#xff1a;www.woniuyxdj.cn 宝塔面板安装 通用自动安装命令 if [ -f /usr/bin/curl ];then curl -sSO https://download.bt.cn/install/install_panel.sh;else wget -O in…...

2026大专国际经济与贸易就业方向有哪些?

2026年企业对于国贸类岗位的描述越来越清晰。外贸业务、跟单、报关报检等传统岗位的需求持续存在&#xff0c;具备实务经验的求职者往往能获得更多的面试机会。与此同时&#xff0c;随着跨境电商和各类新业态的快速发展&#xff0c;涉及海外市场运营、数字营销等方向的新岗位也…...

越南《人工智能法》具体内容详解(附中越互译)

越南《人工智能法》(第134/2025/QH15号)于2025年12月10日由越南国会以90.70%的高赞成率表决通过,2026年3月1日正式生效,共8章35条,是东盟首部全面规范人工智能领域的专项法律。该法以“管理以促进发展”为核心方针,在风险管控与创新激励间寻求平衡,既借鉴国际惯例,又立…...

习题3.12 另类循环队列

习题3.12 另类循环队列分数 20作者 DS课程组单位 浙江大学如果用一个循环数组表示队列&#xff0c;并且只设队列头指针Front&#xff0c;不设尾指针Rear&#xff0c;而是另设Count记录队列中元素个数。请编写算法实现队列的入队和出队操作。函数接口定义&#xff1a;bool AddQ(…...

springboot+vue预报名管理系统--毕业论文

目录系统设计与技术选型需求分析与功能模块数据库设计后端实现前端实现系统安全与优化测试与部署总结与展望项目技术支持源码LW获取详细视频演示 &#xff1a;文章底部获取博主联系方式&#xff01;同行可合作系统设计与技术选型 阐述SpringBoot和Vue的技术优势&#xff0c;说…...

【AI】大语言模型 (LLM) 产品的开发流程参考

&#x1f525;小龙报&#xff1a;个人主页 &#x1f3ac;作者简介&#xff1a;C研发&#xff0c;嵌入式&#xff0c;机器人等方向学习者 ❄️个人专栏&#xff1a;《AI》 ✨ 永远相信美好的事情即将发生 文章目录前言一、个人开发者的大语言模型 (LLM) 产品的开发流程参考1.1 准…...

网络协议红蓝对抗:从TCP重传到QUIC的可靠性战争

网络协议红蓝对抗&#xff1a;从TCP重传到QUIC的可靠性战争原创深度技术长文 | 14,200字 | 含6大协议栈剖析、5个网络故障实验、4段可复现抓包分析 本文以高强度红蓝对抗形式&#xff0c;深入网络协议栈最核心战场——可靠性机制。从TCP的超时重传、快速恢复&#xff0c;到HTTP…...

文件系统红蓝对抗:从ext4到ZFS的数据持久性战争

文件系统红蓝对抗&#xff1a;从ext4到ZFS的数据持久性战争原创深度技术长文 | 13,800字 | 含7大文件系统对比、5个数据损坏实验、4段可复现代码 本文以高强度红蓝对抗形式&#xff0c;深入剖析ext4、XFS、Btrfs、ZFS、NTFS等主流文件系统在数据持久性、崩溃一致性、性能权衡上…...