【面向对象语言三大特性之 “继承”】(一)

简介: 【面向对象语言三大特性之 “继承”】(一)

1.继承的概念及定义

1.1继承的概念

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

我们来举个栗子:

首先我们定义一个基类Person:

class Person
{
public:
  void Print()
  {
    cout << "name:" << _name << endl;
    cout << "age:" << _age << endl;
  }
protected:
  string _name = "peter";//  姓名
  int _age = 18;//  年龄
};

再定义两个子类Stuend和Teacher,这两个子类是继承了父类的成员(成员函数+成员变量)

我们可以通过监视窗口来查看:


fbfafe2f43ed42d29c30ae787616aa2c.png

不难发现的确子类继承了父类的成员。

1.2 继承定义

1.2.1定义格式

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

7dda46fa428e4689bddff9d5e16b0af4.png

1.2.2继承关系和访问限定符

de5cb971420b4d93ba465c1d2390b69a.png

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

image.png

总结:

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

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

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

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

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

这些大家可以自行去验证,这里我就不再多说了。

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

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

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

基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run Time Type Information)的dynamic_cast  来进行识别后进行安全转换。(ps:这个我们后面再讲解,这里先了解一下)

看下面这样一段代码:

int main()
{
  Student sobj;
  Person pobj = sobj;
  Person* pp = &sobj;
  Person& rp = sobj;
  return 0;
}

我们发现编译既然是没有错误的,这里就将子类切片给了父类。

(注意:切片行为是不存在隐式类型转换的,是直接将子类切片给父类)


aff767ab8fb34e209743792b1d574be7.png

但是我们要注意:基类对象不能赋值给派生类对象 基类的指针可以通过强制类型转换赋值给派生类的指针 (这样会有越界的风险)

    //基类对象不能赋值给派生类对象
    sobj = pobj;
    //基类对象不能引用给派生类对象
    Person p;
  Student& rs = p;
    //基类的指针可以通过强制类型转换赋值给派生类的指针
    pp = &sobj
    Student* ps1 = (Student*)pp; // 这种情况转换时虽然可以,但是会存在越界访问的问题
    ps1->_No = 10;

3.继承中的作用域

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

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

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

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

就像下面这段程序:

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();
};

这段程序能够正确运行:

ae8c675ff81b45769e74e2afe2746ea8.png

通过以前学习的知识我们也知道当同名变量出现在一起时会优先使用局部,而这里_num出现在两个不同的类域中,默认情况就在本域去寻找,指定类域后就在指定类域去寻找。这种情况就叫做隐藏。

再来看看一段程序:

// 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);
};

由于fun()函数在不同作用域中,所以他们构成隐藏而不是重载。


目录
相关文章
|
5月前
|
Java 数据安全/隐私保护 开发者
Java是一种完全支持面向对象编程的语言,其面向对象特性包括封装、继承、多态和抽象等
【6月更文挑战第18天】**面向对象编程(OOP)通过对象封装状态和行为,实现问题域的抽象。Java全面支持OOP,核心特性包括**: - **封装**:保护数据安全,隐藏内部细节。 - **继承**:子类继承父类属性和行为,促进代码重用。 - **多态**:一个接口多种实现,增强灵活性和扩展性。 - **抽象**:通过接口和抽象类抽离共性,简化复杂性。 **Java的OOP便于理解和解决复杂系统问题。**
53 3
|
5月前
|
C++
C++ 是一种面向对象的编程语言,它支持对象、类、继承、多态等面向对象的特性
C++ 是一种面向对象的编程语言,它支持对象、类、继承、多态等面向对象的特性
|
6月前
|
Java
Java中的面向对象编程特性(封装、继承、多态)
Java中的面向对象编程特性(封装、继承、多态)
|
6月前
|
编译器 C++
[C++] 面向对象的三大特性:封装、继承和多态
[C++] 面向对象的三大特性:封装、继承和多态
76 0
|
存储 算法 Java
面向对象编程实践:类、对象与继承
面向对象编程实践:类、对象与继承
55 0
|
安全 Java 编译器
C++ 面向对象三大特性——继承
面向对象三大特性的,封装,继承,多态,今天我们研究研究C++的继承。
|
存储 编译器 C++
C++ 面向对象三大特性——多态
面向对象三大特性的,封装,继承,多态,今天我们研究研究C++的多态。
|
Java 编译器 C++
【面向对象语言三大特性之 “继承”】(二)
【面向对象语言三大特性之 “继承”】(二)
52 0
|
编译器 Linux C++
【面向对象语言三大特性之 “多态”】(二)
【面向对象语言三大特性之 “多态”】(二)
75 0
|
编译器 C++
【面向对象语言三大特性之 “多态”】(一)
【面向对象语言三大特性之 “多态”】(一)
70 0