继承和派生知识点总结 C++程序设计与算法笔记总结(四) 北京大学 郭炜(一)

简介: 继承和派生知识点总结 C++程序设计与算法笔记总结(四) 北京大学 郭炜(一)

继承和派生

在C++中,继承和派生是面向对象编程的两个重要概念,用于实现类与类之间的关系。

继承是指一个类可以从另一个类中继承属性和方法,并且可以在此基础上扩展出自己的属性和方法。被继承的类称为基类(父类),继承的类称为派生类(子类)。在C++中,可以通过以下方式定义一个派生类:

class DerivedClass : public BaseClass {
    // 派生类的成员变量和成员函数
};

在上面的示例中,DerivedClass是派生类,BaseClass是基类。关键字public表示使用公有继承,表示DerivedClass继承了BaseClass的所有public和protected成员,但不继承BaseClass的private成员。

派生类可以访问基类的public和protected成员,但不能访问基类的private成员。当派生类的成员变量或成员函数与基类的成员变量或成员函数同名时,可以使用作用域解析运算符::来指定调用哪个类的成员。

在派生类中,可以通过以下方式调用基类的构造函数:

class DerivedClass : public BaseClass {
public:
    DerivedClass(int x, int y, int z) : BaseClass(x, y), m_z(z) {}
private:
    int m_z;
};

在上面的示例中,调用了BaseClass的构造函数,并将参数x和y传递给它。

派生类中还可以重写(override)基类的成员函数,即在派生类中重新定义一个和基类相同名称、参数列表和返回类型的成员函数。在调用派生类的成员函数时,会优先调用派生类中的函数,如果派生类中没有定义相应的函数,则会调用基类的函数。

继承和派生是面向对象编程的重要概念,可以实现代码的复用和扩展。在使用继承和派生时,需要注意类之间的关系,避免出现循环继承等问题。同时,需要注意访问权限的控制,避免对private成员的直接访问。

需要继承机制的例子

继承机制可以实现代码的复用和扩展,下面是一些需要继承机制的例子:

  1. 图形类的继承
    在图形类中,可以定义一个基类Shape,包括图形的公共属性和方法,如颜色、位置、面积、周长等。然后可以通过继承机制定义各种具体的图形类,如矩形类、圆形类、三角形类等。这样,可以避免在每个具体的图形类中都定义公共的属性和方法,提高了代码的复用性。
  2. 汽车类的继承
    在汽车类中,可以定义一个基类Vehicle,包括汽车的公共属性和方法,如品牌、型号、颜色、速度、加速度等。然后可以通过继承机制定义各种具体的汽车类,如轿车类、越野车类、卡车类等。这样,可以避免在每个具体的汽车类中都定义公共的属性和方法,提高了代码的复用性。
  3. 员工类的继承
    在员工类中,可以定义一个基类Employee,包括员工的公共属性和方法,如姓名、性别、年龄、职位、工资等。然后可以通过继承机制定义各种具体的员工类,如经理类、销售员类、工人类等。这样,可以避免在每个具体的员工类中都定义公共的属性和方法,提高了代码的复用性。

继承机制可以极大地提高代码的复用性和可维护性,同时也可以实现代码的扩展。在使用继承机制时,需要注意类之间的关系,选择适当的继承方式,避免出现循环继承等问题。同时,需要注意访问权限的控制,避免对private成员的直接访问。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AtqB4lzN-1687179456360)(2023-06-19-20-36-13.png)]

派生类的写法

在C++中,可以通过继承来创建派生类。派生类继承了基类的所有成员函数和成员变量,并且可以在此基础上扩展出自己的成员函数和成员变量。

派生类的定义方式如下:

// 基类
class BaseClass {
public:
    int m_varBase;
    void funcBase();
};
// 派生类
class DerivedClass : public BaseClass {
public:
    int m_varDerived;
    void funcDerived();
};

在上面的代码中,DerivedClass是派生类,BaseClass是基类。关键字public表示使用公有继承,表示DerivedClass继承了BaseClass的所有public和protected成员,但不继承BaseClass的private成员。

当派生类的成员变量或成员函数与基类的成员变量或成员函数同名时,可以使用作用域解析运算符::来指定调用哪个类的成员。例如,可以通过DerivedClass::m_varBase来访问基类中的成员变量m_varBase。

派生类中可以重写(override)基类的成员函数,即在派生类中重新定义一个和基类相同名称、参数列表和返回类型的成员函数。在调用派生类的成员函数时,会优先调用派生类中的函数,如果派生类中没有定义相应的函数,则会调用基类的函数。

在派生类中,可以通过以下方式调用基类的构造函数:

class DerivedClass : public BaseClass {
public:
    DerivedClass(int x, int y, int z) : BaseClass(x, y), m_varDerived(z) {}
private:
    int m_varDerived;
};

在上面的代码中,调用了BaseClass的构造函数,并将参数x和y传递给它。

继承和派生是面向对象编程的重要概念,可以实现代码的复用和扩展。在使用继承和派生时,需要注意类之间的关系,避免出现循环继承等问题。同时,需要注意访问权限的控制,避免对private成员的直接访问。

派生类对象的内存空间

派生类对象在内存中的空间由两部分组成:基类部分和派生类部分。

基类部分是从基类中继承而来的,派生类对象中包含了基类对象的完整副本。在内存中,基类对象的成员变量和成员函数的布局与基类定义的布局相同。

派生类部分是派生类自己定义的成员变量和成员函数,它们被添加到了基类对象的末尾。派生类的成员变量和成员函数的布局与派生类定义的布局相同。

由于派生类对象包含了基类对象的完整副本,因此可以通过派生类对象访问基类对象中定义的成员变量和成员函数。同时,由于基类部分和派生类部分在内存中是连续的,因此派生类对象可以强制转换为基类对象的指针或引用,并且可以在基类对象的范围内使用。

例如,假设有如下的基类和派生类:

class Base {
public:
    int m_varBase;
    void funcBase() {}
};
class Derived : public Base {
public:
    int m_varDerived;
    void funcDerived() {}
};

那么,派生类对象在内存中的布局如下图所示:

|<-- 基类部分 -->|<-- 派生类部分 -->|
| m_varBase       | m_varDerived      |
| funcBase()      | funcDerived()     |

可以看到,派生类对象中包含了基类对象的完整副本,基类部分和派生类部分在内存中是连续的。

需要注意的是,在派生类中访问基类成员时,需要使用作用域解析运算符::来指定基类成员的名称和访问权限,否则可能会产生二义性。例如,可以使用Base::m_varBase来访问基类中的成员变m_varBase。

继承和派生是面向对象编程的重要概念,可以实现代码的复用和扩展。在使用继承和派生时,需要注意类之间的关系,避免出现循环继承等问题。同时,需要注意访问权限的控制,避免对private成员的直接访问。

示例程序:学籍管理

下面是一个简单的学籍管理的实例程序,展示了如何使用继承来实现不同类型的学生对象。

#include <iostream>
#include <string>
using namespace std;
// 基类:学生
class Student {
public:
    Student(string name, int age, string gender)
        : m_name(name), m_age(age), m_gender(gender) {}
    void display() {
        cout << "姓名:" << m_name << endl;
        cout << "年龄:" << m_age << endl;
        cout << "性别:" << m_gender << endl;
    }
private:
    string m_name;
    int m_age;
    string m_gender;
};
// 派生类1:本科生
class Undergraduate : public Student {
public:
    Undergraduate(string name, int age, string gender, string major)
        : Student(name, age, gender), m_major(major) {}
    void display() {
        Student::display();
        cout << "专业:" << m_major << endl;
    }
private:
    string m_major;
};
// 派生类2:研究生
class Postgraduate : public Student {
public:
    Postgraduate(string name, int age, string gender, string research)
        : Student(name, age, gender), m_research(research) {}
    void display() {
        Student::display();
        cout << "研究方向:" << m_research << endl;
    }
private:
    string m_research;
};
int main() {
    Student s1("张三", 20, "男");
    s1.display();
    Undergraduate s2("李四", 22, "女", "计算机科学与技术");
    s2.display();
    Postgraduate s3("王五", 25, "男", "计算机视觉");
    s3.display();
    return 0;

在上面的代码中,Student是基类,包含了学生的姓名、年龄和性别信息。Undergraduate是派生类1,继承了Student的属性,并添加了专业信息。Postgraduate是派生类2,继承了Student的属性,并添加了研究方向信息。在每个派生类中,都重写了基类的display()函数,以便输出自己的属性。

在主函数中,创建了一个基类对象s1,一个Undergraduate对象s2和一个Postgraduate对象s3,分别输出了它们的属性。

继承可以实现代码的复用和扩展,使得程序更加灵活和可维护。在使用继承时,需要注意类之间的关系,选择适当的继承方式,避免出现循环继承等问题。同时,需要注意访问权限的控制,避免对private成员的直接访问。

继承关系&复合关系

继承关系和复合关系是面向对象编程中两种不同的关系。它们分别用于描述不同的对象之间的关系和组合方式。

继承关系是一种"is-a"的关系,用于描述一个类是另一个类的一种特殊形式。在继承关系中,子类继承了父类的属性和方法,并且可以在此基础上添加自己的属性和方法,从而实现代码的复用和扩展。例如,可以定义一个Animal类作为基类,然后定义Dog类和Cat类作为Animal类的子类,从而实现复用和扩展。

复合关系是一种"has-a"的关系,用于描述一个类包含另一个类的对象。在复合关系中,一个类实例化了另一个类的对象,并将其作为自己的成员变量使用。例如,可以定义一个Car类,包含了多个Wheel类对象,从而实现复杂的组合关系。

虽然继承关系和复合关系都可以用于实现代码的复用和扩展,但它们的应用场景不同。继承关系适用于描述"is-a"的关系,即一个类是另一个类的一种特殊形式;而复合关系适用于描述"has-a"的关系,即一个类包含另一个类的对象。

需要注意的是,在使用继承和复合时,需要考虑类之间的耦合性问题。继承关系会使得子类与父类之间产生紧密的耦合关系,一旦父类发生改变,子类也需要相应地进行修改。而复合关系则可以降低类之间的耦合度,使得类之间更加独立和灵活。

类之间的两种关系是继承关系和组合关系。

下面分别给出继承关系和组合关系的代码实例。

继承关系:

#include <iostream>
#include <string>
using namespace std;
// 基类:人
class Person {
public:
    Person(string name, int age) : m_name(name), m_age(age) {}
    void display() {
        cout << "姓名:" << m_name << endl;
        cout << "年龄:" << m_age << endl;
    }
private:
    string m_name;
    int m_age;
};
// 派生类:学生
class Student : public Person {
public:
    Student(string name, int age, string school) : Person(name, age), m_school(school) {}
    void display() {
        Person::display();
        cout << "学校:" << m_school << endl;
    }
private:
    string m_school;
};
int main() {
    Person p1("李四", 20);
    p1.display();
    Student s1("张三", 18, "清华大学");
    s1.display();
    return 0;
}

在上面的代码中,Person是基类,Student是派生类。Student继承了Person的属性和方法,并添加了自己的属性m_school。在Student中,重写了基类的display()函数,以便输出自己的属性。

组合关系:

#include <iostream>
#include <string>
using namespace std;
// 基类:轮胎
class Tyre {
public:
    Tyre(int size) : m_size(size) {}
    void display() {
        cout << "轮胎尺寸:" << m_size << endl;
    }
private:
    int m_size;
};
// 派生类:汽车
class Car {
public:
    Car(string brand, int size) : m_brand(brand), m_tyre(size) {}
    void display() {
        cout << "品牌:" << m_brand << endl;
        m_tyre.display();
    }
private:
    string m_brand;
    Tyre m_tyre;
};
int main() {
    Car c1("宝马", 18);
    c1.display();
    return 0;
}

在上面的代码中,Tyre是基类,Car是派生类。Car包含了一个Tyre对象m_tyre,从而实现了复杂的组合关系。在Car中,重写了自己的display()函数,以便输出自己的属性和包含的Tyre对象的属性。

需要注意的是,在使用继承和组合时,需要考虑类之间的耦合性问题。继承关系会使得子类与父类之间产生紧密的耦合关系,一旦父类发生改变,子类也需要相应地进行修改。而组合关系则可以降低类之间的耦合度,使得类之间更加独立和灵活。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bMjIzwXI-1687179456362)(2023-06-19-20-43-47.png)]

复合关系的使用

复合关系是面向对象编程中的一种关系,指一个类对象包含了另一个类对象,用于描述对象之间的组合关系。

下面以一个简单的图形类作为例子,说明复合关系的使用。

#include <iostream>
#include <string>
using namespace std;
// 点类
class Point {
public:
    Point(int x, int y) : m_x(x), m_y(y) {}
private:
    int m_x;
    int m_y;
};
// 图形类
class Shape {
public:
    Shape(string type, int width, int height, int x, int y) 
        : m_type(type), m_width(width), m_height(height), m_point(x, y) {}
    void display() {
        cout << "图形类型:" << m_type << endl;
        cout << "宽度:" << m_width << endl;
        cout << "高度:" << m_height << endl;
        cout << "位置:" << m_point.m_x << ", " << m_point.m_y << endl;
    }
private:
    string m_type;
    int m_width;
    int m_height;
    Point m_point; // 复合关系
};
int main() {
    Shape s("矩形", 100, 50, 10, 20);
    s.display();
    return 0;
}

在上述代码中,Point类表示坐标点,Shape类表示图形类,包含了图形类型、宽度、高度和位置信息,其中位置信息通过复合关系包含了一个Point对象。在Shape类中,定义了display()函数以便输出各个属性。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3e52S4Z1-1687179456362)(2023-06-19-20-46-31.png)]

复合关系的使用

 正确的写法:
为“狗”类设一个“业主”类的对象指针;
为“业主”类设一个“狗”类的对象指针数组。
class CMaster; //CMaster必须提前声明,不能先
//写CMaster类后写Cdog类
class CDog {
CMaster * pm;
};
class CMaster {
CDog * dogs[10];
};

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1uNQjjlq-1687179456363)(2023-06-19-20-47-14.png)]

目录
相关文章
|
16天前
|
存储 负载均衡 算法
基于 C++ 语言的迪杰斯特拉算法在局域网计算机管理中的应用剖析
在局域网计算机管理中,迪杰斯特拉算法用于优化网络路径、分配资源和定位故障节点,确保高效稳定的网络环境。该算法通过计算最短路径,提升数据传输速率与稳定性,实现负载均衡并快速排除故障。C++代码示例展示了其在网络模拟中的应用,为企业信息化建设提供有力支持。
42 15
|
15天前
|
存储 算法 数据处理
公司局域网管理中的哈希表查找优化 C++ 算法探究
在数字化办公环境中,公司局域网管理至关重要。哈希表作为一种高效的数据结构,通过哈希函数将关键值(如IP地址、账号)映射到数组索引,实现快速的插入、删除与查找操作。例如,在员工登录验证和设备信息管理中,哈希表能显著提升效率,避免传统线性查找的低效问题。本文以C++为例,展示了哈希表在局域网管理中的具体应用,包括设备MAC地址与IP分配的存储与查询,并探讨了优化哈希函数和扩容策略,确保网络管理高效准确。
|
15天前
|
安全 C++
【c++】继承(继承的定义格式、赋值兼容转换、多继承、派生类默认成员函数规则、继承与友元、继承与静态成员)
本文深入探讨了C++中的继承机制,作为面向对象编程(OOP)的核心特性之一。继承通过允许派生类扩展基类的属性和方法,极大促进了代码复用,增强了代码的可维护性和可扩展性。文章详细介绍了继承的基本概念、定义格式、继承方式(public、protected、private)、赋值兼容转换、作用域问题、默认成员函数规则、继承与友元、静态成员、多继承及菱形继承问题,并对比了继承与组合的优缺点。最后总结指出,虽然继承提高了代码灵活性和复用率,但也带来了耦合度高的问题,建议在“has-a”和“is-a”关系同时存在时优先使用组合。
58 6
|
25天前
|
存储 监控 算法
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
在数字化办公时代,公司监控上网软件成为企业管理网络资源和保障信息安全的关键工具。本文深入剖析C++中的链表数据结构及其在该软件中的应用。链表通过节点存储网络访问记录,具备高效插入、删除操作及节省内存的优势,助力企业实时追踪员工上网行为,提升运营效率并降低安全风险。示例代码展示了如何用C++实现链表记录上网行为,并模拟发送至服务器。链表为公司监控上网软件提供了灵活高效的数据管理方式,但实际开发还需考虑安全性、隐私保护等多方面因素。
23 0
公司监控上网软件架构:基于 C++ 链表算法的数据关联机制探讨
|
2月前
|
C++ 开发者
C++学习之继承
通过继承,C++可以实现代码重用、扩展类的功能并支持多态性。理解继承的类型、重写与重载、多重继承及其相关问题,对于掌握C++面向对象编程至关重要。希望本文能为您的C++学习和开发提供实用的指导。
69 16
|
2月前
|
编译器 数据安全/隐私保护 C++
【C++面向对象——继承与派生】派生类的应用(头歌实践教学平台习题)【合集】
本实验旨在学习类的继承关系、不同继承方式下的访问控制及利用虚基类解决二义性问题。主要内容包括: 1. **类的继承关系基础概念**:介绍继承的定义及声明派生类的语法。 2. **不同继承方式下对基类成员的访问控制**:详细说明`public`、`private`和`protected`继承方式对基类成员的访问权限影响。 3. **利用虚基类解决二义性问题**:解释多继承中可能出现的二义性及其解决方案——虚基类。 实验任务要求从`people`类派生出`student`、`teacher`、`graduate`和`TA`类,添加特定属性并测试这些类的功能。最终通过创建教师和助教实例,验证代码
72 5
|
2月前
|
存储 算法 测试技术
【C++数据结构——树】二叉树的遍历算法(头歌教学实验平台习题) 【合集】
本任务旨在实现二叉树的遍历,包括先序、中序、后序和层次遍历。首先介绍了二叉树的基本概念与结构定义,并通过C++代码示例展示了如何定义二叉树节点及构建二叉树。接着详细讲解了四种遍历方法的递归实现逻辑,以及层次遍历中队列的应用。最后提供了测试用例和预期输出,确保代码正确性。通过这些内容,帮助读者理解并掌握二叉树遍历的核心思想与实现技巧。
66 2
|
2月前
|
存储 算法 安全
基于哈希表的文件共享平台 C++ 算法实现与分析
在数字化时代,文件共享平台不可或缺。本文探讨哈希表在文件共享中的应用,包括原理、优势及C++实现。哈希表通过键值对快速访问文件元数据(如文件名、大小、位置等),查找时间复杂度为O(1),显著提升查找速度和用户体验。代码示例展示了文件上传和搜索功能,实际应用中需解决哈希冲突、动态扩容和线程安全等问题,以优化性能。
|
1月前
|
编译器 C++ 开发者
【C++篇】深度解析类与对象(下)
在上一篇博客中,我们学习了C++的基础类与对象概念,包括类的定义、对象的使用和构造函数的作用。在这一篇,我们将深入探讨C++类的一些重要特性,如构造函数的高级用法、类型转换、static成员、友元、内部类、匿名对象,以及对象拷贝优化等。这些内容可以帮助你更好地理解和应用面向对象编程的核心理念,提升代码的健壮性、灵活性和可维护性。
|
12天前
|
设计模式 安全 C++
【C++进阶】特殊类设计 && 单例模式
通过对特殊类设计和单例模式的深入探讨,我们可以更好地设计和实现复杂的C++程序。特殊类设计提高了代码的安全性和可维护性,而单例模式则确保类的唯一实例性和全局访问性。理解并掌握这些高级设计技巧,对于提升C++编程水平至关重要。
39 16