在友元类中我们知道,一旦在一个类中声明了友元类,那么友元类便拥有了访问该类的所有权限,可以在自己的类中对声明自己的类进行一系列操作。
友元类主要目的是为了拓展友元类的功能,但是友元类的权限未免太多了,有什么办法可以削减其访问权限呢?
继承与派生应运而生;
本篇文章主要介绍如下内容:
1> 公有继承(public)
2> 私有继承(private)
3> 保护继承(protected)
4> 三种继承方式的共同点与差异
5> 派生类的构造函数
6> 多层派生 (存储地址)
7> 多继承
8> 多继承时派生类的构造函数
9> 多继承时派生类的析构函数
10> 多继承时的同名隐藏原则
11> 一些基本问题 [ review ]
****************************************************************************************************************************************
一:公有继承
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> 保护继承举例
除了第一条不私有继承不同外,其他都是相同的。
四:三种继承方式的共同点与差异
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> 多继承的语法格式
这里需要额外注意一个问题,因为每一个派生类都是复制了一份基类数据成员作为自己的模板,因而就会产生另一种情况(二义性):
这个时候,子类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> 友元函数与成员函数、一般函数有何差别?
友元函数不是类的成员函数,它可以在类中的任何位置进行声明,声明后可以访问类的全部成员;
友元函数与一般函数的不同点在于友元函数需要在类中进行声明,且可以访问该类的所有成员,而一般函数却不可以。