c++的三大特性之关于继承

news/2025/1/16 5:55:22/

目录

继承的概念及定义

基类和派生类对象赋值转换

继承中的作用域

派生类的默认成员函数

继承与友元,静态成员


继承的概念及定义

概念:
        继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。继承是类设计层次的复用

示例:

class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}protected:string _name = "Andy"; // 姓名int _age = 18;  // 年龄
};// 继承后父类Person的成员(成员函数+成员变量)都会变成子类的一部分。
class Student : public Person
{
protected:int _stuid; // 学号
};class Teacher : public Person
{
protected:int _jobid; // 工号
};

定义:Person是父类,也称作基类。Student是子类,也称作派生类

继承关系和访问限定符

继承基类成员访问方式的变化

总结:
  • 1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它

  • 2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的

  • 3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected> private。

  • 4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式

  • 5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强
建议使用:

基类和派生类对象赋值转换

        1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫 切片或者切割 。寓意把派生类中父类那部分切来赋值过去。
        2.基类对象不能赋值给派生类对象。
        3.基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(RunTime Type Information)的dynamic_cast 来进行识别后进行安全转换。

class Person
{
protected :string _name; // 姓名string _sex;  // 性别int _age; // 年龄
};class Student : public Person
{
public :int _No ; // 学号
};void Test ()
{Student sobj ;// 1.子类对象可以赋值给父类对象/指针/引用Person pobj = sobj ;Person* pp = &sobj;Person& rp = sobj;//2.基类对象不能赋值给派生类对象sobj = pobj;// 3.基类的指针可以通过强制类型转换赋值给派生类的指针pp = &sobjStudent* ps1 = (Student*)pp; // 这种情况转换时可以的。ps1->_No = 10;
}
注意:在公有继承下,子类赋值给基类是天然支持的,不存在隐式类型转换

继承中的作用域

1. 在继承体系中 基类 派生类 都有 独立的作用域
2. 子类和父类中有同名成员, 子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏, 也叫重定义。 (在子类成员函数中,可以 使用基类 :: 基类成员显示访问

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
4. 注意在实际中在 继承体系里 面最好 不要定义同名的成员

派生类的默认成员函数(很重要,画圈圈)

“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类中,这几个成员函数是如何生成的呢?
  • 1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。

  • 2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

  • 3. 派生类的operator=必须要调用基类的operator=完成基类的复制。

  • 4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。

  • 5. 派生类对象初始化先调用基类构造再调派生类构造。

  • 6. 派生类对象析构清理先调用派生类析构再调基类的析构

  • 7. 因为一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。

    

class Person
{
public:Person(const char* name = "Andy"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person & p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};class Student : public Person
{
public:Student(const char* name, int num)//:_name(name)  error:Person(name) //需要显示调用,_num(num){cout << "Student()" << endl;}//这里调用父类的拷贝构造需要传过去一个父类对象//那么如何拿到一个父类的对象呢? ---  切片Student(const Student& s):Person(s),_num(s._num){cout << "Student(const Student& s)" << endl;}Student operator=(const Student& s){if (this != &s){//operator=(s); //这里构成隐藏Person::operator=(s);_num = s._num;}cout << "Student operator=(const Student& s)" << endl;return *this;}//由于多态的原因,析构函数会被处理成destructor//destructor(){}~Student()  //{//Person::~Person();  //这里构成隐藏cout << "~Student()" << endl;}//注:子类的析构函数完成时,会自动调用父类析构,保障先析构子,在析构父protected:int _num; //学号
};int main()
{Student s("Andy",18);//Student s1(s);//Person p = s;//s = s1;return 0;
}

继承与友元,静态成员

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
class Student;
class Person
{
public:friend void Display(const Person& p, const Student& s);
protected:string _name; // 姓名
};class Student : public Person
{
protected:int _stuNum; // 学号
};void Display(const Person& p, const Student& s)
{cout << p._name << endl;cout << s._stuNum << endl;
}void main()
{Person p;Student s;Display(p, s);
}

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。
class Person
{
public:Person() { ++_count; }
public:string _name; // 姓名public:static int _count; // 统计人的个数。
};
int Person::_count = 0;class Student : public Person
{
protected:int _stuNum; // 学号
};class Graduate : public Student
{
protected:string _seminarCourse; // 研究科目
};int main()
{Person p1;Student s1;cout << &p1._name << endl;cout << &s1._name << endl;cout << &p1._count << endl;cout << &s1._count << endl;Graduate g1;Graduate g2;cout << Person::_count << endl;cout << Graduate::_count << endl;//父类静态成员子类共享return 0;
}


以上仅供参考,欢迎讨论

 


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

相关文章

尚硅谷2023版Promise教程从入门到实战(讲师:任安萍)

一、前言&#xff1a;为什么会出现Promise? Promise的重要性我认为没有必要多讲&#xff0c;概括起来说就是五个字&#xff1a;必&#xff01;须&#xff01;得&#xff01;掌&#xff01;握&#xff01;。 而且还要掌握透彻&#xff0c;在实际的使用中&#xff0c;有非常多…

安装jupyter notebook,jupyter notebook的简单使用

借助anaconda安装jupyter notebook&#xff0c;先下载anaconda然后在Anaconda Prompt中输入命令&#xff1a; 输入"jupyter notebook",在默认浏览器中打开jupyter notebook。 输入"jupyter notebook --no-browser"&#xff0c;启动服务器&#xff0c;但不打…

【笔记】书生·浦语大模型实战营——第一课

群公告 1月3日*更新 第一次课程视频链接&#xff1a;https://www.bilibili.com/video/BV1Rc411b7ns/&#xff0c;第一次课程只需要记笔记&#xff0c;没有作业。第一次课程(1月3日)和第二次课程(1月5日)到本周末(1月7日)截止&#xff0c;笔记记录在 知乎/CSDN/Github 或者任何你…

pythonPandas三: 数据清洗和预处理

让我们通过几个案例来学习如何使用Pandas进行数据清洗和预处理&#xff0c;包括处理缺失值、异常值&#xff0c;进行数据转换和规范化&#xff0c;以及处理重复数据等操作。 处理缺失值&#xff1a; # 创建包含缺失值的DataFrame data {姓名: [张三, 李四, None, 赵六],年龄: …

ECMAScript和JavaScript之间关键的区别和差异

ECMAScript和JavaScript之间有一些关键的区别和差异。虽然它们经常被混为一谈&#xff0c;但它们并不完全相同。以下是两者之间的一些详细区别&#xff1a; 标准化&#xff1a;ECMAScript是一种由Ecma国际&#xff08;前身为欧洲计算机制造商协会&#xff09;通过ECMA-262标准…

Vue.js 3.4版本发布:解析速度提升2倍,双向绑定革新等新功能

引言 随着2024年的来临,Vue团队的领军人物Evan You宣布了Vue.js 3.4的发布。这个版本不仅仅是修复了一些bug,还带来了一些非常实用的新功能和性能提升。 解析速度提升2倍 这次更新中,Vue.js 3.4实现了解析速度的大幅提升。尤其是在构建模板和脚本的源代码映射时,单文件组…

Vue前端文字效果:如何让一段文本像是手动一个一个字打出来的

效果展示 自己做的AI聊天机器人界面&#xff0c;我觉得比微信还好看 由于这个前端略微复杂&#xff0c;下文用最简单的例子来展示&#xff1a; 分析需求 对于AI聊天工具的前端&#xff0c;如果AI生成的文本像是一个一个字打出来的&#xff0c;就会让AI看起来更像真的人&…

C++的魅力与奥秘:工作原理、特性及应用深度解析---探索其发展历程与运行机制

一、引言 C是一种功能强大的编程语言&#xff0c;它的诞生和发展历程与计算机科学的飞速进步密不可分。作为C语言的扩展&#xff0c;C不仅保留了C语言的高效性能&#xff0c;而且引入了面向对象编程的概念&#xff0c;为程序员提供了更丰富的工具和更高效的开发方式。面向对象编…