C++继承的定义与注意事项

 更新时间:2021年05月11日 09:48:04   作者:程序字母K  
这篇文章主要给大家介绍了关于C++继承的定义与注意事项的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、什么是继承?

1.1、概念

**继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。

1.2、定义

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

1.2.1、继承关系和访问限定符

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

// 实例演示三种继承关系下基类成员的各类型成员访问关系的变化
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.基类private成员在派生类中无论以什么方式继承都是不可见的。不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上派生类对像无法访问;

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

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

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

派生类(子类)对象可以赋值给基类(父类)对象/指针/引用;

即将父类中那部分赋值给基类;

这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。

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

基类的指针可以通过强制类型转换赋值给派生类的指针。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 来进行识别后进行安全转换;

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;

三、继承中的作用域

  1. 在继承体系中基类和派生类都有独立的作用域。
  2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定
  3. 义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)
  4. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  5. 注意在实际中在继承体系里面最好不要定义同名的成员。
 // 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();
};
// 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);
};

四、派生类的默认成员函数

6个默认成员函数,“默认”的意思就是指我们不写,编译器会变我们自动生成一个,那么在派生类中,这几个
成员函数是如何生成的呢?

  1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函
  2. 数,则必须在派生类构造函数的初始化列表阶段显示调用。
  3. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  4. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  5. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类
  6. 对象先清理派生类成员再清理基类成员的顺序。
  7. 派生类对象初始化先调用基类构造再调派生类构造。
  8. 派生类对象析构清理先调用派生类析构再调基类的析构。


五、继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员;

六、继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员;

七、菱形继承

单继承:一个子类只有一个直接父类

多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承

菱形继承:菱形继承是多继承的一种特殊情况。

菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。

虚拟继承可以解决菱形继承的二义性和数据冗余的问题,在Student和Teacher的继承;

加关键字:virtual

菱形继承的内存对象成员模型:这里可以看到数据冗余

菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?

这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。

虚基表:存放偏移量

八、继承和组合

public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象,高耦合;

组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象,低耦合;

优先使用对象组合,而不是类继承

继承和耦合的使用:

// Car和BMW Car和Benz构成is-a的关系
class Car{
protected:
string _colour = "白色"; // 颜色
string _num = "陕ABIT00"; // 车牌号
};
class BMW : public Car{
public:
void Drive() {cout << "好开-操控" << endl;}
};
class Benz : public Car{
public:
void Drive() {cout << "好坐-舒适" << endl;}
};
// Tire和Car构成has-a的关系
class Tire{
protected:
string _brand = "Michelin"; // 品牌
size_t _size = 17; // 尺寸
};
class Car{
protected:
string _colour = "白色"; // 颜色
string _num = "陕ABIT00"; // 车牌号
Tire _t; // 轮胎
};

总结

到此这篇关于C++继承的定义与注意事项的文章就介绍到这了,更多相关C++继承内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++指针 详细介绍及总结

    C++指针 详细介绍及总结

    这篇文章主要介绍了C++指针 详细介绍及总结的相关资料,需要的朋友可以参考下
    2016-09-09
  • C/C++时间库chrono的使用总结

    C/C++时间库chrono的使用总结

    std::chrono是C++标准库中的一个组件,用于表示和处理时间,其功能就像是心理学中的感知系统,它可以为我们捕捉、量化并操作抽象的时间概念,这就如同我们的大脑可以理解和感知周围环境的时间流逝一样,这种感知和理解能力是人类进行日常活动所必需的,
    2023-12-12
  • c++将引用或者是指针作为函数参数实现实参的运算

    c++将引用或者是指针作为函数参数实现实参的运算

    这篇文章主要介绍了c++将引用或者是指针作为函数参数实现实参的运算,需要的朋友可以参考下
    2014-05-05
  • C++ 超详细讲解stack与queue的使用

    C++ 超详细讲解stack与queue的使用

    C++ Stack(堆栈) 是一个容器类的改编,为程序员提供了堆栈的全部功能,也就是说实现了一个先进后出(FILO)的数据结构,许多程序都使用了 queue 容器。queue 容器可以用来表示超市的结账队列或服务器上等待执行的数据库事务队列
    2022-03-03
  • C++数据结构之list详解

    C++数据结构之list详解

    list是一种序列式容器。list容器完成的功能实际上和数据结构中的双向链表是极其相似的,list中的数据元素是通过链表指针串连成逻辑意义上的线性表,也就是list也具有链表的主要优点,即:在链表的任一位置进行元素的插入、删除操作都是快速的
    2021-11-11
  • C语言多媒体框架GStreamer使用教程深讲

    C语言多媒体框架GStreamer使用教程深讲

    GStreamer 是用来构建流媒体应用的开源多媒体框架(framework),其目标是要简化音/视频应用程序的开发,已经能够被用来处理像 MP3、Ogg、MPEG1、MPEG2、AVI、Quicktime 等多种格式的多媒体数据
    2022-07-07
  • 利用C语言将点分十进制的IP字符串转成4个整数

    利用C语言将点分十进制的IP字符串转成4个整数

    这篇文章主要为大家详细介绍了如何利用C语言实现将点分十进制的IP字符串转成4个整数,文中的示例代码简洁易懂,感兴趣的小伙伴可以跟随小编一起学习一下
    2025-01-01
  • C语言switch 语句的用法详解

    C语言switch 语句的用法详解

    本文主要介绍C 语言中的switch 语句,这里提供示例代码和详细介绍,希望能帮助学习C语言的小伙伴
    2016-07-07
  • C++中的String的常用函数用法

    C++中的String的常用函数用法

    这篇文章主要介绍了C语言中String的常用函数用法,对学习String的小伙伴有一定的参考价值,感兴趣的小伙伴可以借鉴一下
    2023-03-03
  • Linux C/C++实现显示NIC流量统计信息

    Linux C/C++实现显示NIC流量统计信息

    NIC流量统计信息是由操作系统维护的,当数据包通过NIC传输时,操作系统会更新相关的计数器,通过读取这些计数器,我们可以获得关于网络流量的信息,下面我们就来学习一下如何通过C/C++实现显示NIC流量统计信息吧
    2024-01-01

最新评论