深入理解C++继承:访问控制、虚函数机制和多重继承详解

news/2024/11/30 7:58:10/

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/65184.html

相关文章

5月13日,今日信息差

1、国内首条悬挂式空中轨道列车武汉光谷空轨试乘发车空轨列车悬挂在单型轨道下方&#xff0c;将地面交通移至空中&#xff0c;属于立体交通。该列车最高运行时速60公里&#xff0c;最多能容纳220余人 2、菜鸟国际快递、五粮液和上海电气入选“中国品牌全球行”案例&#xff0c…

MYSQL相关之不常见变量、排序函数、JDBC数据库与Java连接

用户自定义变量 局部变量--->只在当前begin/end代码块中有效 sql 复制代码 create procedure add ( in a int, in b int ) begin declare c int default 0; set c a b; select c as c; end; 2.用户变量--->在客户端链接到数据库实例整个过程中用户变量都是有效的。…

Android14即将发布

行为变化&#xff1a;以 Android 14 或更高版本为目标平台的应用 与早期版本一样&#xff0c;Android 14 包含可能会影响应用的行为更改。以下行为更改仅适用于以 Android 14 或更高版本为目标平台的应用。如果您的应用以 Android 14 或更高版本为目标平台&#xff0c;则应修改…

设计一个可靠的自动化测试框架需要考虑哪些问题呢?

随着软件开发的日益普及&#xff0c;自动化测试框架逐渐成为了保障软件质量的必备工具。然而&#xff0c;如何设计一个可靠的自动化测试框架并不是一件简单的事情&#xff0c;需要考虑多方面的问题。本文将从需求分析、架构设计、测试用例编写等多个角度&#xff0c;介绍设计一…

【专题连载】基于5G+机器视觉的芯片检测解决方案

基于5G机器视觉的芯片检测解决方案 背景 机器视觉的价值体现在它能为工业生产带来产量的增加和产品质量的提升&#xff0c;并同时降低生产成本&#xff0c;推动了工业生产的快速发展&#xff0c;使工业生产企业真正从中受益。为了进一步压缩生产成本&#xff0c;工业控制的产…

MySQL基础(三十)PowerDesigner的使用

1 PowerDesigner的使用 PowerDesigner是一款开发人员常用的数据库建模工具&#xff0c;用户利用该软件可以方便地制作 数据流程图 、概念数据模型 、 物理数据模型&#xff0c;它几乎包括了数据库模型设计的全过程&#xff0c;是Sybase公司为企业建模和设计提供的一套完整的集…

大学毕业设计使用python制作

前言&#xff1a;相信看到这篇文章的小伙伴都或多或少有一些编程基础&#xff0c;懂得一些linux的基本命令了吧&#xff0c;本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python&#xff1a;一种编程语言&…

计算机毕业论文内容参考|基于三维建模和卷积神经网络的人脸转正的技术设计

文章目录 导文文章重点摘要前言绪论课题背景国内外现状与趋势课题内容相关技术与方法介绍技术分析技术设计人脸转正方法卷积神经网络的训练和优化数据预处理技术实现总结与展望本文总结导文 基于java开发汽车销售系统资料 文章重点 摘要 在实际应用中,人脸图像往往具有旋转、…