C++ 继承和派生 万字长文超详解

本文章内容来源于C++课堂上的听课笔记

继承和派生基础

继承是一种概念,它允许一个新创建的类(称为子类或派生类)获取另一个已经存在的类(称为父类或基类)的属性和行为。这就好比是子类继承了父类的特征。想象一下,如果有一个“动物”类,它有一些通用的特征和行为,然后我们有一个“猫”类,它可以继承“动物”类的这些通用特征,如四条腿、呼吸等。这样,“猫”类就继承了“动物”类

派生是指从已有类创建新类的过程。在这个过程中,新类继承了已有类的属性和方法。派生类可以添加新的属性或方法,也可以覆盖或修改继承的属性和方法。这就允许我们基于现有的代码构建和扩展新的功能。在上面的例子中,“猫”类就是通过派生(或继承)从“动物”类创建出来的

继承分为单继承和多重继承,如果一个派生类只有一个基类,就是单继承,否则是多重继承

派生类是基类的具体化,基类是派生类的抽象

假设已经声明了一个基类A,想要声明派生类B的一般形式为:

举个例子:

#include <iostream>
using namespace std;// 定义基类 Animal
class Animal {
public:void makeSound() {cout << "Some generic sound...\n";}
};// 定义派生类 Dog,它继承了 Animal 类
class Dog : public Animal {
public:void bark() {cout << "Woof! Woof!\n";}
};int main() {// 创建一个 Dog 对象Dog myDog;// 调用继承自 Animal 类的方法myDog.makeSound();// 调用派生类 Dog 自己的方法myDog.bark();return 0;
}

对于继承方式,一般有三种,分别是public,protected,private,如果不写继承方式,默认是private

派生类中的成员包括从基类继承的成员和新增的成员两大部分

构造派生类时,会涉及下面的一些事件:

1.从基类接收成员。派生类把基类全部的成员接收过来

2.调整从基类接收的成员。接收基类成员是程序人员不能选择的,但是程序人员可以对这些成员作某些调整。如:  通过指定继承方式,改变基类成员在派生类中的访问属性;  可以在派生类中声明一个与基类成员同名的成员,覆盖基类的同名成员(与重载是不同的,这里叫做重写override

3.在声明派生类时增加的成员

4.在声明派生类时,一般还应当自己定义派生类的构造函数和析构函数,因为构造函数和析构函数是不能从基类继承的

继承方式

上面说过,在声明派生类的时候要加一个名叫“继承方式”的东西,这里就对其进行相关的解释

公有继承

基类的公用成员和保护成员在派生类中仍然保持其公用成员和保护成员的属性; 而基类的私有成员在派生类中并没有成为派生类的私有成员,它仍然是基类的私有成员,只有基类的成员函数可以引用它,而不能被派生类的成员函数引用,因此就成为派生类中的不可访问的成员

#include <iostream>
using namespace std;// 定义基类 Vehicle
class Vehicle {
public:void startEngine() {cout << "Engine started.\n";}void stopEngine() {cout << "Engine stopped.\n";}
private:int m;
};// 定义派生类 ElectricCar,它公有继承自 Vehicle
class ElectricCar : public Vehicle {
public:void chargeBattery() {cout << "Battery is charging.\n";cout << m << endl;//无法访问}
};int main() {// 创建一个 ElectricCar 对象ElectricCar myElectricCar;// 使用继承自 Vehicle 类的方法myElectricCar.startEngine();// 使用 ElectricCar 类自己的方法myElectricCar.chargeBattery();// 使用继承自 Vehicle 类的方法myElectricCar.stopEngine();return 0;
}

私有继承

私有基类的公用成员和保护成员在派生类中的访问属性相当于派生类中的私有成员,即派生类的成员函数能访问它们,在派生类外不能访问它们。 私有基类的私有成员在派生类中成为不可访问的成员,只有基类的成员函数可以引用它们

对于不需要再往下继承的类的功能可以用私有继承方式把它隐蔽起来,这样,下一层的派生类无法访问它的任何成员

#include <iostream>
#include <string>
using namespace std;// 定义基类 Person
class Person {
private:string name;int age;public:Person(const string& n, int a) : name(n), age(a) {}void displayInfo() {cout << "Name: " << name << ", Age: " << age << endl;}
};// 定义派生类 Employee,私有继承自 Person
class Employee : private Person {
private:string jobTitle;public:// 在 Employee 构造函数中初始化 Person 的成员Employee(const string& n, int a, const string& title): Person(n, a), jobTitle(title) {}void displayEmployeeInfo() {// 在 Employee 类的成员函数中,可以访问继承的 Person 成员displayInfo();cout << "Job Title: " << jobTitle << endl;}
};int main() {// 创建一个 Employee 对象Employee employee("John", 30, "Software Engineer");// 无法直接访问 Person 的私有成员// employee.displayInfo(); // 这行代码会导致编译错误// 但可以通过 Employee 的成员函数间接访问继承的 Person 成员employee.displayEmployeeInfo();return 0;
}

保护继承

保护成员:保护成员和私有成员类似,都不能直接被外界访问,但唯一不同的是保护成员可以被对应类的派生类的成员函数所访问

保护基类的公用成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有。也就是把基类原有的公用成员也保护起来,不让类外任意访问

三种继承方式总结如下

派生类的构造函数和析构函数

上面说过子类不能继承父类的构造函数和析构函数,但有一种方法是可以让子类去调用父类的构造函数的,这样,就可以同时初始化父类成员和子类成员了

一般形式如下:

派生类构造函数名(总参数表列): 基类构造函数名(参数表列){派生类中新增数据成员初始化语句}

举一个例子:

Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s) //派生类构造函数
{    age=a;                        //在函数体中只对派生类新增的数据成员初始化addr=ad;
}

其中age,addr是派生类新增成员

也可以在类外实现派生类的构造函数,在类内仅仅声明即可

Student1∷Student1(int n,string nam,char s,int a,string ad):Student(n,nam,s)
{age=a;     addr=ad;}

在类中对派生类构造函数作声明时,不包括基类构造函数名及其参数表列

不仅可以利用初始化表对构造函数的数据成员初始化,而且可以利用初始化表调用派生类的基类构造函数,实现对基类数据成员的初始化。也可以在同一个构造函数的定义中同时实现这两种功能。

Student1(int n, string nam,char s,int a, string ad):Student(n,nam,s),age(a),addr(ad){}

在派生类对象释放时,先执行派生类析构函数~Student1( ),再执行其基类析构函数~Student( )

有子对象的构造函数和组合

什么是子对象?简单来说就是一个类的成员中包含一个类对象,这个类对象就是子对象(subobject),这个子对象也是可以直接在构造函数中一起初始化的,非常方便

注意,如果这个子对象是本派生类的基类,还是正常的继承

如果这个子对象是另外一个已定义的类称为类的组合

类的组合和继承一样,是软件重用的重要方式。组合和继承都是有效地利用已有类的资源。但二者的概念和用法不同。继承是纵向的,组合是横向的

#include <iostream>
#include <string>
using namespace std;// 定义基类 Student
class Student {
private:string name;int age;public:// 基类 Student 的构造函数Student(const string& n, int a) : name(n), age(a) {cout << "Student constructor called." << endl;}void displayInfo() {cout << "Name: " << name << ", Age: " << age << endl;}
};// 定义子对象 Grade
class Grade {
private:char grade;public:// Grade 构造函数Grade(char g) : grade(g) {cout << "Grade constructor called." << endl;}void displayGrade() {cout << "Grade: " << grade << endl;}
};// 定义派生类 Student1,包含一个子对象 Grade
class Student1 : public Student {
private:Grade studentGrade;public:// Student1 构造函数,调用基类 Student 和子对象 Grade 的构造函数Student1(const string& n, int a, char g): Student(n, a), studentGrade(g) {cout << "Student1 constructor called." << endl;}// Student1 类的成员函数,可以访问基类 Student 和子对象 Grade 的成员void displayStudent1Info() {displayInfo();        // 访问基类成员函数studentGrade.displayGrade();  // 访问子对象成员函数}
};int main() {// 创建一个 Student1 对象Student1 student1("Alice", 20, 'A');// 调用 Student1 类的成员函数,间接调用基类 Student 和子对象 Grade 的成员函数student1.displayStudent1Info();return 0;
}

执行派生类构造函数的顺序是: ① 调用基类构造函数,对基类数据成员初始化; ② 调用子对象构造函数,对子对象数据成员初始化; ③ 再执行派生类构造函数本身,对派生类数据成员初始化

基类构造函数和子对象的次序可以是任意的,如果有多个子对象,派生类构造函数的写法依此类推,应列出每一个子对象名及其参数表列。

多级派生时的构造函数

#include <iostream>
#include <string>using namespace std;// 基类 Person
class Person {
private:string name;public:// 基类构造函数Person(const string& n) : name(n) {cout << "Person constructor called." << endl;}void displayName() {cout << "Name: " << name << endl;}
};// 派生类 Student,继承自 Person
class Student : public Person {
private:int studentID;public:// Student 构造函数,调用基类 Person 构造函数Student(const string& n, int id) : Person(n), studentID(id) {cout << "Student constructor called." << endl;}void displayStudentInfo() {displayName(); // 访问基类成员函数cout << "Student ID: " << studentID << endl;}
};// 派生类 GraduateStudent,继承自 Student
class GraduateStudent : public Student {
private:string researchTopic;public:// GraduateStudent 构造函数,调用直接基类 Student 构造函数GraduateStudent(const string& n, int id, const string& topic): Student(n, id), researchTopic(topic) {cout << "GraduateStudent constructor called." << endl;}void displayGraduateStudentInfo() {displayStudentInfo(); // 访问直接基类 Student 的成员函数cout << "Research Topic: " << researchTopic << endl;}
};int main() {// 创建一个 GraduateStudent 对象GraduateStudent gradStudent("John Doe", 12345, "Machine Learning");// 调用 GraduateStudent 类的成员函数,间接调用基类 Person 和直接基类 Student 的成员函数gradStudent.displayGraduateStudentInfo();return 0;
}

main 函数中创建 GraduateStudent 对象时,首先调用 Person 类的构造函数,然后调用 Student 类的构造函数,最后调用 GraduateStudent 类的构造函数

派生类构造函数和析构函数注意点

1.当不需要对派生类新增的成员进行任何初始化操作时,派生类构造函数的函数体可以为空,即构造函数是空函数

2.如果在基类中没有定义构造函数,或定义了没有参数的构造函数,那么在定义派生类构造函数时可不写基类构造函数

3.如果在基类和子对象类型的声明中都没有定义带参数的构造函数,而且也不需对派生类自己的数据成员初始化,则可以不必显式地定义派生类构造函数

4.如果在基类或子对象类型的声明中定义了带参数的构造函数,那么就必须显式地定义派生类构造函数

5.如果在基类中既定义无参的构造函数,又定义了有参的构造函数(构造函数重载),则在定义派生类构造函数时,既可以包含基类构造函数及其参数,也可以不包含基类构造函数

6.在执行派生类的析构函数时,系统会自动调用基类的析构函数和子对象的析构函数,对基类和子对象进行清理

7.调用的顺序与构造函数正好相反: 先执行派生类自己的析构函数,对派生类新增加的成员进行清理,然后调用子对象的析构函数,对子对象进行清理,最后调用基类的析构函数,对基类进行清理

多重继承

注意派生类中的构造函数怎么写的

#include <iostream>
#include <string>using namespace std;// 基类 Shape(形状)
class Shape {
protected:int width;int height;public:// Shape 类构造函数Shape(int w, int h) : width(w), height(h) {cout << "Shape constructor called." << endl;}void displayShapeInfo() {cout << "Shape - Width: " << width << ", Height: " << height << endl;}
};// 基类 Color(颜色)
class Color {
protected:string color;public:// Color 类构造函数Color(const string& c) : color(c) {cout << "Color constructor called." << endl;}void displayColor() {cout << "Color: " << color << endl;}
};// 派生类 ColoredShape(有颜色的形状),继承自 Shape 和 Color
class ColoredShape : public Shape, public Color {
public:// ColoredShape 类构造函数,显式调用基类构造函数ColoredShape(int w, int h, const string& c,int id) : Shape(w, h), Color(c) id(id){cout << "ColoredShape constructor called." << endl;}void displayColoredShapeInfo() {displayShapeInfo(); // 访问 Shape 类的成员函数displayColor();     // 访问 Color 类的成员函数}int id;
};int main() {// 创建一个 ColoredShape 对象ColoredShape coloredShape(10, 5, "Red");// 调用 ColoredShape 类的成员函数,间接调用基类 Shape 和 Color 的成员函数coloredShape.displayColoredShapeInfo();return 0;
}

虚基类

如果一个派生类有多个直接基类,而这些直接基类又有一个共同的基类,则在最终的派生类中会保留该间接共同基类数据成员的多份同名成员

C++提供虚基类(virtual base class)的方法,使得在继承间接共同基类时只保留一份成员

虚基类并不是在声明基类时声明的,而是在声明派生类时,指定继承方式时声明的。因为一个基类可以在生成一个派生类时作为虚基类,而在生成另一个派生类时不作为虚基类。 声明虚基类的一般形式为

class 派生类名: virtual 继承方式 基类名

#include <iostream>// 基类:动物
class Animal {
public:Animal(const std::string& name) : name(name) {}void eat() const {std::cout << name << " is eating." << std::endl;}void sleep() const {std::cout << name << " is sleeping." << std::endl;}private:std::string name;
};// 虚基类:飞行动物
class FlyingAnimal : virtual public Animal {
public:FlyingAnimal(const std::string& name) : Animal(name) {}void fly() const {std::cout << Animal::getName() << " is flying." << std::endl;}
};// 派生类:哺乳动物
class Mammal : virtual public Animal {
public:Mammal(const std::string& name) : Animal(name) {}void giveBirth() const {std::cout << Animal::getName() << " is giving birth." << std::endl;}
};// 派生类:既是哺乳动物又可以飞的动物
class Bat : public FlyingAnimal, public Mammal {
public:Bat(const std::string& name) : Animal(name), FlyingAnimal(name), Mammal(name) {}
};int main() {Bat bat("Batman");// 通过虚基类,避免了二义性问题bat.eat();bat.sleep();bat.fly();bat.giveBirth();return 0;
}

基类和派生类的转换

在C++中,基类和派生类之间的转换主要涉及两种类型:向上转型(Upcasting)和向下转型(Downcasting)。这些转型可能涉及到指针和引用。

向上转型(Upcasting):

向上转型是将派生类的指针或引用转换为基类的指针或引用。这是一个安全的操作,因为派生类对象包含基类的部分。这样的转换通常是自动进行的,不需要显式的转换操作

#include <iostream>class Base {
public:virtual void display() const {std::cout << "Base class display" << std::endl;}
};class Derived : public Base {
public:void display() const override {std::cout << "Derived class display" << std::endl;}void additionalFunction() const {std::cout << "Additional function in the derived class" << std::endl;}
};int main() {Derived derivedObj;Base* basePtr = &derivedObj; // 向上转型// 调用的是 Derived 类的 display 方法basePtr->display();// 无法直接调用 Derived 类的 additionalFunction// basePtr->additionalFunction(); // 这行代码会产生编译错误return 0;
}

向下转型(Downcasting):

向下转型是将基类的指针或引用转换为派生类的指针或引用。这是一个潜在的危险操作,因为基类可能无法包含派生类的所有信息。因此,向下转型时通常需要进行显式的类型转换,并且应该在确保安全性的情况下进行

class Base {//...
};class Derived : public Base {//...
};int main() {Base* basePtr = new Derived();// 向下转型,需要显式的类型转换Derived* derivedPtr = dynamic_cast<Derived*>(basePtr);if (derivedPtr) {// 转换成功,可以使用 derivedPtr 操作 Derived 类的成员} else {// 转换失败,basePtr 实际上不指向 Derived 类的对象}delete basePtr;return 0;
}

在上述代码中,使用了 dynamic_cast 运算符进行向下转型。这个运算符在运行时检查转型的有效性,如果转型合法,则返回指向派生类对象的指针;否则,返回空指针。

总的来说,向上转型是安全的,而向下转型需要谨慎并使用适当的手段进行检查,以避免潜在的运行时错误。在进行向下转型时,通常使用 dynamic_cast 运算符进行类型检查,或者在一些情况下,可以使用 static_cast 运算符,但要确保转型是安全的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/198731.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【dc-dc】世微 电动车摩托车灯 5-80V 1.2A 一切二降压恒流驱动器AP2915

产品描述 AP2915 是一款可以一路灯串切换两路灯串的降压恒流驱动器,高效率、外围简单、内置功率管&#xff0c;适用于5-80V 输入的高精度降压 LED 恒流驱动芯片。内置功率管输出最大功率可达 12W&#xff0c;最大电流 1.2A。AP2915 一路灯亮切换两路灯亮&#xff0c;其中一路灯…

微积分在神经网络中的本质

calculus 在一个神经网络中我们通常将每一层的输出结果表示为&#xff1a; a [ l ] a^{[l]} a[l] 为了方便记录&#xff0c;将神经网络第一层记为&#xff1a; [ 1 ] [1] [1] 对应的计算记录为为&#xff1a; a [ l ] &#xff1a; 第 l 层 a [ j ] &#xff1a; 第 j 个神经…

在市场发展中寻变革,马上消费金融树行业发展“风向标”

11月11日&#xff0c;2023金融街论坛年会第三届全球金融科技大会“金融科技创新与合规安全”平行论坛在北京召开。会上&#xff0c;马上消费金融副总经理孙磊就数据对金融的赋能作用、数据安全治理等方面展开了深度讨论。 公开信息显示&#xff0c;马上消费金融是一家经中国银保…

如何将文字、图片、视频、链接等内容生成一个二维码?

通过二维彩虹的【H5编辑】功能&#xff0c;就可以将文字、图片、视频、文件、链接等多种格式的内容编辑在一个页面&#xff0c;然后生成一个自定义的二维码——H5编辑二维码。扫描后&#xff0c;即可查看二维码中的详细图文视频等内容了。这个功能大受欢迎&#xff01; 这个H5…

大师学SwiftUI第18章Part1 - 图片选择器和相机

如今&#xff0c;个人设备主要用于处理图片、视频和声音&#xff0c;苹果的设备也不例外。SwiftUI可以通过​​Image​​视图显示图片&#xff0c;但需要其它框架的支持来处理图片、在屏幕上展示视频或是播放声音。本章中我们将展示Apple所提供的这类工具。 图片选择器 Swift…

Three.js相机模拟

有没有想过如何在 3D Web 应用程序中模拟物理相机? 在这篇博文中,我将向你展示如何使用 Three.js和 OpenCV 来完成此操作。 我们将从模拟针孔相机模型开始,然后添加真实的镜头畸变。 具体来说,我们将仔细研究 OpenCV 的两个失真模型,并使用后处理着色器复制它们。 拥有逼…

Rockdb简介

背景 最近在使用flink的过程中&#xff0c;由于要存储的状态很大&#xff0c;所以使用到了rockdb作为flink的后端存储&#xff0c;本文就来简单看下rockdb的架构设计 Rockdb设计 Rockdb采用了LSM的结构&#xff0c;它和hbase很像&#xff0c;不过严格的说&#xff0c;基于LS…

高精度算法【Java】(待更新中~)

高进度加法 在Java中可以使用BigInteger进行高精度计算&#xff0c;除此也可以仿照竖式相加的计算原理进行计算。 BigInteger 提供所有 Java 的基本整数操作符的对应物&#xff0c;并提供 java.lang.Math 的所有相关方法。另外&#xff0c;BigInteger 还提供以下运算&#xff1…

11月20日星期一今日早报简报微语报早读

11月20日星期一&#xff0c;农历十月初八&#xff0c;早报微语早读。 1、T1以3-0横扫WBG&#xff0c;拿下S13冠军&#xff01;Faker豪取第4冠&#xff1b; 2、天舟七号货运飞船已运抵文昌发射场&#xff0c;将于明年初发射&#xff1b; 3、“中韩之战”球票已经售罄&#xf…

回归预测 | Matlab实现HPO-ELM猎食者算法优化极限学习机的数据回归预测

回归预测 | Matlab实现HPO-ELM猎食者算法优化极限学习机的数据回归预测 目录 回归预测 | Matlab实现HPO-ELM猎食者算法优化极限学习机的数据回归预测效果一览基本介绍程序设计参考资料 效果一览 基本介绍 Matlab实现HPO-ELM猎食者算法优化极限学习机的数据回归预测&#xff08;…

JAVA多线程(5)

JAVA多线程(5) 线程安全问题概述 卖票问题分析 单窗口卖票 一个窗口(单线程)卖100张票没有问题 单线程程序是不会出现线程安全问题的 多个窗口卖不同的票 3个窗口一起卖票,卖的票不同,也不会出现问题 多线程程序,没有访问共享数据,不会产生问题 多个窗口卖相同的票 3个窗口…

UI 自动化测试框架设计与 PageObject 改造!

在 UI 自动化测试过程中&#xff0c;面对复杂的业务场景&#xff0c;经常会遇到这样的挑战&#xff1a; 简单的录制/回放速度快&#xff0c;但无法适应复杂场景&#xff1b;编写自动化测试脚本比较灵活&#xff0c;但工作量大且可维护性差&#xff1b;以往的封装技术&#xff…

【实用技巧】更改ArduinoIDE默认库文件位置,解放系统盘,将Arduino15中的库文件移动到其他磁盘

本文主要介绍更改Arduino IDE &#xff08;含2.0以上版本&#xff09;默认库文件位置的方法。 原创文章&#xff0c;转载请注明出处&#xff1a; 【实用技巧】更改ArduinoIDE默认库文件位置&#xff0c;解放C盘&#xff0c;将Arduino15中的库文件移动到其他磁盘-CSDN博客文章浏…

SpringBoot-过滤器Filter+JWT令牌实现登录验证

登录校验-Filter 分析 过滤器Filter的快速入门以及使用细节我们已经介绍完了&#xff0c;接下来最后一步&#xff0c;我们需要使用过滤器Filter来完成案例当中的登录校验功能。 我们先来回顾下前面分析过的登录校验的基本流程&#xff1a; 要进入到后台管理系统&#xff0c;我…

CleanMyMac X2024免费测试版好不好用?值不值得下载

如果你是一位Mac用户&#xff0c;你可能会遇到一些问题&#xff0c;比如Mac运行缓慢、磁盘空间不足、应用程序难以管理等。这些问题会影响你的Mac的性能和体验&#xff0c;让你感到沮丧和无奈。那么&#xff0c;有没有一款软件可以帮助你解决这些问题呢&#xff1f;答案是肯定的…

VR智慧景区:VR赋能文旅产业,激活消费潜能

随着国家数字化战略的不断深入实施&#xff0c;文旅产业数字化转型的步伐也在逐渐加快&#xff0c;以VR技术赋能文旅产业&#xff0c;让文旅景区线上线下双渠道融合&#xff0c;进一步呈现文化底蕴、激活消费潜能。 VR智慧景区以沉浸式、互动式、科技感的方式&#xff0c;将景区…

buildadmin+tp8表格操作(7.1)表格的事件监听(el-table中的事件)

因为buildAdmin是封装的 el-table的组件&#xff0c;所以el-table中的事件&#xff0c; 也是可以使用的&#xff0c; 两者有几个事件是有共同的&#xff08;比如 双击事件&#xff09;&#xff0c; 这时可以根据自己的需要自行选择 以下代码是 buildadmin 使用 el-table中的事…

【Linux】第十九站:进程替换

文章目录 一、单进程版---最简单的程序替换二、进程替换的原理三、多进程的程序替换1.多进程的程序替换实例2.那么程序在替换时候有没有创建子进程呢3.再谈原理4.一个现象5.我们的CPU如何得知程序的入口地址&#xff1f; 四、各个接口的介绍1.execl2.execlp3.execv4.execvp5.ex…

Navicat 基于 GaussDB 主备版的快速入门

Navicat Premium&#xff08;16.2.8 Windows版或以上&#xff09; 已支持对GaussDB 主备版的管理和开发功能。它不仅具备轻松、便捷的可视化数据查看和编辑功能&#xff0c;还提供强大的高阶功能&#xff08;如模型、结构同步、协同合作、数据迁移等&#xff09;&#xff0c;这…

BGP的基础知识

BGP——边界网关协议 IGP——内部网关协议——OSPF、RIP、ISIS EGP——外部网关协议——EGP、BGP 边界网关协议BGP是一种实现自治系统AS之间的路由可达&#xff0c;并选择最佳路由的路径矢量路由协议。目前在IPV4环境下主要使用BGPV4&#xff0c;目前市场上也存在BGPV4&…