C++ 之多态虚函数原理及应用
文章目录
-
- 多态基本概念和原理
- 虚函数的基本原理和概念
- 虚析构和纯虚析构
- 多重继承中的虚函数
- 小结
多态基本概念和原理
多态的基本概念
**多态是C++面向对象三大特性之一**
多态的定义
多态是一种面向对象编程概念,指同一个行为(方法)在不同的对象上有不同的实现方式。其定义可以简单地理解为“同名多形”,即同一个方法名可以被不同的类实现,并且表现出不同的行为。 多态使得程序具有更高的灵活性和可扩展性,因为它使得代码与具体对象的实现细节分离开来,从而可以方便地添加新的类或修改旧的类的实现方式,同时也使得代码更易于维护。
虚函数和多态的关系
虚函数是实现多态的关键。在基类中声明一个虚函数,当子类重写该函数时,调用该函数将根据对象类型动态确定。这样做可以实现运行时多态,即在程序运行期间根据对象类型动态确定哪个函数应该被调用。这使得代码更加灵活和可扩展,并且减少了大量的条件判断语句。因此,虚函数和多态密不可分。
多态分为两类
- 静态多态: 函数重载 和 运算符重载属于静态多态,复用函数名
- 动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:
- 静态多态的函数地址早绑定 - 编译阶段确定函数地址
- 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
动态多态满足条件
1、有继承关系
2、子类重写父类的虚函数动态多态的使用
父类的指针或者引用指向子类的对象重写:函数返回值类型 函数名 参数列表 完全一致称为重写
#include <iostream> using namespace std;class Animal{public://speak函数就是虚函数//函数前面加上virtual关键字,变成虚函数,那么编译器在编译的时候就不能确定函数调用了。virtual void speak() { //类Animal的成员函数speak前不加virtual,其size是1,加virtual其size是4,增加一个虚函数指针cout << "动物在说话" << endl;}};class Cat :public Animal {public://子类重写父类的虚函数,void前加不加virtual均可//重写:函数返回值类型 函数名 参数列表 完全一致称为重写void speak() {cout << "小猫在说话" << endl;}};//执行说话的函数//地址早绑定,在编译阶段确定函数地址,父类的speak函数前不加virtual,则即使DoSpeak传入参数cat,则也是调用的父类的speak函数//如果想让小猫说话,那么这个函数地址就不能早绑定,需要在运行阶段进行绑定,地址晚绑定//我们希望传入什么对象,那么就调用什么对象的函数//如果函数地址在编译阶段就能确定,那么静态联编//如果函数地址在运行阶段才能确定,就是动态联编//动态多态满足条件//1、有继承关系//2、子类重写父类的虚函数//动态多态的使用//父类的指针或者引用指向子类的对象void DoSpeak(Animal &animal) //父类的指针或者引用指向子类的对象 Animal &animal = cat;{animal.speak();}void test() {Cat cat;DoSpeak(cat);} int main(){test();return 0; }
多态的基本原理
实现原理:虚函数表+虚表指针
编译器处理虚函数的方法是:为每个类对象添加一个隐藏成员,隐藏成员中保存了一个指向函数地址数组的指针,称为虚表指针(vptr),这种数组成为虚函数表(virtual function table, vtbl),即,每个类使用一个虚函数表,每个类对象用一个虚表指针。
举个例子:基类对象包含一个虚表指针,指向基类中所有虚函数的地址表。派生类对象也将包含一个虚表指针,指向派生类虚函数表。看下面两种情况:
如果派生类重写了基类的虚方法,该派生类虚函数表将保存重写的虚函数的地址,而不是基类的虚函数地址。
如果基类中的虚方法没有在派生类中重写,那么派生类将继承基类中的虚方法,而且派生类中虚函数表将保存基类中未被重写的虚函数的地址。注意,如果派生类中定义了新的虚方法,则该虚函数的地址也将被添加到派生类虚函数表中。
调用虚函数时,程序将查看存储在对象中的虚函数表地址,转向相应的虚函数表,使用类声明中定义的第几个虚函数,程序就使用数组的第几个函数地址,并执行该函数。
//当父类的指针或者引用指向子类的对象时,发生多态 Animal &animal = cat; //由于此处指向的是cat对象,所以它会从cat对象的虚函数表中去找speak()函数,在运行阶段发生了动态多态,你传的是cat对象,它就从cat对象的虚函数表中地址找该函数,最后实现调用cat的speak()函数 animal.speak();类Animal的成员函数speak前不加virtual,其size是1,加virtual其size是4,增加一个虚函数指针
当子类没有重写父类的虚函数时
子类重写父类的虚函数时,子类的虚函数指针指向的地址,将子类虚函数地址将父类的函数地址覆盖了,动态多态实现地址的晚绑定。
多态案例-计算器类
//利用多态实现计算机类//多态的好处 //1、组织结构清晰 //2、可读性强 //3、对于前期和后期的扩展和维护性高#include <iostream> #include <string> using namespace std;class Abcal { public:virtual int getResult() { return 0; }int num1;int num2; };//设计加法计算器类 class Addcal : public Abcal { public:int getResult() { return num1 + num2; } };//减法的计算器类 class Subcal : public Abcal { public:int getResult() { return num1 - num2; } };//乘法的计算器类 class Mulcal : public Abcal { public:int getResult() { return num1 * num2; } };void test() {//多态的使用//父类的指针或者引用指向子类的对象//加法计算Abcal *a = new Addcal; // new Addcal 创建加法计算器的对象 //堆区a->num1 = 10;a->num2 = 10;int c = a->getResult();cout << "c = " << c << endl;//堆区手动开辟数据需要手动释放delete a; //只释放堆区的数据,指针的类型没有变还是父类的指针//减法计算a = new Subcal;a->num1 = 10;a->num2 = 10;int d = a->getResult();cout << "d = " << d << endl;delete a; } int main() {test();return 0; }
虚函数的基本原理和概念
一、什么是虚函数?
在某基类中声明为 virtual 并在一个或多个派生类中被重新定义的成员函数用法格式为:virtual 函数返回类型 函数名(参数表) {函数体};实现多态性,通过指向派生类的基类指针或引用,访问派生类中同名覆盖成员函数。
二、虚函数定义
简单地说,那些被virtual关键字修饰的成员函数,就是虚函数。虚函数的作用,用专业术语来解释就是实现多态性(Polymorphism),多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异,而采用不同的策略。下面来看一段简单的代码。三、虚函数的作用
C++中的虚函数的作用主要是实现了多态的机制。关于多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数(动态绑定)。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。所谓泛型技术,说白了就是试图使用不变的代码来实现可变的算法。比如:模板技术,RTTI技术,虚函数技术,要么是试图做到在编译时决议,要么试图做到运行时决议。
虚函数可以让子类对象在运行时动态地继承和修改父类的成员函数,使得代码更加灵活、可重用,并且可以实现多态性和抽象类等高级特性。
- 通过虚函数,可以实现多态性(Polymorphism),即同一个函数名可以在不同的子类中表现出不同的行为,这样可以提高代码的可重用性和灵活性。
- 避免静态绑定,在使用父类指针或引用调用子类对象的成员函数时,如果没有使用虚函数,则会进行静态绑定(Static Binding),即只能调用父类的成员函数,无法调用子类特有的成员函数。
- 虚函数的调用是动态绑定(Dynamic Binding)的,即在运行时根据指针或引用所指向的对象类型来选择调用哪个函数,从而实现动态多态性。
- 抽象类是一种不能直接实例化的类,只能被其他类继承并实现其虚函数。通过定义纯虚函数(Pure Virtual Function),可以使得一个类成为抽象类,强制其子类必须实现该函数。
四、虚函数的底层实现原理
实现原理:虚函数表+虚表指针
- 虚函数表:每个包含虚函数的类都会生成一个虚函数表(Virtual Table),其中存储着该类中所有虚函数的地址。虚函数表是一个由指针构成的数组,每个指针指向一个虚函数的实现代码。
- 虚函数指针:在对象的内存布局中,编译器会添加一个额外的指针,称为虚函数指针或虚表指针(Virtual Table Pointer,简称 VTable 指针)。这个指针指向该对象对应的虚函数表,从而让程序能够动态地调用正确的虚函数。
当一个基类指针或引用调用虚函数时,编译器会使用虚表指针来查找该对象对应的虚函数表,并根据函数在虚函数表中的位置来调用正确的虚函数。
1、虚函数表的概念与作用
虚函数表是一种用于实现多态的机制,在 C++ 中,当一个类中包含虚函数时,编译器会自动为该类生成一个虚函数表。这个表由包含虚函数的类的指针所指向,其中每个条目都存储着对应虚函数的地址。当使用一个对象调用其虚函数时,编译器通过该对象的虚函数表找到对应的虚函数地址,并进行调用。
虚函数表的作用在于实现了多态,即通过基类指针或引用调用派生类的虚函数,实现了不同对象的不同行为。因此,虚函数表使得程序可以更加灵活地处理不同类型的对象,并支持代码的扩展和修改。同时,由于虚函数表的存在,C++ 中的虚函数调用比起其他语言(如 Java)中的成本要高,因为需要额外的内存空间存储虚函数表及其指针。
2、虚函数指针的意义
虚函数指针的意义在于实现多态。多态是指同一种操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。通过使用虚函数和虚函数指针,可以在运行时确定调用哪个子类中的虚函数,从而实现了动态绑定,使得程序具有更好的灵活性和扩展性。
此外,虚函数指针也是实现多级继承的关键,在多级继承中,每个子类都需要维护自己的虚函数表及其对应的虚函数指针。
3、虚函数的调用过程
- 在编译期间,编译器会根据函数调用的类型和对象的类型确定要调用的函数。
- 在运行期间,程序会根据对象的实际类型来决定调用哪个函数。这个过程叫做动态绑定或者后期绑定。
- 程序通过虚函数表(vtable)来实现动态绑定。每个含有虚函数的类都有自己的虚函数表,存储了指向实际函数地址的指针。在对象被创建时,它的指针会指向所属类的虚函数表。
- 当调用虚函数时,在对象中存储的指针会被解引用,获取到虚函数表的地址。然后根据函数调用的类型,从虚函数表中获取相应的函数地址。
- 最后,程序跳转到函数地址处执行实际的代码。由于是动态绑定,所以调用的函数是根据对象实际类型来决定的。
4、虚函数的重写与覆盖
在 C++ 中,派生类可以重写 (override) 它继承的虚函数,这被称为函数的覆盖 (overriding)。当然,子类也可以选择不重写基类的虚函数,那么它将默认继承基类的实现,这就是虚函数的重载 (overloading)。
虚函数的重写了解起来比较简单,子类定义与父类相同名称和参数列表的虚函数,此时会自动调用子类中定义的方法,而不会调用父类的同名虚函数。例如:
class Base { public:virtual void foo() {std::cout << "Base::foo()" << std::endl;} };class Derived : public Base { public:void foo() {std::cout << "Derived::foo()" << std::endl;} };int main() {Derived obj;Base* ptr = &obj;ptr->foo(); // 输出:Derived::foo()return 0; }例子中,派生类
Derived重写了基类Base的虚函数foo(),所以在调用指针ptr的foo()时,实际上调用的是Derived类中定义的函数,输出结果为"Derived::foo()"。需要注意的是,在子类中重写虚函数时,其访问权限不能更严格(即不能由 public 变为 private 或 protected),否则编译器会报错。
五、使用虚函数后的变化
(1) 对象将增加一个存储地址的空间(32位系统为4字节,64位为8字节)。
(2) 每个类编译器都创建一个虚函数地址表。
(3) 对每个函数调用都需要增加在表中查找地址的操作。六、虚函数的注意事项
总结前面的内容(1) 基类方法中声明了方法为虚后,该方法在基类派生类中是虚的。
(2) 若使用指向对象的引用或指针调用虚方法,程序将根据对象类型来调用方法,而不是指针的类型。
(3)如果定义的类被用作基类,则应将那些要在派生类中重新定义的类方法声明为虚。构造函数不能为虚函数。
基类的析构函数应该为虚函数。
- 首先明确调用顺序:
- 构造函数:先调用父类的,再调用子类的;不可以为虚函数
- 折构函数:先调用子类的,再调用父类的;可以是虚函数
友元函数不能为虚,因为友元函数不是类成员,只有类成员才能是虚函数。
如果派生类没有重定义函数,则会使用基类版本。
重新定义继承的方法若和基类的方法不同(协变除外),会将基类方法隐藏;如果基类声明方法被重载,则派生类也需要对重载的方法重新定义,否则调用的还是基类的方法。
虚函数在多态中的应用
虚函数在多态中的应用十分重要。多态指的是同一种类型的对象,在不同的情况下可以表现出不同的行为。C++通过使用虚函数来实现多态性。
虚函数是指在基类中被声明为虚函数并在派生类中重新定义的函数。当基类指针指向派生类对象时,通过该指针调用虚函数时,会根据指向的实际对象类型进行动态绑定,从而执行相应的派生类函数。
例如,有一个基类Animal和两个派生类Cat和Dog,它们都有一个名为sound()的虚函数。当使用基类指针指向Cat或Dog对象时,通过该指针调用sound()函数时会自动执行相应的派生类函数,从而实现了多态性。
虚函数的应用使得程序具有更好的灵活性和可扩展性,并且简化了代码的编写和维护。
虚函数的优缺点
虚函数的优点:
- 实现多态性:通过虚函数,可以在不知道对象具体类型的情况下,调用特定对象的方法。
- 代码灵活性:虚函数允许子类覆盖父类的方法,并且不需要改变基类的代码。
- 代码可维护性:虚函数使得代码易于维护和扩展,因为子类可以通过重载虚函数来实现自己的行为。
虚函数的缺点:
- 额外的开销:虚函数需要额外的开销来支持运行时的动态绑定和查找虚表。这可能会影响程序的性能。
- 可能会引起混淆:由于虚函数的存在,同名的函数可能会被不同的类定义。如果没有正确的使用虚函数,可能会导致混淆和错误的结果。
- 不适合于小型应用:虚函数对于小型应用来说可能过于复杂和冗余。在这种情况下,使用虚函数可能会导致更多的开销而不是提高效率。
如何合理地使用虚函数
- 虚函数应用于继承层次结构中的多态性,即通过基类指针或引用调用派生类对象的成员函数。
- 可以将虚函数作为接口定义,让不同的派生类实现自己的版本,以满足各自的需求。
- 避免在构造函数和析构函数中调用虚函数,因为此时对象还未完全构造或已经被销毁。
- 虚函数的声明应该在公共部分(例如基类),而不是在私有部分(例如派生类)中声明。
- 将虚函数的实现定义为inline可以提高程序的执行效率。
- 在使用纯虚函数时,需要给出其具体实现。可以在派生类中实现,也可以在基类中实现。
- 避免过度使用虚函数,因为虚函数会增加程序的开销。在没有必要的情况下,可以使用普通成员函数代替虚函数。
纯虚函数和抽象类
纯虚函数是指在基类中定义的没有实现的虚函数。使用纯虚函数可以使该函数只有函数原型,而没有具体的实现。注:这里的“=0”表示该函数为纯虚函数。
纯虚函数的作用是让子类必须实现该函数,并且不能直接创建该类对象(即该类为抽象类)。
抽象类是包含纯虚函数的类,它们不能被实例化,只能被继承。抽象类只能用作其他类的基类。如果一个类继承了抽象类,则必须实现所有的纯虚函数,否则该类也会成为抽象类。
#include <iostream> #include <string> using namespace std;class Base { public://纯虚函数//只要有一个纯虚函数,这个类称为抽象类//抽象类特点:// 1、无法实例化对象// 2、抽象类的子类 必须要重写父类中的纯虚函数,否则也是属于抽象类virtual void func() = 0; //父类的虚函数实现毫无用处,可以直接写成纯虚函数 };class Son : public Base { public:virtual void func() { cout << "func的调用" << endl; } };void test(){Base *b = new Son; //调用通过父类的指针指向子类的对象b->func(); //多态的目的让我们的函数接口更通用化,通过一个父类指针,可以调用不同对象下对应的函数delete b; }int main() {test();return 0; }
虚析构纯虚析构
虚析构函数的必要性
C++虚析构函数的必要性在于,当一个类拥有子类并且该类中包含有动态分配的内存资源时,需要通过虚析构函数来释放这些内存资源。如果不使用虚析构函数,当子类实例被删除时,只会调用基类的析构函数,而不会调用子类的析构函数,从而导致子类中动态分配的内存资源无法正确释放,可能会导致内存泄漏或者程序崩溃。因此,在使用继承和动态内存分配时,为了保证程序的正确性和健壮性,需要使用虚析构函数来释放内存资源。(将父类的析构函数写为虚函数或者纯虚函数即可,可以解决父类指针释放子类对象时不干净的问题)
纯虚析构 :需要声明也需要实现,有了纯虚析构之后,这个类也属于抽象类,无法实例化对象。
多重继承中的虚函数
在多重继承中,如果一个类同时继承了多个基类,而这些基类中都有同名的虚函数,那么子类必须对这些虚函数进行重写并实现。此时,需要使用作用域限定符来指明重写的是哪个基类的虚函数。
例如:
class Base1 { public:virtual void func() { cout << "Base1::func()" << endl; } };class Base2 { public:virtual void func() { cout << "Base2::func()" << endl; } };class Derived : public Base1, public Base2 { public:virtual void func() { Base1::func(); Base2::func(); } };例子中,派生类Derived同时继承了Base1和Base2,这两个基类中都有名为func的虚函数。在Derived中,我们通过使用作用域限定符Base1::和Base2::,分别调用了两个基类中的虚函数。
Derive d; Base1 *b1 = &d; Base2 *b2 = &d; Base3 *b3 = &d; b1->f(); //Derive::f() b2->f(); //Derive::f() b3->f(); //Derive::f() b1->g(); //Base1::g() b2->g(); //Base2::g() b3->g(); //Base3::g()
小结
摘自:
1、对C++虚函数不了解?看完这篇文章掌握虚函数的原理和作用 - 知乎
2、C++虚函数的作用和实现原理_c++虚函数作用及底层原理-CSDN博客
3、C++虚函数的作用和实现原理_c++ 纯虚函数 父类实现-CSDN博客
4、54 类和对象-多态-多态的原理剖析_哔哩哔哩_bilibili
相关文章:
C++ 之多态虚函数原理及应用
文章目录 多态基本概念和原理虚函数的基本原理和概念虚析构和纯虚析构多重继承中的虚函数小结 多态基本概念和原理 多态的基本概念 **多态是C面向对象三大特性之一** 多态的定义 多态是一种面向对象编程概念,指同一个行为(方法)在不同的对象上…...
亮数据——让你的IP走出去,让价值返回来
亮数据——让你的IP走出去,让价值返回来 前言跨境电商最最最大的痛点——让IP走出去超级代理服务器加速网络免费的代理管理软件亮数据解决痛点亮数据优势介绍亮数据浏览器的使用示例总结 前言 当前社会信息的价值是不可想象的,今天在亮数据中看到了个【…...
spring boot-引入Redis并封装redistemplate操作工具类
文章目录 一、关于spring-redis二、springboot引入Redis及其使用案例三、封装redistemplate操作工具类 一、关于spring-redis spring-data-redis针对jedis提供了如下功能: 连接池自动管理,提供了一个高度封装的“RedisTemplate”类 针对jedis客户端中大…...
android 11 SystemUI 状态栏打开之后的界面层级关系说明之一
比如WiFi 图标的父layout为: Class Name: ButtonRelativeLayout Class Name: QSTileView Class Name: TilePage Class Name: PagedTileLayout Class Name: QSPanel Class Name: NonInterceptingScrollView Class Name: QSContainerImpl Class Name: FrameLayout Cl…...
C#___锁(lock)
lock是一种语言级别的关键字,用于实现线程同步和互斥。它提供了一种简单的方式来确保多个线程不会同时访问共享资源,从而避免竞争条件和数据不一致的问题。 作用: 1、避免并行运算中,共享数据的的读写安全问题; 2、并…...
JAVA的sort用法详解(二维数组排序,List<>排序,lambada表达式,自定义类型排序)
目录 前言: 一维数组降序: 方法1.Comparator接口: 代码实现: 方法2.Collections.reverseOrder(): 代码实现: 二维数组排序: 代码如下: List<>排序: 代码…...
数据分析能力模型分析与展示
具体内容: 专业素质 专业素质-01 数据处理 能力定义•能通过各种数据处理工具及数据处理方法,对内外部海量数据进行清洗和运用,提供统一数据标准,为业务分析做好数据支持工作。 L1•掌握一…...
BUG未解之谜01-指针引用之谜
在leetcode里面刷题出现的问题,当我在sortedArrayToBST里面给root赋予初始值NULL之后,问题得到解决! 理论上root是未初始化的变量,然后我进入insert函数之后,root引用的内容也是未知值,因此无法给原来的二叉…...
数据库事务编程
事务是一个不可分割的数据库操作序列,也是数据库并发控制的基本单位,其执行的结果必须使数据库从一种一致性状态到另一种一致性状态。事务是逻辑上的一组操作,要么都执行,要么都不执行。 事务的 4 大特性 ACID 原子性 Atomicit…...
专题二_滑动窗口(1)
目录 209. 长度最小的子数组 解析 题解 3. 无重复字符的最长子串 解析 题解 1004. 最大连续1的个数 III 解析 题解 209. 长度最小的子数组 209. 长度最小的子数组 - 力扣(LeetCode) 解析 题解 class Solution { public:int minSubArrayLen(int…...
把本地文件上传到HDFS上操作步骤
因为条件有限,我这里以虚拟机centos为例 实验条件:我在虚拟机上创建了三台节点,部署了hadoop,把笔记本上的数据上传到hdfs中 数据打包上传到虚拟机节点上 采用的是rz命令,可以帮我们上传数据 没有的话可以使用命令安装…...
Leo赠书活动-21期 《一篇讲明白 Hadoop 生态的三大部件》
✅作者简介:大家好,我是Leo,热爱Java后端开发者,一个想要与大家共同进步的男人😉😉 🍎个人主页:Leo的博客 💞当前专栏: 赠书活动专栏 ✨特色专栏:…...
【Redis】Redis 介绍Redis 为什么这么快?Redis数据结构Redis 和Memcache区别 ?为何Redis单线程效率也高?
目录 Redis 介绍 Redis 为什么这么快? Redis数据结构 Redis 和Memcache区别 ? 为何Redis单线程效率也高? Redis 介绍 Redis 是一个开源(BSD 许可)、基于内存、支持多种数据结构的存储系统,可以作为数据…...
ArrayList和LinkedList有什么区别?
ArrayList和LinkedList的区别 ArrayList 和 LinkedList 是 Java 中常用的两种集合类,它们都实现了 List 接口,但在内部实现和性能上有一些区别。 内部实现: ArrayList 使用数组实现,它的元素在内存中是连续存储的,每…...
离线数仓(八)【DWD 层开发】
前言 1、DWD 层开发 DWD层设计要点: (1)DWD层的设计依据是维度建模理论(主体是事务型事实表(选择业务过程 -> 声明粒度 -> 确定维度 -> 确定事实),另外两种周期型快照事实表和累积型…...
Jenkins用户角色权限管理
Jenkins作为一款强大的自动化构建与持续集成工具,用户角色权限管理是其功能体系中不可或缺的一环。有效的权限管理能确保项目的安全稳定,避免敏感信息泄露。 1、安装插件:Role-based Authorization Strategy 系统管理 > 插件管理 > 可…...
简明conan教程
简明conan教程 1 关于conan1.1 来源1.2 作用 2 安装conan2.1 ubuntu2.1.1 安装python32.1.2 安装conan 2.2 Windows2.2.1 安装python32.2.2 安装conan 2.3 验证conan是否成功2.4 命令解释2.4.1 Consumer commands2.4.2 Creator commands2.4.3 Package development commands2.4.…...
LeetCode刷题--- 等差数列划分 II - 子序列
个人主页:元清加油_【C】,【C语言】,【数据结构与算法】-CSDN博客 个人专栏 力扣递归题 http://t.csdnimg.cn/yUl2I 【C】 http://t.csdnimg.cn/6AbpV 数据结构 http://t.csdnimg.cn/hKh2l 前言:这个专栏主要讲述动态规划算…...
kubectl 启用shell自动补全功能
官网手册参考:https://kubernetes.io/zh-cn/docs/tasks/tools/install-kubectl-linux/ 系统:centos7 补全脚本依赖于工具 bash-completion, 所以要先安装它(可以用命令 type _init_completion 检查 bash-completion 是否已安装&a…...
极简wordpress网站模板
Pithy设计师wordpress网站模板 精练简洁的wordpress模板,设计师或设计工作室展示型网站模板。 https://www.jianzhanpress.com/?p6329...
Python爬虫实战:研究MechanicalSoup库相关技术
一、MechanicalSoup 库概述 1.1 库简介 MechanicalSoup 是一个 Python 库,专为自动化交互网站而设计。它结合了 requests 的 HTTP 请求能力和 BeautifulSoup 的 HTML 解析能力,提供了直观的 API,让我们可以像人类用户一样浏览网页、填写表单和提交请求。 1.2 主要功能特点…...
龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:
在 HarmonyOS 应用开发中,手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力,既支持点击、长按、拖拽等基础单一手势的精细控制,也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档,…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...
网络编程(UDP编程)
思维导图 UDP基础编程(单播) 1.流程图 服务器:短信的接收方 创建套接字 (socket)-----------------------------------------》有手机指定网络信息-----------------------------------------------》有号码绑定套接字 (bind)--------------…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
【Go语言基础【12】】指针:声明、取地址、解引用
文章目录 零、概述:指针 vs. 引用(类比其他语言)一、指针基础概念二、指针声明与初始化三、指针操作符1. &:取地址(拿到内存地址)2. *:解引用(拿到值) 四、空指针&am…...
【笔记】WSL 中 Rust 安装与测试完整记录
#工作记录 WSL 中 Rust 安装与测试完整记录 1. 运行环境 系统:Ubuntu 24.04 LTS (WSL2)架构:x86_64 (GNU/Linux)Rust 版本:rustc 1.87.0 (2025-05-09)Cargo 版本:cargo 1.87.0 (2025-05-06) 2. 安装 Rust 2.1 使用 Rust 官方安…...
站群服务器的应用场景都有哪些?
站群服务器主要是为了多个网站的托管和管理所设计的,可以通过集中管理和高效资源的分配,来支持多个独立的网站同时运行,让每一个网站都可以分配到独立的IP地址,避免出现IP关联的风险,用户还可以通过控制面板进行管理功…...













