C++中类的继承

C++中类的继承

在C++中,类的继承是一种重要的面向对象编程机制,它允许一个类(称为子类或派生类)继承另一个类(称为基类或父类)的属性和方法。派生类可以继承基类的成员变量和成员函数,这使得代码能够复用基类已有的功能,减少重复代码的编写。
其中继承方式可以是公有继承(public)、保护继承(protected)或私有继承(private)。

代码示例

C++中类的继承格式如下:

cpp 复制代码
class 派生类名 : 继承方式 基类名

继承方式

公有继承(public) :当一个类使用public关键字进行继承时,基类的公有成员和保护成员在派生类中保持原有的访问级别,而基类的私有成员仍然保持私有。
保护继承(protected) :当一个类使用protected关键字进行继承时,基类的公有成员和保护成员在派生类中都成为保护成员,只能被派生类的成员函数或友元访问。
私有继承(private) :当一个类使用private关键字进行继承时,基类的公有成员和保护成员在派生类中都成为私有成员,无法被派生类的子类访问。

cpp 复制代码
//基类
class Base {
public:
    void pub_method() {} // 公有方法
protected:
    void prot_method() {} // 保护方法
private:
    void priv_method() {} // 私有方法
};
//公有继承
class Derived1 : public Base {
public:
    void use_base_method() {
        pub_method();    // 可以访问
        prot_method();   // 可以访问
        // priv_method(); // 错误:无法访问
    }
};
//保护继承
class Derived2 : protected Base {
public:
    void use_base_method() {
        pub_method();    // 可以访问
        prot_method();   // 可以访问
        // priv_method(); // 错误:无法访问
    }
};
//私有继承
class Derived3 : private Base {
public:
    void use_base_method() {
        pub_method();    // 可以访问
        prot_method();   // 可以访问
        // priv_method(); // 错误:无法访问
    }
};

访问情况

派生类可以访问基类的公有成员,被保护的成员和私有成员可以调用友元函数或使用指针间接访问。但是基类是不能直接访问派生类的任何一个成员的,不过可以通过友元函数的调用或使用指针间接访问。 因为派生类是在基类定义以后产生的。
派生类的子类能够访问派生类基类的成员函数和继承派生类基类的属性。

在C++中,当一个类从另一个类派生时,它继承了基类的所有公有和保护成员(私有成员不会被继承)。这意味着派生类的子类可以访问这些被继承的成员,但具体的访问权限取决于基类中成员的访问修饰符以及派生时的继承方式。

通过实例对象对成员函数的调用提示也可以发现访问机制

派生类子类的实例对象的访问机制

简单练手

功能需求

设计一个"(人员类)"基类。考虑到通用性,具有的属性:编号、姓名、性别、出生日期、身份证号等;

从(人员类)派生出(学生类),并添加属性:班级;

从(人员类)派生出(教师类),并添加属性:职务、部门;

从(学生类)派生出(研究生类),并添加属性:专业、导师(该属性是教师类对象);

从(研究生类)和(教师类)派生出(助教生类),无新的属性。

编写main函数测试这些类。

运行结果

代码

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

// 基类:人员类
class dly_Person {
protected:
    string id;
    string name;
    string gender;
    int age;
    string birthDate;
    string idNumber;
public:
    // 定义一个dly_Person类的构造函数。用于在创建类的实例对象时自动调用,初始化列表中的冒号后面的部分是成员变量的初始化列表。
    dly_Person(string id, string name, string gender, int age, string birthDate, string idNumber)
        : id(id), name(name), gender(gender), age(age), birthDate(birthDate), idNumber(idNumber) {}

    void display() {
        cout << "编号: " << id;
        cout << "   姓名: " << name;
        cout << "   性别: " << gender;
        cout << "   年龄: " << age;
        cout << "   出生日期: " << birthDate;
        cout << "   身份证号: " << idNumber << endl;
    }
    // 定义一个公有访问方法,用于类外访问被保护的name变量
    string getName() const {
        cout << "公有访问方法被调用" << endl;
        return name;
    }
};

// 派生类:学生类
class dly_Student : public dly_Person {
protected:
    string className;
public:
    dly_Student(string id, string name, string gender, int age, string birthDate, string idNumber, string className)
        : dly_Person(id, name, gender, age, birthDate, idNumber), className(className) {}
    // 类内定义一个成员函数display,用于显示学生的班级
    void display() {
        dly_Person::display();
        cout << "班级: " << className << endl;
    }
};

// 派生类:教师类
class dly_Teacher : public dly_Person {
protected:
    string position;
    string department;
public:
    dly_Teacher(string id, string name, string gender, int age, string birthDate, string idNumber, string position, string department)
        : dly_Person(id, name, gender, age, birthDate, idNumber), position(position), department(department) {}
    
    void display() {
        dly_Person::display();
        cout << "职务: " << position << endl;
        cout << "部门: " << department << endl;
    }
    // 声明友元函数  注意声明时 
    friend string teacher_Position(const dly_Teacher& teacher);
};

// 派生类:研究生类
class dly_Graduate : public dly_Student {
protected:
    string major;
    dly_Teacher advisor; // 导师,是教师类的对象
public:
    dly_Graduate(string id, string name, string gender, int age, string birthDate, string idNumber, string className, string major, dly_Teacher advisor)
        : dly_Student(id, name, gender, age, birthDate, idNumber, className), major(major), advisor(advisor) {}
    
    void display() {
        dly_Student::display();
        cout << "专业: " << major << endl;
        cout << "导师信息:" << endl;
        advisor.display();
    }
};

// 派生类:助教生类
class dly_Assistant : public dly_Graduate, public dly_Teacher {
public:
    dly_Assistant(string id, string name, string gender, int age, string birthDate, string idNumber, string className, string major, dly_Teacher advisor)
        : dly_Graduate(id, name, gender, age, birthDate, idNumber, className, major, advisor), dly_Teacher(id, name, gender, age, birthDate, idNumber, "", "") {}
    
    void display() {
        dly_Graduate::display();
    }
};

// 类外定义友元函数
/*
将 teacher_Position 函数的声明写为接受 const dly_Teacher& 类型参数,以确保传递的是常量引用,避免不必要的拷贝。
*/
string teacher_Position(const dly_Teacher& teacher) {
    cout << "友元函数访问导师职称" << endl;
    return teacher.position;
}

int main() {
    /* 当创建一个dly_Assistant类的对象ta时,构造函数的最后一个参数为teacher,即传递给它一个教师类实例对象。
    该教师类实例对象与研究生类实例对象关联在一起,因此可以通过研究生类实例对象来访问与之关联的教师类实例对象的信息。*/
    dly_Teacher teacher1("T001", "张老师", "男",54,"1970-05-15", "32010119700515", "教授", "计算机科学与技术学院");
    dly_Teacher teacher2("T002", "董老师", "女",54,"1970-06-15", "32010119700615", "副教授", "电子信息学院");
    dly_Graduate graduate("G001", "李明", "男",34,"1990-01-01", "32010119900101", "软件工程1班", "软件工程", teacher1);
    dly_Assistant zhujiao1_assistant("GA001", "王强", "男",24,"2000-10-10", "32010120201010", "软件工程1班", "软件工程", teacher1);
    dly_Teacher zhujiao1_teacher("GA001", "王强", "男",24,"2000-10-10", "32010120201010", "讲师", "计算机科学与技术学院");
    
    dly_Assistant zhujiao2_assistant("GA002", "李强", "男",22,"2002-11-10", "32010120021110", "软件工程2班", "软件工程", teacher2);
    dly_Teacher zhujiao2_teacher("TA002", "李强", "男",22,"2002-11-10", "32010120021110", "讲师", "计算机科学与技术学院");
    
    // 定义一个指针,用于访问导师的姓名(注意要在teacher的定义之后才能引用该实例对象的地址)  
    dly_Teacher* zhujiao2 = &teacher2;	
    
    cout << "教师信息:" << endl;
    teacher1.display();
    cout << endl;
    teacher1.display();
    cout<<"----------------------------------------------------"<<endl;
    cout<<endl;
    
    cout << "研究生信息:" << endl;
    graduate.display();
    cout << endl;
    cout<<"----------------------------------------------------"<<endl;  
    
    cout << "助教生1信息:" << endl;
    cout<<"作为研究生:"<<endl;
    zhujiao1_assistant.display();
    cout<<endl;
    cout<<"作为教师:"<<endl;
    zhujiao1_teacher.display() ;
    cout << endl;
    cout<<"----------------------------------------------------"<<endl;
    cout<<endl;
    
    
    cout << "助教生2信息:" << endl;
    cout<<"作为研究生:"<<endl;
    zhujiao2_assistant.display();
    //使用指针访问导师姓名  
    cout<<"导师姓名:"<<zhujiao2->getName()<<endl;  
    //使用友元函数访问导师职称  
    //在 main 函数中,调用 teacher_Position 时,传入的是解引用后的 zhujiao2 对象,而不是直接传入指针。这样可以避免编译错误。
    cout<<"导师职称:"<<teacher_Position(*zhujiao2)<<endl;  
    cout<<endl;
    
    cout<<"作为教师:"<<endl;
    zhujiao2_teacher.display();  
    cout<<endl;
    cout<<"----------------------------------------------------"<<endl;
    cout<<endl;
    
    return 0;
}

要点

1、从(学生类)派生出(研究生类),并添加属性:专业、导师(该属性是教师类对象);对应的程序如下:

cpp 复制代码
// student类的派生类:研究生类
class dly_Graduate : public dly_Student {
protected:
    string major;
    dly_Teacher advisor; // 导师,是教师类对象
public:
    dly_Graduate(string id, string name, string gender, int age, string birthDate, string idNumber, string className, string major, dly_Teacher advisor)
        : dly_Student(id, name, gender, age, birthDate, idNumber, className), major(major), advisor(advisor) {}
    
    void display() const {
        dly_Student::display();
        cout << "专业: " << major << endl;
        cout << "导师信息:" << endl;
        advisor.display();
    }
};

这里在dly_Graduate类中的保护机制下添加了导师advisor这个成员变量,并且是教师类的对象,这种写法可以减少后期对教师类的访问,提高了程序的安全性,同时也顺应了类的封装机制。

这里需要注意我们在dly_Graduate 类中的默认构造函数中进行了初始化列表,其中已经初始化advisor 这个成员变量;因此在后面给dly_Graduate 类的实例对象传参时,要将dly_Teacher 类的实例对象一起传入到dly_Graduate 类的实例对象中,否则 dly_Graduate 类中的advisor 成员变量将无法初始化。

cpp 复制代码
dly_Teacher teacher1("T001", "张老师", "男", 60, "1970-05-15", "320101197005156789", "教授", "计算机科学与技术学院");
dly_Graduate graduate1("G001", "李明", "男", 35, "1995-08-20", "320101199508201234", "软件工程1班", "软件工程", teacher1);
dly_Graduate graduate2("G002", "张泉", "男", 34, "1996-08-20", "320101199608201238", "软件工程2班", "软件工程", teacher1); 

如果没有传入teacher1,发生的报错如下:

2、在可以通过指针的方式访问类的成员变量,不过类的私有成员以及受保护的成员依然不可直接访问,可以通过修改私有成员或受保护的成员为public公有属性(一般不这样做,这样会降低类的安全性)。

推荐的方式有:在待访问的类中写一个公有方法,用于获取姓名信息,比如:

那么在类外使用指针的方式可以直接访问这个公有方法,由此访问被保护的name变量

注意指针一定是指向你要访问的类的实例对象的,否则会导致编译失败。这里要访问的是Teacher类,因此指针gral是Teacher类的指针,并指向teacher1的。

或者是写一个友元函数,在类外定义这个函数,在待访问类中用friend关键字设置友元函数。

cpp 复制代码
//类内声明友元函数
// 派生类:教师类
class dly_Teacher : public dly_Person {
protected:
    string position;
    string department;
public:
    dly_Teacher(string id, string name, string gender, int age, string birthDate, string idNumber, string position, string department)
        : dly_Person(id, name, gender, age, birthDate, idNumber), position(position), department(department) {}
    
    void display() {
        dly_Person::display();
        cout << "职务: " << position << endl;
        cout << "部门: " << department << endl;
    }
    // 声明友元函数
    friend string teacher_Position(const dly_Teacher& teacher);
};
//定义dly_Teacher类,指向teacher2的指针 zhujiao2
 dly_Teacher* zhujiao2 = &teacher2;	

//类外定义友元函数
string teacher_Position(const dly_Teacher& teacher) {
    cout << "友元函数访问导师职称" << endl;
    return teacher.position;
}

//调用友元函数
    cout<<"导师职称:"<<teacher_Position(*zhujiao2)<<endl;  
    cout<<endl;

这里需要注意的是友元函数的调用机制

一定要注意,类内是可以进行所有成员的访问的,在Teacher类内,被保护的成员变量是position,我需要在类外调用友元函数获取Teacher类position。这里友元函数所传入的参数一定要是Teacher类的实例对象,比如对友元函数的声明如下
friend string teacher_Position(const dly_Teacher& teacher);
又我原本是定义了一个指向实例对象teacher2的的指针,zhujiao2;

dly_Teacher* zhujiao2 = &teacher2;

因此我在调用时需要对zhujiao2这个指针解引用*zhujiao2 ,因为友元函数中接收的参数并非指针。

或者直接将teacher2作为作为参数调用友元函数,也是可以的。
实际上在调用友元函数时就是实现一个类的实例对象在该类内对成员变量的访问。并且在编写友元函数的参数时,一定都得是指向待访问类的实例对象

总结

以上就是对本次类的继承相关知识的记录,以后在学习过程中会不断更新遇到的知识点,如发现有不足之处,还请各位前辈指正,万分感谢!

相关推荐
捕鲸叉6 分钟前
C++软件设计模式之代理(Proxy)模式
c++·设计模式
code monkey.37 分钟前
【排序算法】—— 计数排序
c++·算法·排序算法
云青山水林38 分钟前
2024.12.21 周六
c++·算法·贪心算法
一只淡水鱼661 小时前
【mybatis】基本操作:详解Spring通过注解和XML的方式来操作mybatis
java·数据库·spring·mybatis
张声录11 小时前
【ETCD】【实操篇(十六)】基于角色的访问控制:ETCD 安全管理指南
数据库·安全·etcd
warrah1 小时前
redis——岁月云实战
数据库·redis·缓存
秀儿y1 小时前
Redis-十大数据类型
数据库·redis·缓存·oracle
Moweiii1 小时前
SDL3 GPU编程探索
c++·游戏引擎·图形渲染·sdl·vulkan
凡人的AI工具箱1 小时前
每天40分玩转Django:Django类视图
数据库·人工智能·后端·python·django·sqlite
路在脚下@1 小时前
MySQL的索引失效的原因有那些
数据库·mysql