☆ C++ 继承与派生(包括虚继承)
在友元类中我们知道,一旦在一个类中声明了友元类,那么友元类便拥有了访问该类的所有权限,可以在自己的类中对声明自己的类进行一系列操作。
友元类主要目的是为了拓展友元类的功能,但是友元类的权限未免太多了,有什么办法可以削减其访问权限呢?
继承与派生应运而生;
本篇文章主要介绍如下内容:
1> 公有继承(public)
2> 私有继承(private)
3> 保护继承(protected)
4> 三种继承方式的共同点与差异
5> 派生类的构造函数
6> 多层派生 (存储地址)
7> 多继承 + 虚继承
8> 多继承时派生类的构造函数
9> 多继承时派生类的析构函数
10> 多继承时的同名隐藏原则
11> 一些基本问题 [ review ]
关于public、private与protected:
private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问.
protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问
public: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问
************************************************************************************************************************************
一:公有继承
1> 公有继承特点:
2> 公有继承示例 (这里的构造函数将在第五点讲到)
class Person{
private:int age;string name;string sex;
public:Person(int a, string b, string c){age = a; name = b; sex = c;}void Print1();
};
void Person::Print1()
{cout << name << " " << sex << " " << age << endl;
}class Student2:public Person{
private:public:Student2(int a, string b, string c) :Person(a, b, c) {}void Print();
};
void Student2::Print(){cout << "Student2:";Person::Print1();
}int main()
{Student2 S2(25,"Student2","Male");S2.Print();S2.Print1();return 0;
}
这里派生类的对象是可以直接访问基类public成员函数的,但是私有继承则不允许访问:
二:私有继承
1> 私有继承特点
2> 私有继承举例 (将上面的继承方式改为private)
这里我们就可以直接看到私有继承的第三条特点:
一旦继承方式由公有变为私有,使用类的对象访问基类的权限已经被完全抹杀了,本来公有继承还可以访问public数据成员的~
三:保护继承
1> 保护继承特点
2> 保护继承举例
除了第一条不私有继承不同外,其他都是相同的。
【检测部分】
分析下面代码中类B是否有问题,有问题的话说明理由并将其补全(不得修改,只允许添加)
class A{
private:int a;
public:A(int aa) { a = aa; }void Show_A() { a = 10; cout << "Hello_A!" << endl; }
};class B :protected A{
private:int b;
public:B(int i, int j) :A(i) { b = j; }void Show_B() { b = 20; cout << "Hello_B!" << endl; }
};
理由及修改:
因为是保护继承,A中的Show_A()函数在B中已经变成保护类型(丢失了原有的public接口),必须重新为其提供公有接口;
class A
{
private:int a;
public:A(int aa) { a = aa; }void Show_A() { a = 10; cout << "Hello_A!" << endl; }
};
class B :protected A
{
private:int b;
public:B(int i, int j) :A(i) { b = j; }void Show_B() { b = 20; cout << "Hello_B!" << endl; }void Show_A() { A::Show_A(); }
};
四:三种继承方式的共同点与差异
1> 三种继承方式的第一个特点都是控制基类被继承后的属性,
这样做是为了当继承类再被继承后明确各个继承类与基类之间的关系
2> 第二条特点三者是一致的,规定了在派生类中对基类中各部分的访问权限。
而第三条特点则是对在派生类的外部,通过对象对基类进行操作权限进行了限制。
五:派生类的构造函数
1> 基类的构造函数不会被派生类继承,但是当派生类中没有对基类的函数进行初始化的时候,会自动调用基类中的构造函数进行初始化;
2> 当继承类基类中都没有构造函数,一律使用系统默认的构造函数;
3> 如第一点所说,这时就要在派生类中使用指定的格式调用基类构造函数进行初始化。
在开篇的公有继承示例中:
class Person{
private:int age;string name;string sex;
public:Person(int a, string b, string c){age = a; name = b; sex = c;}void Print1();
};
void Person::Print1()
{cout << name << " " << sex << " " << age << endl;
}class Student2:public Person{
private:int pocket_month;
public:Student2(int a, string b, string c,int d) :Person(a, b, c) {pocket_month = d;}void Print();
};
void Student2::Print(){cout << "Student2:";Person::Print1();
}
我们可以发现Person基类中有相应的构造函数,当被继承的时候,我们就要考虑如何对基类数据进行初始化,那如何对基类中的数据进行初始化呢?
这里额外提一点,那到底为什么要初始化基类数据呢?
回到本质,继承与派生的最初目的是为了减少代码的重用,即当两个类A、B中有大量一致的成员,可以将一致的成员提取出来,独自封装为一个类C,然后再通过继承来使用这个重复的类C,
然后分别在A、B类中给C中的数据赋予不同的数据进而完善A、B类,这样做就使得这些重复的部分在系统内存中只有一个备份,从而减少了资源开销,而且A、B类所需求的数据没有改变。
派生类中构造函数格式:
构造函数中的部分称呼:
六:多层派生 (派生类继续作为基类派生)
1> 示例:
#include <iostream>
using namespace std;class B0 //基类B0声明
{
public:void display() { cout << "B0::display()" << endl; } //公有成员函数
};
class B1 : public B0
{
public:void display() { cout << "B1::display()" << endl; }
};
class D1 : public B1
{
public:void display() { cout << "D1::display()" << endl; }
};void fun(B0 *ptr)
{ptr->display();
} //"对象指针->成员名"void main() //主函数
{B0 b0; //声明B0类对象B1 b1; //声明B1类对象D1 d1; //声明D1类对象B0 *p; //声明B0类指针p = &b0; //B0类指针指向B0类对象fun(p);p = &b1; //B0类指针指向B1类对象fun(p);p = &d1; //B0类指针指向D1类对象fun(p);
}
2> 输出结果
3> 结果分析以及多层派生存储方式理解
为了解决输出一致的问题,引入了虚函数的概念:
点我即达<虚函数部分>
七:多继承
1> 举例
#include <iostream>
using namespace std;class A {
public:void setA(int);void showA();
private:int a;
};
class B {
public:void setB(int);void showB();
private:int b;
};
class C : public A, private B {
public:void setC(int, int, int);void showC();
private:int c;
};
void A::setA(int x)
{a = x;
}
void A::showA(){cout << a << endl;
}
void B::setB(int x)
{b = x;
}
void B::showB() {cout << b << endl;
}
void C::setC(int x, int y, int z)
{ //派生类成员直接访问基类的//公有成员setA(x);setB(y);c = z;
}
void C::showC() {cout << c << endl;
}int main()
{C obj;obj.setA(5);obj.showA();// obj.setB(6); //错误,因为B被私有继承,使用对象无法访问B类中的任何数据成员// obj.showB(); //错误obj.setC(6, 7, 9);obj.showC();return 0;
}
2> 多继承的语法格式
☆虚继承
1> 源码
#include <iostream>
using namespace std;class A {
private:public:void func_A();
};
void A::func_A()
{cout << "Func_A!\n" << endl;
}class B :public A {
private:public:void func_B();
};
void B::func_B()
{cout << "Func_B!\n" << endl;
}class C :public A {
private:public:void func_C();
};
void C::func_C()
{cout << "Func_C!\n" << endl;
}class D :public B, public C {
private:public:void func_D();
};
void D::func_D()
{cout << "Func_D!\n" << endl;
}int main()
{D d;d.func_A(); //这里会报错return 0;
}
这里需要额外注意一个问题,因为每一个派生类都是复制了一份基类数据成员作为自己的模板,因而在使用底层派生类访问最上层基类的函数的时候将会产生二义性!
这个时候,子类C就会因为继承不明确而使得程序出现错误:
为了结局这种文体,引入了虚基类的概念:
修改部分:
☆在建立虚基类的派生对象的时候,只有最远派生类的构造函数调用虚基类的构造函数!!!
八:多继承时派生类的构造函数
1> 多继承时继承类含有内嵌对象,此时构造函数调用顺序 (基类构造函数->内嵌构造函数->本身构造函数 )
-->>举例:
#include <iostream>
using namespace std;class B1 //基类B1,构造函数有参数
{
public:B1(int i) { cout << "constructing B1 " << i << endl; }
};
class B2 //基类B2,构造函数有参数
{
public:B2(int j) { cout << "constructing B2 " << j << endl; }
};
class B3 //基类B3,构造函数无参数
{
public:B3() { cout << "constructing B3 *" << endl; }
};
class C : public B2, public B1, public B3
{
public: //派生类的公有成员C(int a, int b, int c, int d) :B1(a), memberB2(d), memberB1(c), B2(b) { }
private: //派生类的私有对象成员B1 memberB1;B2 memberB2;B3 memberB3;
};
void main()
{C obj(1, 2, 3, 4);
}
2> 输出结果
3> 具体输出顺序分析 (与构造函数中的声明顺序无关 )
九:多继承时派生类的析构函数
1> 源码示例
#include <iostream>
using namespace std;class B1 {
public:B1(int i) {cout << "Constructint B1 " << i << endl;}~B1() {cout << "Destructing B1 " << endl;}
};
class B2 {
public:B2(int i) {cout << "Constructint B2 " << i << endl;}~B2() {cout << "Destructing B2 " << endl;}
};
class B3 {
public:B3(int i) {cout << "Constructint B3 " << i << endl;}~B3() {cout << "Destructing B3 " << endl;}
};
class D1:public B2,public B1,public B3 {
public:D1(int a, int b, int c, int d,int e,int f):B2(a),B1(b),B3(c),memberB1(d),memberB2(e),memberB3(f){}
private:B1 memberB1;B2 memberB2;B3 memberB3;
};int main()
{D1 d1(1, 2, 3, 4, 5, 6);return 0;
}
2> 运行结果
十:多继承时的同名隐藏原则
1> 介绍
当基类中的成员函数和继承类的成员函数同名时,使用派生对象调用该重名函数的时候,调用的是派生对象中自己的函数;
如果需要使用派生对象来调用基类中的重名函数,
需要使用基类名限制其作用域:
2> 源码示例
#include <iostream>
using namespace std;class B1 {
public:B1(int i) {cout << "Constructint B1 " << i << endl;}~B1() {cout << "Destructing B1 " << endl;}void Print() {cout << "This is B1 !" << endl;}
};
class B2 {
public:B2(int i) {cout << "Constructint B2 " << i << endl;}~B2() {cout << "Destructing B2 " << endl;}void Print() {cout << "This is B2 !" << endl;}
};
class B3 {
public:B3(int i) {cout << "Constructint B3 " << i << endl;}~B3() {cout << "Destructing B3 " << endl;}
};
class D1:public B2,public B1,public B3 {
public:D1(int a, int b, int c, int d,int e,int f):B2(a),B1(b),B3(c),memberB1(d),memberB2(e),memberB3(f){}void Print() {cout << "This is D1 !" << endl;}
private:B1 memberB1;B2 memberB2;B3 memberB3;
};int main()
{D1 d1(1, 2, 3, 4, 5, 6);d1.Print();d1.B1::Print();d1.B2::Print();return 0;
}
十一:一些基本问题 [ review ]
1> 类的继承方式有几种?不同的继承方式下,基类成员的访问属性到派生类中有怎样的变化?
继承方式分为三种:public、private、protected
public继承方式下基类的public和protected成员的访问属性在派生类中保持不变,但基类的private成员不可以直接访问;
private继承方式下基类的public和protected成员的访问属性在派生类中访问属性变为private,但基类的private成员不可以直接访问;
priotected继承方式下基类的public和protected成员的访问属性在派生类中访问属性变为protected,但基类的private成员不可以直接访问;
2> 类的保护成员有哪些特征?
protected成员可以被派生类的成员函数访问,但是对于外界是隐蔽的;若为公有派生,则基类的protecyed成员在派生类中属性不变;若为私有派生,则基类中的protected成员在派生类中属性变为private。
3> 使成员函数成为内联函数的方法有哪些?
(一)在类定义的时候直接给出成员函数的实现;
(二)在类中仅仅给出函数声明,在类定义之外使用inline关键字给出成员函数的实现。
4> 友元函数与成员函数、一般函数有何差别?
友元函数不是类的成员函数,它可以在类中的任何位置进行声明,声明后可以访问类的全部成员;
友元函数与一般函数的不同点在于友元函数需要在类中进行声明,且可以访问该类的所有成员,而一般函数却不可以。
☆ C++ 继承与派生(包括虚继承)相关推荐
- 【C++】继承和派生、虚继承和虚基类、虚基类表和虚基类指针
继承和派生.虚继承和虚基类.虚基类表和虚基类指针 继承和派生 继承概述 继承基本概念 派生类中的成员 继承的内容 派生类定义 派生类访问控制 对象构造和析构 对象构造和析构的调用顺序 继承中的构造和析 ...
- 继承与派生、虚函数、多态
文章目录 继承 单继承 多重继承 派生 派生类的声明方式 派生类的构成 派生类成员的访问属性 公用继承 私有继承 保护继承 普通派生类的构造函数 创建普通对象的构造函数 有子对象的派生类的构造函数 创 ...
- 钻石问题(菱形继承问题) 和虚继承
在C++中,什么叫做钻石问题(也可以叫菱形继承问题),怎么避免它? 下面的图表可以用来解释钻石问题. 假设我们有类B和类C,它们都继承了相同的类A.另外我们还有类D,类D通过多重继承机制继承了类B和类 ...
- 继承(下)----虚继承
单继承&多继承 一个子类只有一个直接父类时称这种继承关系为单继承. 一个子类有两个或者两个以上的父类时称这种继承关系为多继承. 菱形继承 ---------特殊的多继承 有很大的缺点: 二义性 ...
- 多继承的二义性和虚继承(虚基类)
一般来说,在派生类中对基类成员的访问是应该是唯一的.但是,由于在多继承的情况下,可能出现基类中某个成员的访问不唯一的情况,这称为对基类成员访问的二义性. 在多继承的情况下,通常有两种可能出现的二义性. ...
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7883531 一.虚函数的工作原理 虚函数的实现要求对象携带额 ...
- C++虚继承(九) --- 构造函数调用顺序的实用之处
虚拟继承是C++语言中一个非常重要但是又比较生僻的存在,它的定义非常简单,但是对于理解C++的继承机制却是非常有用的.笔者最近学习过程中发现对C++的虚拟继承不是很明朗,故在这里对虚继承做个小结. 首 ...
- C++对象模型:单继承,多继承,虚继承
什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 cla ...
- 【C++进阶知识】C++类的继承和派生
目录 C++进阶知识系列文章 C++类的继承和派生 基本语法 访问修饰符 构造函数和析构函数 覆盖父类的方法 虚函数和纯虚函数 示例代码 参考资料 C++进阶知识系列文章 C++类的继承和派生 在面向 ...
最新文章
- kong安装配置手册
- 高性能消息中间件——NATS
- Elasticsearch 基本介绍及其与 Python 的对接实现
- 检查图层当中是否存在高程基准(C++)ArcObject
- java数据类型转换方向_java基础学习篇02(数据类型转换和基本面试题)
- 深入分析windows和Linux动态链接库的应用异同[转]
- java学生管理系统,(史上最全)
- “33岁,阿里程序员,税后年薪170万,我征婚有要求,有错吗?”
- PMP工作绩效数据、信息和报告三者的区别
- nginx动静分离和资源隔离的网站搭建
- 求余数(c++基础)
- 水星怎么设置网速最快_水星路由器怎么限制别人网速_水星怎么限制wifi网速?-192路由网...
- pandas中的concat操作
- 计算机内部线有,电脑主机内部有几根线?分别叫什么?
- open-falcon 学习一二
- 计算机文档用什么翻译,有什么方便使用的文档翻译软件?
- 什么是U盘量产工具?
- RabbitMQ系列之高可用集群
- Java中类与类之间的关系:依赖,关联,耦合,聚合,组合,继承,实现
- 利用模板Q>一键制作你懂的任意视频规则(无需懂写任何代码)