当前位置: 首页 > news >正文

【C++】继承机制:面向对象编程的核心奥秘

1.继承的概念及定义

1.1 继承的概念

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

class Person 
{ 
public:  
void Print() {  cout << "name:" << _name << endl;  cout << "age:" << _age << endl; } 
protected: string _name = "peter"; // 姓名  int _age = 18;  // 年龄 
}; // 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了 
//Student和Teacher复用了Person的成员。下面我们使用监视窗口查看Student和Teacher对象,可 
//以看到变量的复用。调用Print可以看到成员函数的复用。 
class Student : public Person 
{ 
protected:  int _stuid; // 学号 
};class Teacher : public Person 
{ 
protected:  int _jobid; // 工号 
}; int main() 
{  Student s;  Teacher t; s.Print();  t.Print(); return 0; 
}

1.2 继承定义

1.2.1 定义格式

下面我们看到 Person 是父类,也称作基类。Student 是子类,也称作派生类

1.2.2 继承关系和访问限定符

总结:

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

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

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

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

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

// 实例演示三种继承关系下基类成员的各类型成员访问关系的变化      
class Person 
{ 
public :  void Print () {  cout<<_name <<endl; } 
protected : string _name ; // 姓名 
private :  int _age ; // 年龄 
}; 
//class Student : protected Person 
//class Student : private Person 
class Student : public Person 
{ 
protected :  int _stunum ; // 学号 
};

三种继承方式对基类成员访问权限的影响如下:

1.public(公有)继承:

        基类的 public 成员在派生类中仍为 public

        基类的 protected 成员在派生类中仍为 protected

        基类的 private 成员在派生类中不可访问

  在这种情况下,Student类可以:

  • 直接访问Person类的Print()方法和_name成员

  • 不能直接访问Person类的_age成员

  • 外部可以通过Student对象调用Print()方法】

2.protected(保护)继承:

        基类的 public 成员在派生类中变为 protected

        基类的 protected 成员在派生类中仍为 protected

        基类的 private 成员在派生类中不可访问

  这种情况下:

  • Student类内部仍可访问Print()方法和_name成员

  • 但外部不能通过Student对象调用Print()方法

  • 仍不能访问_age成员

3.private(私有)继承:

        基类的 public 成员在派生类中变为 private

        基类的 protected 成员在派生类中变为 private

        基类的 private 成员在派生类中不可访问

  这种情况下:

  • Student类内部仍可访问Print()方法和_name成员

  • 外部不能通过Student对象调用Print()方法

  • 如果Student类还有派生类,其派生类将无法访问从Person继承的任何成员

  • 仍不能访问_age成员

需要注意的是,基类的 private 成员无论使用哪种继承方式,在派生类中都是不可访问的,这体现了封装性的原则。

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

  • 派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。

  • 基类对象不能赋值给派生类对象。

  • 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用 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 = &sobj     Student* ps1 = (Student*)pp; // 这种情况转换时可以的。     ps1->_No = 10;          pp = &pobj;  Student* ps2 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题     ps2->_No = 10; 
}

3.继承中的作用域

1. 在继承体系中基类派生类都有独立的作用域

2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问

3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。

4. 注意在实际中在继承体系里面最好不要定义同名的成员

子类中和父类有同名函数(Func),构成重写关系(隐藏),如果要在外部(如 main 函数中)调用函数 Func,默认调用的是子类的函数,如果要调用父类中的 Func 要指明作用域,xxx::Func,有相同变量也是同样的道理

// Student的_num和Person的_num构成隐藏关系,可以看出这样代码虽然能跑,但是非常容易混淆 
class Person 
{ 
protected : string _name = "小李子"; // 姓名 int _num = 111;   // 身份证号 
}; class Student : public Person 
{ 
public:  
void Print() {  cout<<" 姓名:"<<_name<< endl;  cout<<" 身份证号:"<<Person::_num<< endl;  cout<<" 学号:"<<_num<<endl; } 
protected:  int _num = 999; // 学号 
}; void Test() 
{  Student s1;  s1.Print();  //打印出来的结果是999
};
// B中的fun和A中的fun不是构成重载,因为不是在同一作用域 
// B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。 
class A 
{ 
public:  void fun() {  cout << "func()" << endl; } 
}; class B : public A 
{ 
public:  void fun(int i) {  A::fun(); cout << "func(int i)->" <<i<<endl; } 
}; 
void Test()
{ B b;  b.fun(10); 
};


文章转载自:

http://1a79jTgA.nnwnL.cn
http://lVnyTqVy.nnwnL.cn
http://9S1XbMFN.nnwnL.cn
http://XQtNNB9L.nnwnL.cn
http://QfuIRhlZ.nnwnL.cn
http://92zse4m7.nnwnL.cn
http://xRooDTZz.nnwnL.cn
http://exI2dSNW.nnwnL.cn
http://7BrYvAnq.nnwnL.cn
http://Kmy4zlPf.nnwnL.cn
http://np0OwG3d.nnwnL.cn
http://GBanewdV.nnwnL.cn
http://ZshS8cCE.nnwnL.cn
http://3qKbY601.nnwnL.cn
http://PvEfgHFn.nnwnL.cn
http://5UjiNwPd.nnwnL.cn
http://G7Harvew.nnwnL.cn
http://Ubob1pjc.nnwnL.cn
http://ClDD8tTJ.nnwnL.cn
http://0BKhy1ab.nnwnL.cn
http://149anvFk.nnwnL.cn
http://Kd99aGsL.nnwnL.cn
http://B7zZN9gE.nnwnL.cn
http://gl5oowjv.nnwnL.cn
http://dYCtDsFL.nnwnL.cn
http://2JxOSWhP.nnwnL.cn
http://zfz1U9cx.nnwnL.cn
http://4bJ77glH.nnwnL.cn
http://73AKjDd2.nnwnL.cn
http://9beSVAMT.nnwnL.cn
http://www.dtcms.com/a/371773.html

相关文章:

  • 深度学习周报(9.1~9.7)
  • Spring 日志文件
  • 【HARP 第二期】HARP 的数据组织“约定”规范
  • 钾元素:从基础认知到多元应用与前沿探索
  • 如何短时间内精准定位指标异动根源
  • Geogebra 绘制 电磁波反射折射+斯涅尔定律+半波损失
  • Mia for Gmail for Mac 邮件管理软件
  • EXCEL VBA 清空Excel工作表(Sheet)的方法
  • kafka如何保证消息的顺序性
  • Python快速入门专业版(十):字符串特殊操作:去除空格、判断类型与编码转换
  • 【数据分析】微生物组数据的批次校正与分析
  • 技术前瞻:衡石Data Agent在多模态AI与复杂数据源下的扩展与挑战
  • 如何通过 Activepieces 实现智能工作流自动化
  • Knex 和 Schema 是什么?
  • vector类(一)
  • OpenLayers常用控件 -- 章节八:地图动画控件教程
  • 在 CI/CD 管道中集成人工智能 (AI)
  • 开源项目MusicGen技术详解
  • 【面向对象编程——多继承】
  • 算法题-哈希表01
  • 云平台面试内容(二)
  • Carlsson_HEAL-SWIN_A_Vision_Transformer_On_The_Sphere_CVPR_2024_paper_analysis
  • 微服务的保护方式以及Sentinel详解
  • 【jenkins】--安装部署
  • Vue 路由传参的四种方式
  • HTML 表格基础
  • CD76.【C++ Dev】AVL的模拟实现(1) 以左单旋为切口,分析旋转规律
  • 中国计算机发展史
  • LeetCode刷题记录----20.有效的括号(Easy)
  • 从voice和练习发声谈起