施磊老师c++笔记(五)
继承与多态-深入掌握oop语言最强大的机制
文章目录
- 继承与多态-深入掌握oop语言最强大的机制
- 1.继承的基本意义
- 2.派生类的构造过程
- 3.重载,隐藏,覆盖
- 4.虚函数, 静态绑定和动态绑定--面试重点
- 5.虚析构函数--重点在于什么呢时候用
- 6.再讨论虚函数和动态绑定
- 7.理解多态到底是什么
- 8.理解抽象类----纯虚函数
- 9.笔试问题讲解
1.继承的基本意义
-
继承的本质和原理?
继承的本质: 1.做代码复用; 2.
类和类的关系:
组合: a part of … 一部分的关系
继承: a kind of … 一种的关系#include <iostream> using namespace std;class A { public:int ma; protected:int mb; private:int mc; }; // A 12字节//class B : public A //继承 A是基类/父类 B是派生类/子类 //{ //public: // void func() // { // cout << "ma:" << ma << endl; // } // int md; // int me; // int mf; //};//class B : protected A //继承 A是基类/父类 B是派生类/子类 //{ //public: // void func() // { // // } // int md; // int me; // int mf; //};class B : private A //继承 A是基类/父类 B是派生类/子类 { public:void func(){}int md;int me;int mf; };//B有两部分, A+B本身, 24字节class C : public B //继承 A是基类/父类 B是派生类/子类 { public:void func(){}int md;int me;int mf; };int main() {} -
继承的细节
继承方式 基类的访问限定 派生类的访问限定 (main)外部的访问限定 public public public ok protected protected no(main只能访问公有的) private 不可见,无法访问,不是私有 no protected public 降级, protected no protected protected no private 不可见,无法访问,不是私有 no private public private no protected private no private 不可见,无法访问,不是私有 no private只有自己和友元能访问!!
-
对于更多的继承, class C ,要看直接继承的B里面的各个访问限定和继承方式
-
总结:
外部只能访问对象public的成员, protected和private的成员无法直接访问
继承结构中, 派生类从基类可以继承过来private的成员, 但是派生类无法访问
protected和private的区别?----基类中定义的成员, 想被派生类访问, 但是不想被外部访问, 那么基类可以定义为protected; 若 派生类和外部都不访问, 基类中设置为private -
默认继承方式------要看派生类是class(private)还是struct(public)!!!
2.派生类的构造过程
- 派生类怎么初始化从基类继承的成员变量呢?
派生类可以从继承得到 继承的所有的成员(变量+方法), 除了构造和析构
解决办法: 调用基类的 构造函数
派生类的构造和析构, 负责初始化和清理派生类部分
派生类从基类继承来的成员–的初始化和清理由谁负责?----由基类的构造和析构 - 派生类对象构造和析构过程?
派生类调用基类构造(初始化基类继承来的成员)—>派生类自己构造—>派生类自己的析构—>调用基类的析构
#include <iostream>
using namespace std;class Base
{
public:Base(int data) :ma(data) { cout << "Base()" << endl; }~Base() { cout << "~Base()" << endl; }
protected:int ma;
};class Derive : public Base
{
public:/*Derive(int data) :ma(data), mb(data)*///改为Derive(int data) :Base(data), mb(data){cout << "Derive()" << endl;}~Derive(){cout << "~Derive()" << endl;}
private:int mb;
};int main()
{Derive d(20);return 0;
}/*输出:
Base()
Derive()
~Derive()
~Base()*/
3.重载,隐藏,覆盖
- 重载关系
一组函数要重载, 必须在一个作用域内;且函数名相同, 参数列表不同
基类和派生类是两个不同的作用域!! - 隐藏的关系
在继承结构中, 派生类的同名成员, 把基类的同名成员给 隐藏了, 也就是默认调用派生类的同名成员
想要基类, 就要 加基类作用域
#include <iostream>
using namespace std;class Base
{
public:Base(int data=10) :ma(data) { }~Base() { }void show() { cout << "Base:show" << endl; }void show(int) { cout << "Base:show(int)" << endl; }
protected:int ma;
};class Derive : public Base
{
public:Derive(int data=20) :Base(data), mb(data){}~Derive(){}void show() { cout << "Derive:show" << endl; }
private:int mb;
};int main()
{Derive d;d.show(); // 隐藏了 基类的同名成员, 没有才去基类 Derive:showd.Base::show(); //Base:show//d.show(10);//Derive 没有函数重载return 0;
}//输出
Derive:show
Base:show
-
把继承结构, 也理解为 从上(基类)到下(派生类)的结构
-
基类对象变为–>派生类对象, 派生类对象变为—>基类对象, 基类对象(引用)指向–>派生类对象, 派生类对象(引用)指向—>基类对象
这是不强转时, 直接=的情况, 实际强转还是可以的int main() {Base b(10);Derive d(20);//派生类对象--->基类对象 类型从下到上的转换 yes//怎么理解? 派生类看做学生, 基类看做人, 想要人, 把学生给你, 是可以的b = d; //相当于把派生类里继承的基类给了 基类//基类对象-->派生类对象 类型从上到下的转换 no//d = b; //这是不行的, 多出来了内存 派生类自己的 //基类对象(引用)-->指向 派生类对象 类型从下到上的转换 yesBase* pb = &d; // 是可以的, 解引用这能访问 Base那么大的区域, 后面的派生类自己的那部分, 管不着, 本来也管不着pb->show(); //yes 想要访问派生类show, 可以强转pb->show(20); //yes//派生类对象(引用)--->指向 基类对象 类型从上到下的转换 no//Derive* pd = &b; //会访问b没有的内存区域, 属于非法访问return 0; } -
总结: 继承结构中, 只支持从下到上的类型转换!!!-----前提是不强转
4.虚函数, 静态绑定和动态绑定–面试重点
-
什么时候是动态绑定?—并不是有虚函数就是动态绑定, 这是误区, 这个老师这节课开始没说明白!!到了第六节课才说
1.函数是虚函数:基类中的函数必须声明为 virtual。2.通过 指针或引用 调用:通过基类指针或基类引用调用虚函数。这是 必要条件!!!二者必须满足即 Derive d(20);Base *pb=&d; -
无虚函数时:
#include <iostream> using namespace std;class Base { public:Base(int data=10) :ma(data) { }~Base() { }void show() { cout << "Base:show" << endl; }void show(int) { cout << "Base:show(int)" << endl; } protected:int ma; };class Derive : public Base { public:Derive(int data=20) :Base(data), mb(data){}~Derive(){}void show() { cout << "Derive:show" << endl; } private:int mb; };int main() {Derive d(50);Base* pb = &d;pb->show(); // 静态(编译时期)的绑定(函数的调用) Base::show (07FF7C6ED1406h) pb->show(10); // 静态(编译时期)的绑定(函数的调用) Base::show (07FF7C6ED10DCh)cout << sizeof(Base) << endl; // 4cout << sizeof(Derive) << endl; // 8cout << typeid(pb).name() << endl; //class Base* __ptr64cout << typeid(*pb).name() << endl; // class Basereturn 0; } -
虚函数的总结-1:
如果类里面定义了虚函数, 编译阶段 就会给这个类类型产生一个 惟一的 vftable 虚函数表, 这里面主要存储的内容就是RTTI指针和虚函数的地址
RTTI–run-time type infomation
程序运行时, 每一张虚函数表 都会加载到内存的 .rodata区, 只能读, 不能写 -
虚函数的总结-2:
一个类里有虚函数, 这个类定义的对象, 在其运行时, 内存的开始部分, 多存储一个 vfptr虚函数指针, 指向相应类型的 虚函数表vftable. 一个类型定义的n个对象, vfptr都指向同一个虚函数表 -
虚函数的总结-3:
一个类里 虚函数的个数, 不影响对象的大小(对象里只是指针), 影响的是 虚函数表的大小 -
虚函数的总结-4:----覆盖!!!
如果派生类中的方法和基类继承来的某个方法, 函数名,参数列表,返回值都一样, 切基类这个方法是虚函数virtual,
则 派生类的 该方法 会 自动处理成虚函数—这是cpp标准
因此, 派生类的 虚函数表里, 该函数会覆盖基类的 -
有虚函数:
Base将不再是只有ma了, 还有虚函数指针vfptr, 指向虚函数表
因此不再是4字节, 而是8字节 -
静态绑定和动态绑定
静态(编译时期)的绑定(函数的调用)
动态(运行时期)的绑定(函数的调用) -
从汇编看 静动态绑定
//静态绑定的汇编 mov rcx,qword ptr [pb] //this指针存储 call Base::show (07FF7C6ED1406h)//动态绑定: x64的反汇编mov rax,dword ptr [pb] //把pb里面的地址给eax, 即虚函数表地址mov rax,dword ptr [rax] //取存储虚函数表地址里的地址,即虚函数表的地址mov rcx,qword ptr [pb] //this指针存储 call qword ptr [rax+8] //调用虚函数表里偏移8字节的地址里面的地址, 即 show的地址, 8字节是RTTI的信息 -
总体代码:
该代码在show 是不是虚函数时, 输出不一样的-----x64//是虚函数-------注意考虑内存对齐 Derive:show Base:show(int) 16 24 class Base * __ptr64 class Derive//不是虚函数 Base:show Base:show(int) 4 8 class Base * __ptr64 class Base#include <iostream> using namespace std;class Base { public:Base(int data=10) :ma(data) { }~Base() { }virtual void show() { cout << "Base:show" << endl; }virtual void show(int) { cout << "Base:show(int)" << endl; } protected:int ma; };class Derive : public Base { public:Derive(int data=20) :Base(data), mb(data){}~Derive(){}void show() { cout << "Derive:show" << endl; } private:int mb; };int main() {//有虚函数时Derive d(50);Base* pb = &d;pb->show(); // 静态(编译时期)的绑定(函数的调用) //静态绑定的汇编 call Base::show (07FF7C6ED1406h) // pb 是Base指针, 如果, Base::show 是普通函数, 则进行 静态绑定// pb 是Base指针, 如果, Base::show 是虚函数, 就进行动态绑定/*动态绑定: x64的反汇编mov rax,dword ptr [pb] 把pb里面的地址给eax, 即虚函数表地址mov rax,dword ptr [rax] 取存储虚函数表地址里的地址,即虚函数表的地址mov rcx,qword ptr [pb] this指针存储call qword ptr [rax+8] 调用虚函数表里偏移8字节的地址里面的地址, 即 show的地址, 这个+8, 不是RTTI的, 一般RTTI是反偏移的, 是因为覆盖是原来的不要了, 新的放在最下面*/pb->show(10); // 静态(编译时期)的绑定(函数的调用) Base::show (07FF7C6ED10DCh)cout << sizeof(Base) << endl; cout << sizeof(Derive) << endl; cout << typeid(pb).name() << endl; //class Base* __ptr64cout << typeid(*pb).name() << endl; // class Basereturn 0; } -
命令行展示 虚函数结构
vs命令行工具—>进入文件目录—>输入
cl 文件.cpp /d1reportSingleClassLayoutDerive -
使用命令行也解释了, call qword ptr [rax+8]
Derive::$vftable@:| &Derive_meta| 00 | &Base::show // 重载在这里, 看不出来, 实际这个是 int1 | &Derive::show // 覆盖是 之前最上面的不要了, 新的放到最下面
5.虚析构函数–重点在于什么呢时候用
-
哪些函数不能实现成 虚函数?–接上一节
虚函数能产生函数地址
虚函数表位置在 vfptr里, vfptr在内存里----- 对象必须存在, 依赖对象
构造函数–不能是–虚函数, 构造函数中调用虚函数, 不会发生动态绑定, 构造函数中调用的任何函数,都是静态绑定---->这是因为在构造函数执行期间,对象的动态类型尚未完全确定,虚函数表(vtable)也没有完全初始化。
派生类构造过程–>先调用基类构造–>才调用–>派生类构造
static静态成员方法–>不能是—>虚函数, 因为不依赖对象
析构函数–>可以!!–>虚函数, 因为析构时,对象是存在的 -
特别注意:基类析构和派生类虚构!!
基类虚构是虚函数-----> 派生类析构 自动成为虚函数—> 尽管名字不一样!! -
虚析构使用实例:
为什么 不是虚析构时, 会出问题? 因为使用了
因为此时是静态绑定, Base类的指针即使指向Derive类, 使用的还是Base的析构派生类使用虚析构, 就有了虚函数表, 派生类的虚函数表还会覆盖 基类 的 虚析构, 使用自己的虚析构, 使得可以 正确析构派生类, —>动态绑定
pb是Base类, 但是指向Derive, 动态绑定, 使得本来是使用Base的析构, 但是发现是虚析构, 于是去找虚函数表, 此时虚函数表的析构已经被 Derive覆盖了, 因此使用了 Derive的析构, 后续继承基类的部分也会正确析构#include <iostream> using namespace std;class Base { public:Base(int data=10) :ma(data) { cout << "Base" << endl; }virtual ~Base() { cout << "~Base" << endl; }void show() { cout << "call Base:show" << endl; }protected:int ma; };class Derive : public Base { public:Derive(int data=20) :Base(data), mb(data){cout << "Derive" << endl;}~Derive(){cout << "~Derive" << endl;}void show() { } private:int mb; };int main() {Base* pb = new Derive(10);pb->show(); // 静态绑定,pb是Base*类型, 而*pb类型取决于show是不是虚函数, 若是, 则是Derive,因为指向了派生类的show, 且是动态绑定delete pb; // 不使用虚析构, 会导致派生类析构没有调用-->若有指针.容易内存泄漏/* Derive d(50);Base* pb = &d;pb->show(); */return 0; } -
什么时候用?—特别重点
基类的指针(引用)指向 堆上new出来的 派生类, delete 基类指针时
6.再讨论虚函数和动态绑定
-
虚函数的调用就是 动态绑定?
不是!!!
构造函数就是例外, 构造函数中调用虚函数, 不会发生动态绑定, 构造函数中调用的任何函数,都是静态绑定 -
什么时候发生动态绑定?
1.函数是虚函数:基类中的函数必须声明为 virtual。2.通过 指针或引用 调用:通过基类指针或基类引用调用虚函数。这是 必要条件!!!二者必须满足 -
实例:---- 一定要搞清楚什么时候动态绑定!!!
#include <iostream> using namespace std;class Base { public:Base(int data=10) :ma(data) { cout << "Base" << endl; }virtual ~Base() { cout << "~Base" << endl; }virtual void show() { cout << "call Base:show" << endl; }protected:int ma; };class Derive : public Base { public:Derive(int data=20) :Base(data), mb(data){cout << "Derive" << endl;}~Derive(){cout << "~Derive" << endl;}void show() { } private:int mb; };int main() {Base b;Derive d;b.show(); // 二者都是静态绑定,d.show();Base* pb1 = &b; //二者是动态绑定pb1->show();Base* pb2 = &d;pb2->show();Base& rb1 = b;//二者是动态绑定pb1->show();Base& rb2 = d;pb2->show();Derive* pd2 = (Derive*)&b;//动态绑定---满足两个条件-----这里必须强转, 回看第三节的总结pd2->show();//---这里是调用的基类的show, 因为实际的b是Base, 其虚函数表是基类的, 这种强转并不会 改变原本的虚函数表指向return 0; }
7.理解多态到底是什么
-
如何理解多态?
静态的 多态: — 编译阶段就确定好 — 函数重载和模板(函数模板,类模板),动态的 多态: — 继承结构中, 基类指针(引用) 指向派生类对象, 通过该指针(引用)调用同名覆盖方法(虚函数),基类指针指向哪个派生类, 就调用哪个派生类对象的同名覆盖方法, 称为多态
多态底层是通过 动态绑定来实现的的 -
实例: 虚函数配合基类, 完成 开-闭 设计, 使用指针时切记虚析构
#include <iostream> using namespace std;// 动物的基类 class Animal { public:Animal(string name) : _name(name) {}virtual void bark() {} protected:string _name; };class Cat : public Animal { public:Cat(string name) : Animal(name) {}void bark() { cout << _name << " bark: miao miao!" << endl; } };class Dog : public Animal { public:Dog(string name) : Animal(name) {}void bark() { cout << _name << " bark: wang wang!" << endl; } };class Pig : public Animal { public:Pig(string name) : Animal(name) {}void bark() { cout << _name << " bark: heng heng!" << endl; } };//下面的API无法达到 软件设计的 开-闭 原则: 对修改关闭, 对扩展开放 //void bark(Dog &dog) //{ // dog.bark(); //} // //void bark(Pig& pig) //{ // pig.bark(); //} // //void bark(Cat& cat) //{ // cat.bark(); //}//使用基类指针 void bark(Animal *p) {p->bark(); // 虚函数 覆盖 }int main() {Dog dog("dog");Pig pig("pig");Cat cat("cat");bark(&dog);bark(&cat);bark(&pig);return 0; } -
继承的好处?
1.做代码复用
2.在基类中给所有的派生类提供统一的虚函数接口, 让派生类重写, 然后就可以多态了
8.理解抽象类----纯虚函数
-
类 是 抽象一个实体的类型
-
什么是纯虚函数?
纯虚函数(Pure Virtual Function)是 C++ 中用于定义抽象类的一种机制。纯虚函数在基类中声明但不提供实现,要求派生类必须重写(实现)该函数。包含纯虚函数的类称为抽象类,抽象类不能被实例化。在函数声明的末尾加上
= 0,表示这是一个纯虚函数
virtual void foo() = 0; -
什么是 抽象类?
拥有纯虚函数的类, 叫做抽象类
抽象类不能再实例化对象, 但是可以定义指针和引用变量
一般是基类作为抽象类, 派生类去实例化对象 -
实例: — 注意 外部接口是 基类
#include <iostream> using namespace std;// 动物的基类 class Animal { public:Animal(string name) : _name(name) {}virtual void bark() {} protected:string _name; };class Cat : public Animal { public:Cat(string name) : Animal(name) {}void bark() { cout << _name << " bark: miao miao!" << endl; } };class Dog : public Animal { public:Dog(string name) : Animal(name) {}void bark() { cout << _name << " bark: wang wang!" << endl; } };class Pig : public Animal { public:Pig(string name) : Animal(name) {}void bark() { cout << _name << " bark: heng heng!" << endl; } };//下面的API无法达到 软件设计的 开-闭 原则: 对修改关闭, 对扩展开放 //void bark(Dog &dog) //{ // dog.bark(); //} // //void bark(Pig& pig) //{ // pig.bark(); //} // //void bark(Cat& cat) //{ // cat.bark(); //}//使用基类指针 void bark(Animal *p) {p->bark(); // 虚函数 覆盖 }int main() {Dog dog("dog");Pig pig("pig");Cat cat("cat");bark(&dog);bark(&cat);bark(&pig);return 0; } -
实例-2:
#include <iostream> #include <string> using namespace std;// 抽象基类 Car class Car { public:Car(string name) : _name(name) {}virtual double getMilesPerGallon() = 0; // 纯虚函数string getName() { return _name; }double getLeftMiles(double fuel) {return fuel * getMilesPerGallon();} protected:string _name; };// 派生类 Audi class Audi : public Car { public:Audi(string name) : Car(name) {}double getMilesPerGallon() override {return 18.0;} };// 派生类 BMW class BMW : public Car { public:BMW(string name) : Car(name) {}double getMilesPerGallon() override { //override 是 C++11 引入的一个关键字,用于显式地标记派生类中的函数是对基类虚函数的重写。它的主要作用是提高代码的可读性和安全性,帮助开发者避免一些常见的错误。return 19.0;} };// 给外部提供一个统一的获取汽车剩余路程数的API void showCarLeftMiles(Car &car, double fuel) {cout << car.getName() << " left miles: " << car.getLeftMiles(fuel) << " 公里" << endl; }int main() {Audi a("奥迪");BMW b("宝马");showCarLeftMiles(a, 10.0); // 假设有10加仑的油showCarLeftMiles(b, 10.0);return 0; }
9.笔试问题讲解
-
实例-1
重点: 函数调用, 参数压栈是在编译时期 就确定的
因此, 派生类虚函数参数默认值 是用不到的
默认参数值是静态绑定的,而虚函数的调用是动态绑定的。即使 基类无,派生类有,也没用#include <iostream> using namespace std;class Base { public:virtual void show(int i = 10) {cout << "call Base::show i:" << i << endl;}virtual ~Base() {} // 虚析构函数 };class Derive : public Base { public:void show(int i = 20) override {cout << "call Derive::show---" << i << endl;} };int main() {Base* p = new Derive(); // 使用基类指针指向派生类对象p->show(); // 动态绑定,调用Derive::show, 但是输出却是 10, 不是20delete p; // 释放内存return 0; }//为什么会是10 呢 从函数调用角度讲, 先压参数列表, 才压函数符号 而在编译阶段, 看不到动态绑定, 只能看到是Base*类, 因此压入的是10 push 0Ah 编译时 mov eax, dword ptr[p] 运行时 mov ecx, deord ptr[eax] call eaxpush的是死的, 不会因为后面调虚函数而改变 -
实例-2
重点:成员的权限, 是在编译阶段 确定好的!!!
编译阶段只能看见 p是Base的, 而基类里是 public的
千万不要去 运行时看 成员权限!!#include <iostream> using namespace std;class Base { public:virtual void show() {cout << "call Base::show i:" << endl;}virtual ~Base() {} // 虚析构函数 };class Derive : public Base { private:void show() override {cout << "call Derive::show---" << endl;} };int main() {Base* p = new Derive(); p->show(); // 运行时确定delete p; return 0; } -
实例-3
重点: vfptr什么时候拿到虚函数表地址? 是重点!!!
每个类(无论是基类还是派生类)都有自己的虚函数表(vtable)。
每个对象(无论是基类对象还是派生类对象)都有自己的虚函数表指针(vfptr),指向其所属类的虚函数表。#include <iostream> using namespace std;class Base { public:Base() {/*push ebpmov ebp, espsub esp, 4Chrep stos esp<->ebp 0xCCCCCCCC (windows VS GCC/G++)此时, 就会进行 vfptr->vftable地址进入函数后, 第一件事就是虚函数表指针的存储*/cout << "call Base()" << endl;clear();}void clear(){memset(this, 0, sizeof(*this));}virtual void show() {cout << "call Base::show()" << endl;}virtual ~Base() {cout << "call ~Base()" << endl;} };class Derive : public Base { public:Derive() {cout << "call Derive()" << endl;}void show() override {cout << "call Derive::show()" << endl;}~Derive() {cout << "call ~Derive()" << endl;} };int main() { //Base* pbl = new Base(); //pbl->show(); // 动态绑定//delete pbl; //这一段肯定会出错, vfptr是0了, 肯定访问不到了Base* pb2 = new Derive(); pb2->show(); // 动态绑定,delete pb2; // 这一段是可以的, 先基类构造, 再派生类构造//涉及到了 vfptr什么时候得到的vftable的 地址, 在构造函数里//将会把派生类虚函数地址写入vfptr, return 0; }
相关文章:
施磊老师c++笔记(五)
继承与多态-深入掌握oop语言最强大的机制 文章目录 继承与多态-深入掌握oop语言最强大的机制1.继承的基本意义2.派生类的构造过程3.重载,隐藏,覆盖4.虚函数, 静态绑定和动态绑定--面试重点5.虚析构函数--重点在于什么呢时候用6.再讨论虚函数和动态绑定7.理解多态到底是什么8.理…...
µCOS-III从入门到精通 第十四章(软件定时器)
参考教程:【正点原子】手把手教你学UCOS-III实时操作系统_哔哩哔哩_bilibili 一、软件定时器简介 1、定时器的概念与种类 (1)定时器的概念:从指定的时刻开始,经过一个指定时间,然后触发一个超时事件&…...
MySQL数据库复杂的增删改查操作
在前面的文章中,我们主要学习了数据库的基础知识以及基本的增删改查的操作。接下去将以一个比较实际的公司数据库为例子,进行讲解一些较为复杂且现时需求的例子。 基础知识: 一文清晰梳理Mysql 数据库基础知识_字段变动如何梳理清楚-CSDN博…...
KCD 北京站丨Volcano 邀您畅聊云原生智能调度技术与应用
AI与云原生技术正以前所未有的速度改变着我们的世界,而云原生技术则如同一座坚实的桥梁,连接着传统IT与现代化的数字世界。当AI与云原生相遇,它们相互赋能,相互促进,为开发者们打开了一个全新的技术宇宙。 3 月 15 日&…...
BLEU评估指标
一、介绍 用于评估模型生成的句子和实际句子差异的指标,取值在[0,1],匹配度高就距离1近,反之距离0近。这个指标计算代价小,容易理解,与语言无关,与人类评价结果高度相关。 BLEU主要基于n-gram匹配&#x…...
高效自动化测试:打造Python+Requests+Pytest+Allure+YAML的接口测试框架
一、背景 在快节奏的开发周期中,如何确保接口质量?自动化测试是关键。通过构建标准化、可复用的测试框架,能显著提升测试效率与准确性,为项目质量保驾护航[1][7]。 二、目标 ✅ 核心目标: ● 实现快速、高效的接口测试…...
如何修复 Tauri 发布后程序运行时显示 `asset not found: index.html` 的问题
如何修复 Tauri 发布后程序运行时显示 asset not found: index.html 的问题 在使用 Tauri 发布应用程序时,如果运行时出现 asset not found: index.html 的错误,通常是因为 Tauri 无法找到或正确加载前端资源文件(如 index.html)…...
BSides Vancouver: 2018 (Workshop)
BSides Vancouver: 2018 (Workshop) 来自 <https://www.vulnhub.com/entry/bsides-vancouver-2018-workshop,231/> 1,将两台虚拟机网络连接都改为NAT模式 2,攻击机上做namp局域网扫描发现靶机 nmap -sn 192.168.23.0/24 那么攻击机IP为192.168.23…...
rStar论文精读
论文简介 论文标题:《Mutual reasoning makes smaller LLMs stronger problem-solvers》 论文地址:https://arxiv.org/abs/2408.06195 录用会议:ICLR2025 背景与挑战 挑战1:在SLM中平衡exploration与exploitation。一些方法有很…...
【动态规划】对局匹配 (分组线性DP)
题目详情 问题描述: 小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。 小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是K的两名用户匹配在一起。如果两人分差小于或大于K…...
python 提取视频中的音频
在Python中提取视频中的音频,你可以使用moviepy库,这是一个非常强大且易于使用的库,专门用于视频编辑。以下是如何使用moviepy来提取视频中的音频的步骤: 安装moviepy 首先,你需要安装moviepy。你可以通过pip安装它&a…...
self.cls_token在 Vision Transformer (ViT) 模型中的训练阶段和推理阶段的行为和作用的异同
self.cls_token 在 Vision Transformer (ViT) 模型中,在训练阶段和推理阶段的行为和作用是不同的,而且它的值在训练过程中会发生变化。 1. self.cls_token 的作用 在 ViT 中,self.cls_token 是一个特殊的、可学习的嵌入向量(emb…...
【量化科普】Leverage,杠杆
【量化科普】Leverage,杠杆 🚀量化软件开通 🚀量化实战教程 在量化投资领域,杠杆(Leverage)是一个核心概念,它允许投资者通过借入资金来增加投资规模,从而放大投资收益或亏损。简…...
247g 的工业级电调,如何让无人机飞得更 “聪明“?——STONE 200A-M 深度测评
一、轻量化设计背后的技术取舍 当拿到 STONE 200A-M 时,247g 的重量让人意外 —— 这个接近传统 200A 电调 70% 的重量,源自 1205624.5mm 的紧凑结构(0.1mm 公差控制)。实测装机显示,相比同规格产品,其体积…...
Maven Deploy Plugin如何使用?
在Java开发中,Maven是一个非常重要的构建工具。它不仅可以管理项目的依赖关系,还能帮助我们打包和发布项目。在Maven中,deploy插件是一个很实用的功能,它可以将构建好的项目发布到远程仓库。今天,就来聊聊如何使用Mave…...
Node.js:快速启动你的第一个Web服务器
Node.js 全面入门指南 文章目录 Node.js 全面入门指南一 安装Node.js1. Windows2. MacOS/Linux 二 配置开发环境1. VSCode集成 三 第一个Node.js程序1. 创建你的第一个Node.js程序 四 使用Express框架1. 快速搭建服务器 一 安装Node.js 1. Windows 以下是Windows环境下Node.j…...
自定义日志回调函数实现第三方库日志集成:从理论到实战
一、应用场景与痛点分析 在开发过程中,我们经常会遇到以下场景: 日志格式统一:第三方库使用自己的日志格式,导致系统日志混杂,难以统一管理和分析。日志分级过滤:需要动态调整第三方库的日志输出级别&…...
Linux练级宝典->任务管理和守护进程
任务管理 进程组概念 每个进程除了进程ID以外,还有一个进程组,进程组就是一个或多个进程的集合 同一个进程组,代表着他们是共同作业的,可以接收同一个终端的各种信号,进程组也有其唯一的进程组号。还有一个组长进程&a…...
C语言:计算并输出三个整数的最大值 并对三个数排序
这是《C语言程序设计》73页的思考题。下面分享自己的思路和代码 思路: 代码: #include <stdio.h> int main() {int a,b,c,max,min,mid ; //设置大中小的数分别为max,mid,min,abc为输入的三个数printf("ple…...
工具(十二):Java导出MySQL数据库表结构信息到excel
一、背景 遇到需求:将指定数据库表设计,统一导出到一个Excel中,存档查看。 如果一个一个弄,很复杂,耗时长。 二、写一个工具导出下 废话少絮,上码: 2.1 pom导入 <dependency><grou…...
如何设计微服务及其设计原则?
微服务架构是一种将大型单体应用拆分成多个小型、自治服务的设计方式,每个服务专注于单一的业务功能。设计微服务时,需要遵循以下原则和最佳实践: 1. 单一职责原则 核心思想: 每个微服务都应该只负责一块独立的业务功能。这使得…...
ACL初级总结
ACL–访问控制列表 1.访问控制 在路由器流量流入或者流出的接口上,匹配流量,然后执行相应动作 permit允许 deny拒绝 2.抓取感兴趣流 3.ACL匹配规则 自上而下逐一匹配,若匹配到了则按照对应规则执行动作,而不再向下继续匹配 思科:ACL列表末尾隐含一条拒绝所有的规则 华为:AC…...
调优案例一:堆空间扩容提升吞吐量实战记录
📝 调优案例一:堆空间扩容提升吞吐量实战记录 🔧 调优策略:堆空间扩容三部曲 # 原配置(30MB堆空间) export CATALINA_OPTS"$CATALINA_OPTS -Xms30m -Xmx30m"# 新配置(扩容至120MB&am…...
C语言 —— 此去经年梦浪荡魂音 - 深入理解指针(卷一)
目录 1. 内存和地址 2. 指针变量和地址 2.1 取地址操作符(&) 2.2 指针变量 2.3 解引用操作符 (*) 3. 指针的解引用 3.1 指针 - 整数 3.2 void* 指针 4. const修饰指针 4.1 const修饰变量 4.2 const修饰指针变量 5…...
计算机毕业设计:留守儿童的可视化界面
留守儿童的可视化界面mysql数据库创建语句留守儿童的可视化界面oracle数据库创建语句留守儿童的可视化界面sqlserver数据库创建语句留守儿童的可视化界面springspringMVChibernate框架对象(javaBean,pojo)设计留守儿童的可视化界面springspringMVCmybatis框架对象(javaBean,poj…...
golang算法二叉树对称平衡右视图
100. 相同的树 给你两棵二叉树的根节点 p 和 q ,编写一个函数来检验这两棵树是否相同。 如果两个树在结构上相同,并且节点具有相同的值,则认为它们是相同的。 示例 1: 输入:p [1,2,3], q [1,2,3] 输出:…...
c++20 Concepts的简写形式与requires 从句形式
c20 Concepts的简写形式与requires 从句形式 原始写法(简写形式)等效写法(requires 从句形式)关键区别说明:组合多个约束的示例:两种形式的编译结果:更复杂的约束示例:标准库风格的约…...
Chatbox通过百炼调用DeepSeek
解决方案链接:评测|零门槛,即刻拥有DeepSeek-R1满血版 方案概览 本方案以 DeepSeek-R1 满血版为例进行演示,通过百炼模型服务进行 DeepSeek 开源模型调用,可以根据实际需求选择其他参数规模的 DeepSeek 模型。百炼平台…...
【数据结构】6栈
0 章节 3.1到3.3小节。 认知与理解栈结构; 列举栈的操作特点。 理解并列举栈的应用案例。 重点 栈的特点与实现; 难点 栈的灵活实现与应用 作业或思考题 完成学习测试2,? 内容达成以下标准(考核…...
PyTorch 入门学习
目录 PyTorch 定义 核心作用 应用场景 Pytorch 基本语法 1. 张量的创建 2. 张量的类型转换 3. 张量数值计算 4. 张量运算函数 5. 张量索引操作 6. 张量形状操作 7. 张量拼接操作 8. 自动微分模块 9. 案例-线性回归案例 PyTorch 定义 PyTorch 是一个基于 Python 深…...
