继承机制是面向对象程序设计使代码能够复用的最重要的手段,它同意程序猿在保持原有类特性的基础上进行扩展,添加功能。

这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。

#include<iostream>
using namespace std;
class Base
{
public:
Base()
{}
~Base()
{}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
class Derive : public Base
{
public:
Derive()
{}
~Derive()
{}
private:
int _dPri;
protected:
int _dPro;
public:
int _dPub;
};
int main()
{
cout << sizeof(Base) << endl;//12=4+4+4
cout << sizeof(Derive) << endl;//24,派生类的大小包括了基类的大小和自己的大小
return 0;
}

继承关系和訪问限定符:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">

class Base
{
public:
Base()
{}
~Base()
{}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
class Derive :public Base
{
public:
Derive()
{}
~Derive()
{}
void Display()
{
cout << "_pri=" << _pri << endl;//error基类的私有成员在派生类中不能被訪问
cout << "_pri=" << _pro << endl;
cout << "_pri=" << _pub << endl;
cout << "_dpri=" << _dpri << endl;
cout << "_dpri=" << _dpri << endl;
cout << "_dkpri=" << _dpri << endl;
}
private:
int _dpri;
protected:
int _dpro;
public:
int _dpub;
};
int main()
{
Derive d;
d._pri = 10;//error
d._pro = 20;//error

	d._pub = 30;
	d._dpri = 20;//error
d._dpro = 20;//error
d._dpub = 20;
}
总结:
1.基类的private成员在派生类中是不能被訪问的。假设基类成员不想在类外直接被訪问,但须要在派生类中能訪问。就定义为protected。能够看出保护成员限定符是因继承才出现的。

2.public继承是一个接口继承。保持is-a原则,每一个父类可用的成员对子类也可用,由于每一个子类对象也都是一个父类对象。

3.protetced/private继承是一个实现继承。基类的部分成员并不是全然成为子类接口的一部分,是 has-a 的关系原则,所以非特殊情况下不会使用这两种继承关系。在绝大多数的场景下使用的都是公有继承。

4.无论是哪种继承方式,在派生类内部都能够訪问基类的公有成员和保护成员。基类的私有成员存在可是在子类中不可见(不能訪问)。

5.使用keywordclass时默认的继承方式是private。使用struct时默认的继承方式是public。只是最好显示的写出继承方式。

6.在实际运用中一般使用都是public继承,极少场景下才会使用protetced/private继承.

在派生类中假设没有显示定义类的六个成员函数,编译器系统则会默认合成这六个默认的成员函数

class Base
{
public:
Base()
{
cout << "Base()" << endl;
}
~Base()
{
cout << "~Base()" << endl;
}
private:
int _pri;
protected:
int _pro;
public:
int _pub;
};
class Derive :public Base
{
public:
Derive()
{
cout << "Derive()" << endl;
}
~Derive()
{
cout << "~Derive()" << endl;
}
private:
int _dpri;
protected:
int _dpro;
public:
int _dpub;
};
int main()
{
{
Derive d;
}
getchar();
return 0;
}

继承关系中构造函数调用的顺序:基类构造函数 =》派生类中对象构造函数 =》派生类构造函数体

继承关系中析构函数调用的顺序:派生类析构函数 =》派生类包括成员对象析构函数 =》基类析构函数

说明:  1、基类没有缺省构造函数。派生类必需要在初始化列表中显式给出基类名和參数列表。

        2、基类未定义构造函数。则派生类也能够不用定义,所有使用缺省构造函数。

        3、基类定义了带有形參表构造函数。派生类就一定定义构造函数。

默认构造函数:指调用时无需提供參数的构造函数。因此:它能够是没有參数的,也能够是全部參数都有默认值的,两者取其一,否则有二义性。

编译器会合成默认(缺省)构造函数:1.当类中有类类型的成员,且类成员有默认的构造函数。

2.当基类有默认构造函数时,派生类没有构造函数;

3.当类中有虚函数或类派生链中有虚函数时;

4.当类採用虚继承方式时。

调用拷贝构造函数:1.用一个对象初始化还有一个对象时,且是同类对象。(对象构建对象)

2.当函数的參数是对象。通过对象调用函数时,參数的拷贝会调用拷贝构造函数;(值传參)

3.当函数的返回值是对象,调用函数时。返回一个对象会调用拷贝构造函数。

(值返回值)

编译器会合成拷贝构造函数:1.当类中有类类型的成员,且类成员有拷贝构造函数;

2.当基类有拷贝构造函数时。派生类没有拷贝构造函数。

3.当类中有虚函数时;

4.当类採用虚继承方式时。

继承体系中的作用域:

1.在继承体系中基类和派生类是两个不同作用域。

2.子类和父类中有同名成员。子类成员将屏蔽父类对成员的直接訪问。(在子类成员函数中。能够使用 基类::基类成员 訪问)--隐藏 --重定义

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

继承与转换--赋值兼容规则--public继承:1.子类对象能够赋值给父类对象(分割/切片)

                                     2.父类对象不能赋值给子类对象

                                     3.父类的指针/引用能够指向子类对象

                                     4.子类的指针/引用不能指向父类对象(能够通过强制类型转换完毕)

class Base
{
public:
Base(int data)
{}
~Base()
{}
private:
int _pri;
protected:
int _pro;
public:
int _pub; static int count;
}; int Base::count = 0;
class Derive : public Base
{
public:
Derive()
: Base(20)
{} ~Derive()
{}
private:
int _dPri;
protected:
int _dPro;
public:
int _dPub;
int _pri;
};
int main()
{
Derive d;
Base b(0);
b = d;
d = b;//error,父类对象不能赋值给子类
Base* pBase = &d;
Derive *pD = (Derive*)&b;//通过强制类型转换能够赋值
return 0;
}

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

继承与静态成员:基类定义了static成员,则整个继承体系里面仅仅有一个这种成员。不管派生出多少个子类。都仅仅有一个static成员实例。

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

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center">

多继承:一个子类至少有两个父类。

菱形继承:会产生二义性和数据冗余问题。

class Person
{
public:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _num; //学号
};
class Teacher : public Person
{
protected:
int _id; // 职工编号
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse; // 主修课程
};
void Test()
{
// 显示指定訪问哪个父类的成员
Assistant a;
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
}
为了解决菱形继承的二义性和数据冗余问题。使用虚继承
#include<iostream>
using namespace std;
class Base
{
public:
virtual void FunTest1() //虚函数
{
cout << "Bsae::FunTest1()" << endl;
}
virtual void FunTest2()  //虚函数
{
cout << "Base::FunTest2()" << endl;
}
int _data1=0x01;
};
class Derive :virtual public Base  //虚继承
{
public:
virtual void FunTest3()  //虚函数
{
cout << "Derive::FunTest3()" << endl;
}
virtual void FunTest4()  //虚函数
{
cout << "Derive::FunTest4()" << endl;
}
int _data2=0x02;
};
int main()
{
Base b1;
Derive d1;
return 0;
}
<img src="http://img.blog.csdn.net/20160413123747132?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
对象d1中存储了两个虚指针:各自是指向base的虚指针_vfptr和指向Derive的虚指针_vfptr。
<img src="http://img.blog.csdn.net/20160413123806697?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
0x00cfddf8表示的是Derive表自己的地址
0x00cfdbb0表示的是派生类独立成员相对于Base类成员偏移的地址
0x00cfdca8表示Derive中属于Base类的那部分的虚表地址
</pre><pre class="cpp" name="code" code_snippet_id="1640343" snippet_file_name="blog_20160413_18_7825472">在base的虚指针中存放的是base中虚表的地址。通过虚指针能够找到虚表的地址,即虚表中存放的是成员函数的调用地址:
<img src="http://img.blog.csdn.net/20160413115724037?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
</pre><pre class="cpp" name="code" code_snippet_id="1640343" snippet_file_name="blog_20160413_21_4368718">相同的在Derive中的虚指针中存放了Derive的虚表地址:
<img src="http://img.blog.csdn.net/20160413120037523?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="" />
</pre><pre class="cpp" name="code" code_snippet_id="1640343" snippet_file_name="blog_20160413_24_9275761">总结:
<span style="color:#ff0000;">虚拟继承下,派生类对象组成:</span>
<span style="color:#ff0000;">1.派生类自己的虚表指针+派生类第一个成员相对于基类成员的偏移+派生类自己的数据部分</span>
<span style="color:#ff0000;">2.基类的虚表指针+基类的数据部分</span>


初步学习C++中的继承关系的更多相关文章

  1. [原创]java WEB学习笔记87:Hibernate学习之路-- -映射 继承关系(subclass , joined-subclass,union-subclass )

    本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用 内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系. 本人互联网技术爱 ...

  2. C++反汇编第四讲,反汇编中识别继承关系,父类,子类,成员对象

    C++反汇编第四讲,反汇编中识别继承关系,父类,子类,成员对象 讲解目录: 1.各类在内存中的表现形式   备注: 主要复习开发知识,和反汇编没有关系,但是是理解反汇编的前提.     2.子类继承父 ...

  3. Hibernate学习之路-- -映射 继承关系(subclass , joined-subclass,union-subclass )

    1.继承映射 举例:对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念.Hibernate 的继承映射可以理解持久化类之间的继承关系.例如:人和学生之间的关系.学生继承了人,可以认为学生是一 ...

  4. 在Entity Framework 中实现继承关系映射到数据库表

    继承关系映射到数据库表中有多种方式: 第一种:TPH(table-per-hiaerachy) 每一层次一张表 (只有一张表) 仅使用名为父类的类型名的一张表,它包含了各个子类的所有属性信息,使用区分 ...

  5. Style在Android中的继承关系

    Style在Android中的继承关系 Android的Styles(样式)和Themes(主题)非常类似Web开发里的CSS,方便开发者将页面内容和布局呈现分开.Style和Theme在Androi ...

  6. Dom中的继承关系

    首先声明,一些内容基于个人猜测,如果哪里有错误,请立即联系在下! 我们用js操作Dom时,会经常用到一些个方法比如基于获取到的元素选择其子元素: <!DOCTYPE html> <h ...

  7. c++中的继承关系

    1 什么是继承 面向对象的继承关系指类之间的父子关系.用类图表示如下: 2 为什么要有继承?/ 继承的意义? 因为继承是面向对象中代码复用的一种手段.通过继承,可以获取父类的所有功能,也可以在子类中重 ...

  8. android 中组件继承关系图,一目了然

    View继承关系图 Adapter适配器继承关系图 Activity继承关系图

  9. 浅谈javaScript中的继承关系<一>

    // JavaScript Document //创建三个构造函数 function Shape(){ this.name='ahape'; this.toString=function(){retu ...

随机推荐

  1. MySQL丨02丨忘记root用户密码怎么办?

    软件:Mysql 版本:8.0.13 1. 先暂停mysql的服务,方法是在cmd里输入如下代码: net stop mysql 2. 在安装文件夹下创建一个文件:mysql-ini.txt (我的安 ...

  2. 20. Valid Parentheses (python版)

    Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the inpu ...

  3. Canvas标签

    1.Canvas标签: HTML5<canvas>元素用于图形的绘制,通过脚本(通常是javascript)来完成<canvas>标签只是图形容器,必须使用脚本来绘制图形.你可 ...

  4. 【瞎扯】我的OI之路

    这里大概是一些我自己对我的OI之路的一些记录. 2015.11不知道哪一天-- 我听说了"编程". 当时还不懂得啥是信息学竞赛,以为这只是纯粹的程序设计.后来才明白信息学竞赛是算法 ...

  5. hadoop格式化出错,提示IO异常

    配置好hadoop之后,在进行格式化的时候出现异常,原因是由于在core-site.xml 配置文件中写的路径格式不对. 不需要加 file:/ 或者 file:// 直接写绝对路径就行. <c ...

  6. python多线程--线程同步

    如果多个线程共同对某个数据修改,则可能出现不可预料的结果,为了保证数据的正确性,需要对多个线程进行同步. 使用Thread对象的Lock和Rlock可以实现简单的线程同步,这两个对象都有acquire ...

  7. POJ 2352 star level

    题目链接: http://poj.org/problem?id=2352 题目大意:对于每一颗星星来说,都有一个属于自己的level,这个值为其他星星x,y坐标均不大于本星星的个数.输入时按先y由小到 ...

  8. HDU 1358 next数组的推移

    题目大意: 输入n,再输入一个长度为n的字符串,从第二位开始,计算它的前缀(包括他自己)中出现过的重复字符串的个数,如aabaabaabaab的第6位的前缀aabaab,aab连续出现了两次,所以输出 ...

  9. [luoguP2564][SCOI2009]生日礼物(队列)

    传送门 当然可以用队列来搞啦. # include <iostream> # include <cstdio> # include <cstring> # incl ...

  10. 洛谷P1757 通天之分组背包

    题目背景 直达通天路·小A历险记第二篇 题目描述 自01背包问世之后,小A对此深感兴趣.一天,小A去远游,却发现他的背包不同于01背包,他的物品大致可分为k组,每组中的物品相互冲突,现在,他想知道最大 ...