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

优秀响应式网站教育行业网站制作

优秀响应式网站,教育行业网站制作,邯郸做网站,建设彩票网站目录 前言 一、重载 1.函数重载 2.运算符重载 二、构造函数 1.什么是构造函数 2.带参数的构造函数 3.使用初始化列表 4.this关键字 5.new关键字 三、析构函数 1.什么是析构函数 四、静态成员变量 1.静态成员的定义 2.静态成员变量的作用 五、继承 1.继承基本概…目录 前言 一、重载 1.函数重载 2.运算符重载 二、构造函数 1.什么是构造函数 2.带参数的构造函数 3.使用初始化列表 4.this关键字 5.new关键字 三、析构函数 1.什么是析构函数 四、静态成员变量 1.静态成员的定义 2.静态成员变量的作用 五、继承 1.继承基本概念 2.权限对继承的影响 3.基类构造函数 4.虚函数 5.多重继承 6.虚继承 六、多态 1.多态的基本概念(polymorphic) 2.如何实现多态 3.抽象类 抽象类的基本概念 抽象类的特点 前言 本博客为C学习第二篇第一篇见C基础入门一。 本教程是针对QT学习打下基础可能有些知识点讲的不是很深刻但是对于QT的学习够用了等见到未知的知识后会在QT相关文章进行补充将要学习QT的同学可以关注我后续会更新QT项目从项目上手学习QT。 一、重载 1.函数重载 在同一个作用域内可以声明几个功能类似的同名函数这些同名函数的形式参数指参数的个数、类型或者顺序必须不同。您不能仅通过返回类型的不同来重载函数。下面的实例中同名函数 print() 被用于输出不同的数据类型 #include iostream using namespace std;class printData { public:void print(int i) {cout 整数为: i endl;}void print(double f) {cout 浮点数为: f endl;}void print(string str) {cout 字符串为: str endl;} };int main(void) {printData pd;// 输出整数pd.print(5);// 输出浮点数pd.print(500.263);// 输出字符串string str Hello C;pd.print(str);return 0; } 这里要注意不能只通过返回值来重载函数必须通过参数的不同来重载。函数重载在QT中也是非常常见的。 2.运算符重载 用的不多在C中运算符重载是一个允许程序员自定义各种运算符如-!等在自定义类型类或结构体上的行为的特性。这意味着你可以定义类似于内置类型的运算符行为使你的自定义类型更加直观和易于使用。 基本原则 ① 不可以创建新的运算符只能重载已经存在的运算符。② 至少有一个操作数是用户定义的类型不能重载两个基本类型的运算符。③ 不能更改运算符的优先级重载的运算符保持其原有的优先级和结合性。 示例假设我们有一个 Person 类我们可以重载 运算符来实现两个 Person 是否相等的判断。 #include iostream using namespace std;class Person { public:string name;int inNumberTail;// 重载 运算符bool operator(Person pTmp); };// 可以把 operator 理解成一个函数名之前在类里面是一个函数的声明这里实现函数的功能 bool Person::operator(Person pTmp) {// 这里的 name 是调用对象 p1 的成员变量return pTmp.name name pTmp.inNumberTail inNumberTail; }int main() {// 假设我们认定名字和身份证尾号6位一样的两个对象是同一个人Person p1;p1.name 张三;p1.inNumberTail 412508;Person p2;p2.name 张三;p2.inNumberTail 412508;bool ret p1 p2; // p1 是调用对象p2 是传递给 operator 的参数cout ret endl; // 输出 1truereturn 0; } 解释 ● 调用 p1 p2 p1 是调用对象 this 指针后面讲到构造函数时会讲指向 p1。 p2 是传递给 operator 函数的参数 pTmp。 在 operator 函数内部name 和 inNumberTail 指的是 p1 的成员变量。 pTmp.name 和 pTmp.inNumberTail 指的是 p2 的成员变量。 主要让人疑惑的点就在重载函数的具体实现部分在我们不是到传入参数是p1还是p2时并不会影响我们对代码的阅读但是怎么和另一个对象比较我们之前讲成员函数知道了成员函数可以直接访问类里面的数据这里重载运算符也相当于是成员函数后面我们知道了p1是调用对象也就是说重载函数是调用的p1的成员函数所以p1可以直接访问name入参就是p2。 示例2假设我们有一个简单的 Point 类我们可以重载 运算符来实现两个点的加法。 #include iostreamusing namespace std;class Point { public:int x, y;// 重载 运算符Point operator(Point other) const {Point ret;ret.x x other.x;ret.y y other.y;return ret;} };int main() {Point p1;p1.x 1;p1.y 2;Point p2;p2.x 2;p2.y 3;Point p3 p1 p2; // 使用重载的 运算符std::cout p3.x: p3.x , p3.y: p3.y std::endl; // 输出 p3.x: 3, p3.y: 5return 0; } 在这个例子中 operator 被重载为一个成员函数接受一个 Point 类型的常量引用作为参数并返回两个点相加的结果。 二、构造函数 1.什么是构造函数 类的构造函数是类的一种特殊的成员函数它会在每次创建类的新对象时执行。 构造那构造的是什么呢 构造成员变量的初始化值内存空间等。构造函数的名称与类的名称是完全相同的并且不会返回任何类型也不会返回 void。构造函数可用于为某些成员变量设置初始值。 下面的实例有助于更好地理解构造函数的概念 #include iostream #include string using namespace std; // 使用std命名空间class Car { public:string brand; // 不需要使用std::stringint year;// 无参构造函数Car() {brand 未知;year 0;cout 无参构造函数被调用 endl; // 不需要使用std::cout和std::endl}void display() {cout Brand: brand , Year: year endl;} };int main() {Car myCar; // 创建Car对象myCar.display(); // 显示车辆信息return 0; } 可以看到先打印了无参构造函数里的信息再打印了品牌等信息。由此可见实例化一个类的对象后构造函数最先被调用如果不写构造函数也会有个默认的构造函数只不过什么事也不做。 2.带参数的构造函数 默认的构造函数没有任何参数但如果需要构造函数也可以带有参数。这样在创建对象时就会给对象赋初始值如下面的例子所示 #include iostream #include string using namespace std;class Car { public:string brand;int year;// 带参数的构造函数使用常规的赋值方式Car(string b, int y) {brand b;year y;}void display() {cout Brand: brand , Year: year endl;} };int main() {Car myCar(Toyota, 2020); // 使用带参数的构造函数创建Car对象myCar.display(); // 显示车辆信息return 0; } 3.使用初始化列表 在C中使用初始化列表来初始化类的字段是一种高效的初始化方式尤其在构造函数中。初始化列表直接在对象的构造过程中初始化成员变量而不是先创建成员变量后再赋值。这对于提高性能尤其重要特别是在涉及到复杂对象或引用和常量成员的情况下。 初始化列表紧跟在构造函数参数列表后面以冒号:开始后跟一个或多个初始化表达式每个表达式通常用逗号分隔。下面是使用初始化列表初始化字段的例子 class MyClass { private:int a;double b;std::string c;public:// 使用初始化列表来初始化字段MyClass(int x, double y, const std::string z) : a(x), b(y), c(z) {// 构造函数体} }; 在这个例子中 MyClass 有三个成员变量a int 类型、b double 类型和 c s td::string 类型。当创建MyClass 的一个实例时我们通过构造函数传递三个参数这些参数被用于通过初始化列表直接初始化成员变量。初始化列表 : a(x), b(y), c(z) 的意思是用 x 初始化 a 用 y 初始化 b用 z 初始化 c。 初始化列表的优点包括① 效率对于非基本类型的对象使用初始化列表比在构造函数体内赋值更高效因为它避免了先默认构造然后再赋值的额外开销。② 必要性对于引用类型和常量类型的成员变量必须使用初始化列表因为这些类型的成员变量在构造函数体内不能被赋值。③ 顺序成员变量的初始化顺序是按照它们在类中声明的顺序而不是初始化列表中的顺序。         使用初始化列表是C中推荐的初始化类成员变量的方式因为它提供了更好的性能和灵活性。 4.this关键字 在 C 中 this 关键字是一个指向调用对象的指针。它在成员函数内部使用用于引用调用该函数的对象。使用 this 可以明确指出成员函数正在操作的是哪个对象的数据成员。下面是一个使用 Car 类来 展示 this 关键字用法的示例看这段文字的描述可能有些绕还记得上一节的运算符重载吗 我们重载 运算符用于判断两个类是否相等重载函数只有一个传入的参数那么我们怎么和另外一个类的对象比较我们理所当然的认为p1是调用对象所以重载函数里的name是p1的name传入的参数是p2的name我们现在引入this关键字我的理解是我们调用的是p1的重载函数那么this这时候存储的就是p1的地址this指向p1所以我们之前的代码也可以写成this-name表示p1的name和p2进行比较 #include iostream #include string using namespace std;class Car { public:Car(string brand, int year) {cout 构造函数中 endl;cout this endl;} };int main() {Car car(宝马,2024);cout main函数中 endl;cout car endl;return 0; }可以看到this指向被调用对象当我们实例化一个Car类的对象时构造函数被调用这时候this指向car我们再在main函数中打印car的地址发现是一样的。 利用引用和this关键字我们可以做到链式调用稍微有点复杂示例如下 #include iostream #include string using namespace std;class Car { private:string brand;int year; public:Car(string brand, int year) {this-brand brand;this-year year;}void display() const {cout Brand: this-brand , Year: this-year endl;// 也可以不使用 this-直接写 brand 和 year}//重点看这里Car setYear(int year) {this-year year; // 更新年份return *this; // 返回调用对象的引用} };int main() {Car car(宝马,2024);car.display();//链式调用car.setYear(2023).display();return 0; } 可以看到我们调用完修改年份的函数后又调用了打印函数。重点就在于 setYear 成员函数返回值是一个Car类的引用关于引用的知识看我上篇博客C基础入门一引用是直达地址的我们 return *this this是一个指针嘛我们之前使用this都是用-操作符this 存放的是实例化后的car的地址引用的初始化不是赋值地址而是直接变量名赋值所以这里return *this相当于 Car car;返回值就是对car的引用。 在我之前的博客就讲了引用作为返回值时可以把函数放在赋值语句的左边我们调用 car.setYear(2023) 后返回值还是引用引用是变量的别名这里可以把 car.setYear(2023) 简单理解成 car 那么我们理所当然调用 car.display(); 这就是链式调用。可能我说的不太好同学们看看引用的含义和用法this指针很好理解自己用自己的理解解释一遍可以在评论区说出自己的理解让大伙们更加深刻。 5.new关键字 在C中 new 关键字用于动态分配内存。它是C中处理动态内存分配的主要工具之一允许在程序运行时根据需要分配内存。 基本用法         分配单个对象使用 new 可以在堆上动态分配一个对象。例如 new int 会分配一个 int 类型的空间并返回一个指向该空间的指针。 int* ptr new int; //C语言中int *p (int *)malloc(sizeof(int)); 分配对象数组 new 也可以用来分配一个对象数组。例如 new int[10] 会分配一个包含10个整数的数组。  int* arr new int[10]; //C语言中int *arr (int *)malloc(sizeof(int)*10); 初始化可以在 new 表达式中使用初始化。对于单个对象可以使用构造函数的参数  MyClass* obj new MyClass(arg1, arg2); 与 delete 配对使用 使用 new 分配的内存必须显式地通过 delete 对于单个对象或 delete[] 对于数组来释放以避免内存泄露 释放单个对象 delete ptr; // 释放 ptr 指向的对象 释放数组 delete[] arr; // 释放 arr 指向的数组 三、析构函数 1.什么是析构函数 析构函数是C中的一个特殊的成员函数它在对象生命周期结束时被自动调用用于执行对象销毁前的清理工作。析构函数特别重要尤其是在涉及动态分配的资源如内存、文件句柄、网络连接等的情况下。 基本特性 1. 名称析构函数的名称由波浪号~后跟类名构成如 ~MyClass() 。 2. 无返回值和参数析构函数不接受任何参数也不返回任何值。 3. 自动调用当对象的生命周期结束时例如一个局部对象的作用域结束或者使用 delete 删除一个动态分配的对象析构函数会被自动调用。 4. 不可重载每个类只能有一个析构函数。 5. 继承和多态如果一个类是多态基类其析构函数应该是虚的。 示例         假设我们有一个类 MyClass 它包含了动态分配的内存或其他资源 class MyClass { private:int* datas; // 指向动态分配的整数数组 public:MyClass(int size) { // 构造函数datas new int[size]; // 动态分配一个大小为 size 的整数数组}~MyClass() { // 析构函数cout 析构函数被调用 endl;delete[] datas; // 释放动态分配的数组} };int main() {MyClass m1(5); // 在栈上创建一个 MyClass 对象 m1大小为 5MyClass *m2 new MyClass(10); // 在堆上创建一个 MyClass 对象 m2大小为 10delete m2; // 释放堆上的对象 m2return 0; } 详细解释         栈上对象 m1 是在栈上创建的当 main 函数结束时栈上的对象会自动析构。因此m1 的析构函数会在 main 函数结束时被调用。         堆上对象 m2 是在堆上创建的需要显式调用 delete 来释放。delete m2; 会调用 m2 的析构函数释放动态分配的数组并输出 析构函数被调用。 四、静态成员变量 1.静态成员的定义 静态成员在C类中是一个重要的概念它包括静态成员变量和静态成员函数。静态成员的特点和存在的意义如下  静态成员变量 1. 定义静态成员变量是类的所有对象共享的变量。与普通成员变量相比无论创建了多少个类的实例静态成员变量只有一份拷贝。 2. 初始化静态成员变量需要在类外进行初始化通常在类的实现文件中。 3. 访问静态成员变量可以通过类名直接访问不需要创建类的对象。也可以通过类的对象访问。 4. 用途常用于存储类级别的信息例如计数类的实例数量或全局数据需要被类的所有实例共享。 静态成员函数 1. 定义静态成员函数是可以不依赖于类的实例而被调用的函数。它不能访问类的非静态成员变量和非静态成员函数。 2. 访问类似于静态成员变量静态成员函数可以通过类名直接调用也可以通过类的实例调用。 3. 用途常用于实现与具体对象无关的功能或访问静态成员变量。 示例代码 #include iostreamclass MyClass { public:static int staticValue; // 静态成员变量MyClass() {// 每创建一个对象静态变量增加1staticValue;}static int getStaticValue() {// 静态成员函数return staticValue;} }; // 类外初始化静态成员变量 int MyClass::staticValue 0; int main() {MyClass obj1, obj2;std::cout MyClass::getStaticValue(); // 输出2 } 存在的意义共享数据允许对象之间共享数据而不需要每个对象都有一份拷贝。节省内存对于频繁使用的类使用静态成员可以节省内存。独立于对象的功能静态成员函数提供了一种在不创建对象的情况下执行操作的方法这对于实现工具函数或管理类级别状态很有用。 2.静态成员变量的作用 静态成员变量在C中的一个典型应用是用于跟踪类的实例数量。这个案例体现了静态成员变量的特性它们在类的所有实例之间共享因此适合于存储所有实例共有的信息。 下面是一个示例展示了如何使用静态成员变量来计数一个类的实例数量 #include iostream using namespace std; class Myclass{ private:static int staticNumofInstance; public:Myclass(){staticNumofInstance;}~Myclass(){staticNumofInstance--;}static int getNunofInstance(){return staticNumofInstance;} };int Myclass::staticNumofInstance 0;int main() {Myclass m1;cout Myclass::getNunofInstance() endl;Myclass m2;cout m2.getNunofInstance() endl;{Myclass m3;cout Myclass::getNunofInstance() endl;Myclass m4;cout Myclass::getNunofInstance() endl;}cout Myclass::getNunofInstance() endl;Myclass *m5 new Myclass;cout Myclass::getNunofInstance() endl;delete m5;cout Myclass::getNunofInstance() endl;return 0; }可以看到我们将m3和m4放在一个{ }里面当大括号结束时m3和m4的栈被释放这里是QT的一个新的用法。 在这个例子中 ● Myclass 类有一个静态成员变量 staticNumofInstance 用来跟踪该类的实例数量。 ● 每当创建 Myclass 的新实例时构造函数会增加 staticNumofInstance 。 ● 每当一个 Myclass 实例被销毁时析构函数会减少 staticNumofInstance 。 ● 通过静态成员函数 getNunofInstance 可以随时获取当前的实例数量。 ● 静态成员变量 staticNumofInstance 在类外初始化为0。 这个案例展示了静态成员变量如何在类的所有实例之间共享并为所有实例提供了一个共同的状态在这个例子中是实例的数量。这种技术在需要跟踪对象数量或实现某种形式的资源管理时特别有用。 五、继承 1.继承基本概念 继承是面向对象编程OOP中的一个核心概念特别是在C中。它允许一个类称为派生类或子类继承另一个类称为基类或父类的属性和方法。继承的主要目的是实现代码重用以及建立一种类型之间的层次关系。 特点 1. 代码重用子类继承了父类的属性和方法减少了代码的重复编写。2. 扩展性子类可以扩展父类的功能添加新的属性和方法或者重写覆盖现有的方法。3. 多态性通过继承和虚函数C支持多态允许在运行时决定调用哪个函数。 基本用法 在C中继承可以是公有public、保护protected或私有private的这决定了基类成员在派生类中的访问权限。 #include iostream using namespace std; //基类父类 class Vehicle{ //交通工具车,抽象的概念 public:string type;string contry;string color;double price;int numOfWheel;void run(){cout 车跑起来了 endl;}void stop(); }; //派生类子类 class Bickle : public Vehicle{ }; //派生类子类 class Roadster : public Vehicle{ //跑车也是抽象比父类感觉上范围缩小了点 public:int stateOfTop;void openTopped();void pdrifting(); };int main() {Roadster ftype;ftype.type 捷豹Ftype;ftype.run();Bickle bike;bike.type 死飞;bike.run();return 0; } 在这个例子中 Vehicle 的子类公有地继承自 Vehicle 类这意味着所有 Vehicle 类的公有成员在 Vehicle 的子类中也是公有的。 2.权限对继承的影响 在C中访问控制符对继承的影响可以通过下表来清晰地展示。这个表格展示了不同类型的继承如何影响基类的不同类型成员在派生类中的访问级别。 这个表格提供了一个快速参考帮助理解在不同类型的继承中基类成员的访问级别是如何变化的。记住无论继承类型如何基类的 private 成员始终不可直接在派生类中访问 访问权限回顾 代码验证 #include iostream using namespace std; //基类父类 class Vehicle{ //交通工具车,抽象的概念 public:string type;string contry;string color;double price;int numOfWheel; protected:int protectedData; private:int privateData; public:void run(){cout 车跑起来了 endl;}void stop(); };//私有继承测试 class TestClass : private Vehicle{ public:void tsetFunc(){price 10; //基类的公有数据被私有继承后在派生类中权限编程私有只限在类内部使用} }; //公有继承测试 class Truck : protected Vehicle{ public:void testFunc(){type 数据测试; //编程了公有权限protectedData 10; //保持公有权限privateData 10; //报错了基类的私有成员不管哪种方式的继承都是不可访问的。} }; //公有继承基类的公有权限和保护权限不变私有成员不能访问 class Bickle : public Vehicle{ public:void testFunc(){protectedData 10;} }; //派生类子类 class Roadster : public Vehicle{ //跑车也是抽象比父类感觉上范围缩小了点 public:int stateOfTop;void openTopped();void pdrifting(); }; int main() {TestClass test;test.price 3.3; //报错了基类的公有成员被私有继承后降为私有权限Truck t;t.type 测试; //报错了基类的公有成员被保护继承后降为保护权限t.protectedData 10; //从报错信息看出保护继承造成基类的保护成员还是保持保护权限Roadster ftype;ftype.type 捷豹Ftype;ftype.run();Bickle bike;bike.type 死飞;bike.run();return 0; }3.基类构造函数 在C中派生类可以通过其构造函数的初始化列表来调用基类的构造函数。这是在构造派生类对象时初始化基类部分的标准做法。 当创建派生类的对象时基类的构造函数总是在派生类的构造函数之前被调用。如果没有明确指定将调用基类的默认构造函数。如果基类没有默认构造函数或者你需要调用一个特定的基类构造函数就需要在派生类构造函数的初始化列表中明确指定。 示例  #include iostream class Base { public:int data;Base(int x) {std::cout Base constructor with x x std::endl;} }; class Derived : public Base { public:double ydata;Derived(int x, double y) : Base(x) { // 调用 Base 类的构造函数std::cout Derived constructor with y y std::endl;} };int main() {Derived obj(10, 3.14); // 首先调用 Base(10)然后调用 Derived 的构造函数return 0; }在这个例子中 ● Base 类有一个接受一个整数参数的构造函数。 ● Derived 类继承自 Base 它的构造函数接受一个整数和一个双精度浮点数。在其初始化列表中它调用 Base 类的构造函数并传递整数参数。 ● 当 Derived 类的对象被创建时首先调用 Base 类的构造函数然后调用 Derived 类的构造函数。         通过这种方式派生类能够确保其基类部分被正确初始化。在继承层次结构中这是非常重要的特别是当基类需要一些特定的初始化操作时。 4.虚函数 在C中 virtual 和 override 关键字用于支持多态尤其是在涉及类继承和方法重写的情况下。正确地理解和使用这两个关键字对于编写可维护和易于理解的面向对象代码至关重要。 如果学过32的话大概知道虚函数是什么一回事我们的中断回调函数前通常会有__weak 标志表示我们可以重写这个回调函数实现我们自己的功能 virtual 关键字1. 使用场景在基类中声明虚函数。2. 目的允许派生类重写该函数实现多态。3. 行为当通过基类的指针或引用调用一个虚函数时调用的是对象实际类型的函数版本。4. 示例 class Base { public:virtual void func() {std::cout Function in Base std::endl;} }; override 关键字1. 使用场景在派生类中重写虚函数。2. 目的明确指示函数意图重写基类的虚函数。3. 行为确保派生类的函数确实重写了基类中的一个虚函数。如果没有匹配的虚函数编译器会报错。4. 示例  class Derived : public Base { public:void func() override {std::cout Function in Derived std::endl;} }; 注意点 ● 只在派生类中使用 override override 应仅用于派生类中重写基类的虚函数。 ● 虚析构函数如果类中有虚函数通常应该将析构函数也声明为虚的。 ● 默认情况下成员函数不是虚的在C中成员函数默认不是虚函数。只有显式地使用 virtual 关键字才会成为虚函数。 ● 继承中的虚函数一旦在基类中声明为虚函数该函数在所有派生类中自动成为虚函数无论是否使用 virtual 关键字。 正确使用 virtual 和 override 关键字有助于清晰地表达程序员的意图并利用编译器检查来避免常 见的错误如签名不匹配导致的非预期的函数重写。 5.多重继承 在C中多重继承是一种允许一个类同时继承多个基类的特性。这意味着派生类可以继承多个基类的属性和方法。多重继承增加了语言的灵活性但同时也引入了额外的复杂性特别是当多个基类具有相同的成员时。 基本概念 在多重继承中派生类继承了所有基类的特性。这包括成员变量和成员函数。如果不同的基类有相同名称的成员则必须明确指出所引用的是哪个基类的成员。 示例 假设有两个基类 ClassA 和 ClassB 以及一个同时从这两个类继承的派生类 Derived #include iostream class ClassA { public:void displayA() {std::cout Displaying ClassA std::endl;} }; class ClassB { public:void displayB() {std::cout Displaying ClassB std::endl;} };//同时继承A和B class Derived : public ClassA, public ClassB { public:void display() {displayA(); // 调用 ClassA 的 displayAdisplayB(); // 调用 ClassB 的 displayB} };int main() {Derived obj;obj.displayA(); // 调用 ClassA 的 displayAobj.displayB(); // 调用 ClassB 的 displayBobj.display();// 调用 Derived 的 displayreturn 0; } 注意事项 ● 菱形继承问题如果两个基类继承自同一个更高层的基类这可能导致派生类中存在两份基类的副本称为菱形继承或钻石继承问题。这可以通过虚继承来解决。● 复杂性多重继承可能会使类的结构变得复杂尤其是当继承层次较深或类中有多个基类时。 ● ● 设计考虑虽然多重继承提供了很大的灵活性但过度使用可能导致代码难以理解和维护。在一些情况下使用组合或接口纯虚类可能是更好的设计选择。 多重继承是C的一个强大特性但应谨慎使用。合理地应用多重继承可以使代码更加灵活和强大但不当的使用可能导致设计上的问题和维护困难。 6.虚继承 虚继承是C中一种特殊的继承方式主要用来解决多重继承中的菱形继承问题。在菱形继承结构中一个类继承自两个具有共同基类的类时会导致共同基类的成员在派生类中存在两份拷贝这不仅会导致资源浪费还可能引起数据不一致的问题。虚继承通过确保共同基类的单一实例存在于继承层次中来解决这一问题。 菱形继承问题示例 FinalDerived 类如果想引用 Base 类里的成员那么是使用 Derived1 里的还是 2 要解决这个问题应使用虚继承 class Base { public:int data; };class Derived1 : virtual public Base {// 虚继承 Base }; class Derived2 : virtual public Base {// 虚继承 Base };class FinalDerived : public Derived1, public Derived2 {// 继承自 Derived1 和 Derived2 }; 通过将 Derived1 和 Derived2 对 Base 的继承声明为虚继承 virtual public BaseFinalDerived 类中只会有一份 Base 类的成员。无论通过 Derived1 还是 Derived2 的路径访问的都是同一个 base 类的成员。 特点和注意事项 ● 初始化虚基类在使用虚继承时虚基类如上例中的 Base 类只能由最派生的类如 FinalDerived 初始化。● 内存布局虚继承可能会改变类的内存布局通常会增加额外的开销比如虚基类指针。● 设计考虑虚继承应谨慎使用因为它增加了复杂性。在实际应用中如果可以通过其他设计如组合或接口避免菱形继承那通常是更好的选择。 虚继承是C语言中处理复杂继承关系的一种重要机制但它也带来了一定的复杂性和性能考虑。正确地使用虚继承可以帮助你建立清晰、有效的类层次结构。 六、多态 1.多态的基本概念(polymorphic) 想象一下你有一个遥控器这就像是一个基类的指针这个遥控器可以控制不同的电子设备这些设备就像是派生类。无论是电视、音响还是灯光遥控器上的“开/关”按钮这个按钮就像是一个虚函数都能控制它们但具体的操作打开电视、播放音乐、开灯则取决于你指向的设备。 2.如何实现多态 1. 使用虚函数Virtual Function         ● 我们在基类中定义一个虚函数这个函数可以在任何派生类中被“重写”或者说“定制”。         ● 使用关键字 virtual 来声明。2. 创建派生类并重写虚函数         ● 在派生类中我们提供该虚函数的具体实现。这就像是告诉遥控器“当你控制我的这个设备时这个按钮应该这样工作”。3. 通过基类的引用或指针调用虚函数         ● 当我们使用基类类型的指针或引用来调用虚函数时实际调用的是对象的实际类型派生类中的函数版本。 代码示例 #include iostream using namespace std;// 基类遥控器接口 class RemoteCon { public:// 虚函数用于打开设备virtual void openUtils() {cout 遥控器的开被按下 endl;} };// 派生类电视遥控器 class TvRemoteCon : public RemoteCon { public:// 重写虚函数实现具体功能void openUtils() override {cout 电视遥控器的开被按下 endl;}// 测试函数仅用于演示void testFunc() {} };// 派生类音响遥控器 class RoundspeakerCon : public RemoteCon { public:// 重写虚函数实现具体功能void openUtils() override {cout 音响遥控器的开被按下 endl;} };// 派生类灯光遥控器 class LightCon : public RemoteCon { public:// 重写虚函数实现具体功能void openUtils() override {cout 灯光遥控器的开被按下 endl;} };// 测试函数接受基类引用作为参数 void test(RemoteCon r) {r.openUtils(); // 调用虚函数具体调用哪个函数取决于传入对象的实际类型 }int main() {// 创建一个电视遥控器对象通过基类指针管理RemoteCon *remoteCon new TvRemoteCon; // 多态基类指针指向派生类对象remoteCon-openUtils(); // 调用重写的虚函数输出 电视遥控器的开被按下// 创建一个音响遥控器对象通过基类指针管理RemoteCon *remoteCon2 new RoundspeakerCon; // 多态基类指针指向派生类对象remoteCon2-openUtils(); // 调用重写的虚函数输出 音响遥控器的开被按下// 创建一个灯光遥控器对象通过基类指针管理RemoteCon *remoteCon3 new LightCon; // 多态基类指针指向派生类对象remoteCon3-openUtils(); // 调用重写的虚函数输出 灯光遥控器的开被按下// 创建一个电视遥控器对象通过对象本身管理TvRemoteCon tvRemote;test(tvRemote); // 传入对象引用调用重写的虚函数输出 电视遥控器的开被按下// 释放动态分配的内存delete remoteCon;delete remoteCon2;delete remoteCon3;return 0; } 在这个例子中不同的对象以它们自己的方式“开”尽管调用的是相同的函数。这就是多态的魅力——相同的接口不同的行为。 为什么使用多态 灵活性允许我们编写可以处理不确定类型的对象的代码。 可扩展性我们可以添加新的派生类而不必修改使用基类引用或指针的代码。 接口与实现分离我们可以设计一个稳定的接口而将具体的实现留给派生类去处理。 3.抽象类 抽象类的基本概念 想象一下你有一个“交通工具”的概念。这个概念告诉你所有交通工具都应该能做什么比如移动move但它并不具体说明怎么移动。对于不同的交通工具比如汽车和自行车它们的移动方式是不同的。在这个意义上“交通工具”是一个抽象的概念因为它本身并不能直接被使用。你需要一个具体的交通工具比如“汽车”或“自行车”它们根据“交通工具”的概念具体实现了移动的功能。 在 C 中抽象类就像是这样的一个抽象概念。它定义了一组方法比如移动但这些方法可能没有具体的实现。这意味着抽象类定义了派生类应该具有的功能但不完全实现这些功能。 抽象类的特点 1. 包含至少一个纯虚函数         抽象类至少有一个纯虚函数。这是一种特殊的虚函数在抽象类中没有具体实现而是留给派生类去实现。         纯虚函数的声明方式是在函数声明的末尾加上 0。2. 不能直接实例化         由于抽象类不完整所以不能直接创建它的对象。就像你不能直接使用“交通工具”的概念去任何地方你需要一个具体的交通工具。3. 用于提供基础结构         抽象类的主要目的是为派生类提供一个共同的基础结构确保所有派生类都有一致的接口和行为。 示例代码 #include iostream using namespace std;// 抽象基类教师 class Teacher { public:string name; // 教师姓名string school; // 所在学校string major; // 专业领域// 纯虚函数定义教师进入教室的行为virtual void goInClass() 0;// 纯虚函数定义教师开始教学的行为virtual void startTeaching() 0;// 纯虚函数定义教师教学结束后的行为virtual void afterTeaching() 0; };// 派生类英语老师 class EnglishTeacher : public Teacher { public:// 重写虚函数实现英语老师进入教室的具体行为void goInClass() override {cout 英语老师开始进入教室 endl;}// 重写虚函数实现英语老师开始教学的具体行为void startTeaching() override {cout 英语老师开始教学 endl;}// 重写虚函数实现英语老师教学结束后的具体行为void afterTeaching() override {// 英语老师教学结束后没有特定行为可以留空} };// 派生类编程老师 class ProTeacher : public Teacher { public:// 重写虚函数实现编程老师进入教室的具体行为void goInClass() override {cout 编程老师开始进入教室 endl;}// 重写虚函数实现编程老师开始教学的具体行为void startTeaching() override {cout 编程老师开始撸代码了拒绝读PPT endl;}// 重写虚函数实现编程老师教学结束后的具体行为void afterTeaching() override {cout 编程老师下课后手把手教学生写代码 endl;} };int main() {// Teacher t; // 抽象类不支持被实例化// 创建一个英语老师对象EnglishTeacher e;e.goInClass(); // 调用英语老师进入教室的行为// 创建一个编程老师对象ProTeacher t;t.startTeaching(); // 调用编程老师开始教学的行为t.afterTeaching(); // 调用编程老师教学结束后的行为// 抽象类多态Teacher *teacher new ProTeacher; // 基类指针指向派生类对象teacher-startTeaching(); // 调用编程老师开始教学的行为// 释放动态分配的内存delete teacher;return 0; } 到此C阶段的学习就结束了这些对于QT的学习已经够用了等后续要用到再在QT文章进行讲解下一篇博客就是QT的记事本项目直接从项目入手。希望大家持续关注一手一起学习。
http://www.dnsts.com.cn/news/184494.html

相关文章:

  • 中天建设招标网站网站展示模板
  • 外贸哪家做网站百度软件下载
  • 汽车装饰网站源码北京旅游型网站建设
  • 隆回网站建设制作wordpress 设成中文
  • 门户网站开发公司网站制作费用多少
  • 推广网站的图片怎么做遵义网络科技公司
  • 上海建设银行黄浦区营业网站广州市财贸建设开发监理网站
  • 甘肃网站建设哪家好世界互联网峰会时间
  • 怎么修改网站后台路径做医院网站公司吗
  • 做网站 不是计算机专业想开加工厂怎么找订单
  • 南宁市保障住房建设管理服务中心网站帮非法集资公司做网站违法吗
  • 做网站开发哪种语言更稳定高效网站内容建设 互联互通
  • 济南互联网选号网站网址信息查询
  • 做代理网站用什么软件猪八戒网做网站
  • 网站模板怎么改视频聚合网站怎么做不侵权
  • 重庆最火的网站做网站是不是涉及很多语言职
  • 微信公众号微网站怎么做网站平台规划
  • 有没有专门做家乡图片的网站中航建设集团有限公司网站
  • 济南企业建站平台邯郸集团网站建设
  • 中英切换的网站咋做建网站挣钱吗
  • 新开传奇网站超变响应式网站模块
  • 怎么创立网站品牌网站开发动态模块
  • 国外的智慧城市建设网站福建就福建省住房与城乡建设厅网站
  • 企业网站设计优化公司开发公众号的体会
  • 黄国外网站新市区做网站
  • 重庆做网站公司有哪些网站开发公司建站源码
  • 山东网站制作公司排名安徽省建设厅官网查询
  • 网站建设逻辑组织的几种模型什么是网络设计方案网络设计的原则有哪些
  • 美术主题资源网站建设上海发布公众号app
  • 通江县住房和城乡建设局网站旅游找什么网站好