C++ 基础知识 五 ( 来看来看 面向对象的继承 上篇 )

news/2024/11/29 13:51:54/

C++ 基础知识 五 来看来看 面向对象的继承 上篇

  • 一、简介
    • 1 什么是 C++ 继承
    • 2 继承的目的和作用
  • 二、定义
    • 1 派生类和基类的定义
    • 2 公有继承和私有继承
  • 三、访问控制
    • 1 派生类对基类成员的访问权限
    • 2 如何控制访问权限
  • 四、虚函数继承
    • 1 继承中的虚函数机制
    • 2 在派生类中重写虚函数
  • 五、多重继承
    • 1 语法实现
    • 2 派生类和基类成员的访问规则
      • 2.1 共同基类的继承
      • 2.2 不同基类的继承
  • 六、小结回顾

一、简介

1 什么是 C++ 继承

C++ 继承是指派生类(子类)从基类(父类)继承属性和行为的过程。我们可以创建一个新的类,该类可以继承另一个类的数据属性和方法。

// 定义一个父类 Person
class Person {
public:string name;int age;string gender;void eat() {cout << name << " is eating. \n";}
};// 定义一个子类 Student 继承父类 Person 
class Student : public Person {
public:string school;void study() {cout << name << " is studying at " << school << ". \n";}
};

在上述代码中,我们定义了一个父类 Person 与一个子类 Student。Student 类继承了 Person 类的属性和方法,包括 name、age、gender 和 eat() 函数。

2 继承的目的和作用

继承的主要目的是代码重用:我们可以重用现有的代码,避免重复编写相同的代码,从而提高生产效率。此外,继承还使得代码更易于管理和维护,因为我们可以通过继承将代码组织成层次结构。

// 使用继承创建 Student 对象
Student myStudent;
myStudent.name = "Tom";
myStudent.age = 18;
myStudent.gender = "Male";
myStudent.school = "ABC University";myStudent.eat();   // 输出 "Tom is eating."
myStudent.study(); // 输出 "Tom is studying at ABC University."

二、定义

继承是面向对象编程语言中一种非常常见的概念。继承可以让类之间实现代码重用,使得程序更加易于扩展和维护。在 C++ 中可以使用派生类和基类来实现继承。

1 派生类和基类的定义

派生类是指从一个或多个基类中派生出来的类。基类是指在一个类继承另一个类的语法中被继承的那个类。在 C++ 中,我们可以通过如下语法定义一个派生类:

class DerivedClass : access-specifier BaseClass {
public:// DerivedClass 的公有成员
protected:// DerivedClass 的保护成员
private:// DerivedClass 的私有成员
};

在上述代码中DerivedClass是我们新创建的类的名称,access-specifier 可以是 public、protected、或 private。BaseClass 是我们想要从中继承属性和方法的类,我们可以在 access-specifier 后面指定 BaseClass 的访问类型。

代码示例:
定义一个派生类 DerivedPerson,从基类 Person 继承属性和方法 :

#include <iostream>
#include <string>
using namespace std;class Person {
public:string name;int age;void eat() {cout << name << " is eating.\n";}
};class DerivedPerson : public Person {
public:void work() {cout << name << " is working.\n";}
};int main() {DerivedPerson p;p.name = "John";p.age = 30;p.eat(); // 输出 "John is eating."p.work(); // 输出 "John is working."return 0;
}

2 公有继承和私有继承

C++ 中有三种类型的继承:公有继承、保护继承和私有继承。
公有继承是最常用的继承类型之一。在公有继承中派生类获得了基类的公有成员和保护成员,但是基类的私有成员不会被派生类直接访问。

公有继承的用法 示例:
代码定义一个基类 Base 和一个派生类 Derived
在Derived中使用公有继承来继承 Base类的属性和方法。在 main() 函数中创建一个Derived对象d,然后调用 set() 和 print() 函数来设置和获取成员变量 x 的值。需要注意的是此处通过派生类的对象访问了基类的公有成员 x 和 print()

#include <iostream>
using namespace std;class Base {
public:int x;void print() {cout << "Base class: x = " << x << "\n";}
};class Derived : public Base {
public:void set(int n) {x = n;}
};int main() {Derived d;d.set(10);d.print(); // 输出 "Base class: x = 10"return 0;
}

除了公有继承C++ 中还有保护继承和私有继承。
在保护继承中,基类的公有成员和保护成员都成为了派生类的保护成员。
在私有继承中,基类的公有成员和保护成员都成为了派生类的私有成员。
挑选合适的继承类型对于代码的设计非常重要,在实际编码中需要仔细考虑。

三、访问控制

1 派生类对基类成员的访问权限

在C++中如果一个类派生自另一个类,则派生类可以继承基类的成员变量和成员函数。在继承过程中访问权限是非常重要的一个问题。
有三种访问权限:public、protected 和 private。当一个类派生自另一个类时继承的访问权限可以通过下列三种方式指定:

  • public 继承:基类的 public 成员在派生类中保持 public 属性,基类的 protected 成员在派生类中保持 protected 属性,基类的 private 成员在派生类中不可访问。
  • protected 继承:基类的 public 和 protected 成员在派生类中都被保护起来,不能被外部直接访问。这种方式很少使用,一般只有在继承链比较复杂的时候才使用。
  • private 继承:基类的 public 和 protected 成员在派生类中均变为 private 属性,不能被外部直接访问。这种方式也比较少使用,一般只有在需要从多个类中继承同一个基类的时候才使用。

下面是一个使用 public 继承的例子,演示了派生类对基类成员的访问权限:

在代码中Derived类继承了Base类的公有成员变量 x 和成员函数 print(),以及保护成员变量y
在Derived类的成员函数中可以访问这些成员但是不能直接访问 Base 类的私有成员z

#include <iostream>
using namespace std;class Base {
public:int x;void print() {cout << "Base class: x = " << x << "\n";}
protected:int y;
private:int z;
};class Derived : public Base {
public:void set(int a, int b, int c) {x = a;y = b;// z = c; 无法访问基类的私有成员}void print1() {cout << "Derived class: x = " << x << "\n";}void print2() {cout << "Derived class: y = " << y << "\n";}// void print3() { cout << "Derived class: z = " << z << "\n"; } 无法访问基类的私有成员
};int main() {Derived d;d.set(1, 2, 3);d.print(); // 输出 "Base class: x = 1"// cout << d.y; 无法访问基类的保护成员d.print1(); // 输出 "Derived class: x = 1"d.print2(); // 输出 "Derived class: y = 2"return 0;
}

2 如何控制访问权限

在C++中可以使用访问修饰符来控制类的成员的访问权限。访问修饰符可以放到类的定义中的任何位置,而且多次使用访问修饰符是合法的,因为后面的修饰符会覆盖之前的修饰符。

  • public:公有修饰符可以从类的任何地方访问公有成员也可以从派生类的任何地方访问公有成员。
  • protected:保护修饰符只能从类的内部或派生类的内部访问保护成员外部不能访问。
  • private:私有修饰符只能从类的内部访问私有成员派生类的内部不能访问。

下面是一个使用访问修饰符的例子,演示了如何控制成员的访问权限:

在代码中定义了MyClass类和MyDerivedClass类。
在MyClass中使用 public、protected 和 private 三种访问修饰符定义了三个不同的成员变量。在MyDerivedClass中从MyClass中继承了各种成员,并在成员函数中分别访问了这些成员。需要注意的是,在派生类中可以访问基类的 public 和 protected 成员,但是不能访问基类的 private 成员。

#include <iostream>
using namespace std;class MyClass {
public:int a;          // 公有成员变量
protected:int b;          // 保护成员变量
private:int c;          // 私有成员变量public:void set(int x, int y, int z) {a = x;b = y;c = z;}void print() {cout << "a = " << a << ", b = " << b << ", c = " << c << "\n";}
};class MyDerivedClass : public MyClass {
public:void setDerived(int x, int y, int z) {// a 和 b 都是 public 和 protected 成员,可以直接访问a = x;b = y;// c 是 private 成员,无法直接访问//c = z;}void printDerived() {// a 是 public 成员,可以直接访问cout << "a = " << a << "\n";// b 是 protected 成员,在派生类的内部可以访问cout << "b = " << b << "\n";// c 是 private 成员,无法直接访问//cout << "c = " << c << "\n";}
};int main() {MyClass obj;obj.set(1, 2, 3);obj.print();MyDerivedClass obj2;obj2.setDerived(4, 5, 6);obj2.printDerived();return 0;
}

四、虚函数继承

1 继承中的虚函数机制

在C++ 中如果一个类有虚函数,那么当这个类被继承时在派生类中也会有虚函数表 (V-Table)。当使用基类指针或引用指向一个派生类对象时会根据虚函数表找到正确的虚函数。这种机制被称为动态联编,也就是运行时多态或后期绑定。

代码中定义了一个Base 类和一个 Derived 类。在 Base 类中有一个虚函数 print()
Derived类继承了Base 类并重写了 print() 函数。在 main() 函数中创建了 Derived 类的对象 d并使用 Base 类的指针 b_ptr 指向它,然后调用 print() 函数输出了 "Derived"

#include <iostream>
using namespace std;class Base {
public:virtual void print() {cout << "Base\n";}
};class Derived : public Base {
public:void print() override {cout << "Derived\n";}
};int main() {Derived d;Base* b_ptr = &d;b_ptr->print(); // Derivedreturn 0;
}

2 在派生类中重写虚函数

派生类可以通过重写基类中的虚函数来改变其行为

在代码中使用了上一个示例中的 Base 类和 Derived 类并在 Derived 类中重写了print() 函数
main() 函数中通过Base类和Derived类的指针调用 print() 函数。当使用 Base 类指针 b_ptr2 指向 Base 类的对象时,调用的是 Base 类中的 print() 函数,输出了 "Base"。当使用 Base 类指针 b_ptr 指向 Derived 类的对象时,调用的是 Derived 类中的 print() 函数,输出了 "Derived"

#include <iostream>
using namespace std;class Base {
public:virtual void print() {cout << "Base\n";}
};class Derived : public Base {
public:void print() override {cout << "Derived\n";}
};int main() {Derived d;Base* b_ptr = &d;b_ptr->print(); // DerivedBase base;Base* b_ptr2 = &base;b_ptr2->print(); // Basereturn 0;
}

五、多重继承

在C++ 中一个派生类可以同时继承多个基类,这种继承方式被称为多重继承。

1 语法实现

多重继承是指一个派生类可以同时继承多个基类,其中每个基类可以有自己的成员函数和成员变量。一个类可以声明对多个基类的继承方式语法如下:

class Derived : access-specifier Base1, access-specifier Base2, ... {// Derived class members and functions
};

其中,access-specifier 指定了基类成员在派生类中的访问权限,可以用 publicprotectedprivate 指定。如果没有指定,则默认为 private

2 派生类和基类成员的访问规则

在多继承中派生类对象同时包含多个基类对象的数据成员和成员函数。根据继承方式的不同,基类成员的访问规则也会有所不同。

2.1 共同基类的继承

如果多个基类都派生自同一个基类,那么这个基类的数据成员和成员函数只会在派生类中出现一次。在访问这个基类的成员时,将使用这个共同基类的数据成员和成员函数。

代码示例:
在代码中创建了 AnimalMammalDogCatPersianCat 类。
MammalCat 都是直接继承自 AnimalDogPersianCat 分别是直接继承自 MammalCat。在 PersianCat 类中同时使用了 CatMammal 作为基类

main() 函数中创建了一个 PersianCat 对象,并调用了它的 show_info() 函数。在这个函数中调用了 AnimalMammalCat 这三个基类的一些成员函数,它们分别是在不同的层级中定义的。由于它们都继承自共同的基类 Animal,因此在访问这个基类的成员时,它们都使用同一个 Animal 对象的数据成员和成员函数。在控制台输出中可以看到不同的动物都调用了正确的成员函数

#include <iostream>
using namespace std;class Animal {
public:void eat() {cout << "Animal is eating\n";}
};class Mammal : public Animal {
public:void run() {cout << "Mammal is running\n";}
};class Dog : public Mammal {
public:void bark() {cout << "Dog is barking\n";}
};class Cat : public Animal {
public:void meow() {cout << "Cat is meowing\n";}
};class PersianCat : public Cat, public Mammal {
public:void show_info() {eat(); // 使用Animal中的eat()函数run(); // 使用Mammal中的run()函数meow(); // 使用Cat中的meow()函数cout << "A Persian cat\n";}
};int main() {PersianCat persian_cat;persian_cat.show_info(); return 0;
}

2.2 不同基类的继承

当派生类继承了多个不同的基类时,会出现基类成员的二义性,需要使用作用域解析运算符来解决。

代码示例:
代码中创建了 ABC 类。C 类同时继承自 AB。在 C 类的 show_info() 函数中,我们使用作用域解析运算符 :: 来指定调用哪个基类的 func() 函数。在 main() 函数中,我们创建了 C 对象,并调用了它的 show_info() 函数。在控制台输出中可以看到它分别调用了两个基类的 func() 函数。

#include <iostream>
using namespace std;class A {
public:void func() {cout << "class A" << endl;}
};class B {
public:void func() {cout << "class B" << endl;}
};class C : public A, public B {
public:void show_info() {A::func();B::func();}
};int main() {C c;c.show_info(); // class A \n class Breturn 0;
}

六、小结回顾

C++中访问控制通过 public、protected 和 private 关键字来实现。public 表示公开的可以被任何地方访问。protected 表示受保护的只有本类和其子类可以访问。private 表示私有的只有本类可以访问。

C++中的虚函数继承可以实现多态性。子类可以重载父类的虚函数,并且子类的对象可以看做是父类的对象从而实现多态性。

多重继承是 C++ 中的一个重要特性允许一个类从多个基类中继承属性和方法。在实现上如果一个类同时继承了多个基类就可能出现菱形继承问题。为了避免这种问题C++ 提供了虚继承来解决。虚继承可以实现让派生类只继承一份基类的数据成员,从而避免数据冗余和数据不一致的问题。


http://www.ppmy.cn/news/61723.html

相关文章

2个月快速通过PMP证书的经验分享

01 PMP证书是什么&#xff1f; 指的是项目管理专业人士资格认证。它是由美国项目管理协会&#xff08;Project Management Institute(简称PMI)&#xff09;发起的&#xff0c;严格评估项目管理人员知识技能是否具有高品质的资格认证考试。其目的是为了给项目管理人员提供统一的…

FFmpeg HEVC 解码 YUV

1. 概要与流程图 1.1 FFmpeg 支持 h264,hevc 等解码,由于分离视频文件为 hevc 格式,为了方便起见,当前解码的格式为 hevc,代码支持各种视频格式解码,需要修改参数和适配 1.2 HEVC 解码 YUV 流程图如下: 2. 封装读写文件操作 2.1 读写头文件,FileTool.h #import <Fou…

Handler

背景 1、在日常Android研发工作中&#xff0c;经常用到Handler&#xff0c;但对其原理不熟悉。 目的 理解Handler 的工作原理 知识说明 polling&#xff1a;轮询idle&#xff1a;闲置Threshold&#xff1a;阈值Dispatch&#xff1a;分发Asynchronous&#xff1a;异步的syn…

cmd命令(dos命令)大全基础笔记

1、基本命令 ipconfig /all 查看本地网络配置 ipconfig /release 释放ip ipconfig /renew 重新获得ip ifconfig /flushdns 清空dns解析器缓存 ping ip地址 -t 一直ping tracert www.baidu.com 网络诊断工具 netstat -ano 查看侦听端口 netstat -ano | findstr ‘8080’检查8080…

Spring的事务原理

一、什么是事务&#xff1f; 事务是数据库操作的最小工作单元&#xff0c;是作为单个逻辑工作单元执行的一系列操作&#xff1b;这些操作作为一个整体一起向系统提交&#xff0c;要么都执行、要么都不执行&#xff1b;事务 是一组不可再分割的操作集合&#xff08;工作逻辑单元…

Android开机时间工具分析

背景 android 上面有很多的方法可以分析开机时间 比如打log&#xff0c;通过log 分析。android 的官网上面提供了下面的两种图形化的方式来分析开机时间&#xff0c;一些异常很明显的拖长整个开机时间的活动 可以很容易就看出来。 问题 android 官网和网上的教程很多都不适用于…

SPSS如何进行生存分析之案例实训?

文章目录 0.引言1.寿命表分析2.Kaplan-Meier分析方法3.Cox回归分析 0.引言 因科研等多场景需要进行数据统计分析&#xff0c;笔者对SPSS进行了学习&#xff0c;本文通过《SPSS统计分析从入门到精通》及其配套素材结合网上相关资料进行学习笔记总结&#xff0c;本文对生存分析进…

给表格中的文字设置字体以及设置表格样式

表格是一种常见的数据呈现方式&#xff0c;它可以将数据按照一定的规则排列&#xff0c;使得数据更加直观易懂。而表格中的文字是表格的重要组成部分&#xff0c;字体的选择和样式的设置对于表格的呈现效果至关重要。本文将为大家介绍如何设置表格中的文字字体以及表格样式。 一…