继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,继承中的构造函数与析构函数

时间:2022-09-07 21:34:19

1,派生类的声明方式

class Student
{
public:
void display()
{
cout << "num:" << num << endl;
cout << "name:" << name << endl;
cout << "sex:" << sex << endl;
}
private:
int num;
char name[10];
char sex;
};

class Student1 : public Student//声明派生类
{
public:
void display_1()
{
cout << "age:" << endl;
cout << "address:" << addr << endl;
}
private:
int age;
char addr[10];
};

2,派生类成员的访问属性
(1)public(公用继承)

class student
{
public:
void getvalue()
{
cin >> num >> name >> sex;
}
void display()
{
cout << "num:" << num << endl;
cout << "name::" << name << endl;
cout << "sex:" << sex << endl;
}
private:
int num;
char name[10];
char sex[5];
};

class student1:public student
{
public:
void getvalue_1()
{
getvalue();
cin >> age>>addr;
}
void display_1()
{
display();//调用基类公有成员函数来访问基类私有成员变量
cout << "num:" << num << endl;//报错,基类私有成员不可访问
cout << "age:" << age << endl;
cout << "addr:" << addr << endl;
}
int age;
char addr[10];
};

int main()
{
Student1 stud;
stud.getvalue_1();
stud.display_1();
system("pause\n");
return 0;
}

下面用一张表来说明公用基类的成员在派生类中的访问属性
继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,继承中的构造函数与析构函数

(2)private(私有继承)
将上面的程序改为私有继承

class student
{
public:
void getvalue()
{
cin >> num >> name >> sex;
}
void display()
{
cout << "num:" << num << endl;
cout << "name::" << name << endl;
cout << "sex:" << sex << endl;
}
private:
int num;
char name[10];
char sex[5];
};

class Student1 :private student
{
public:
void getvalue_1()
{
getvalue();
cin >> age >> addr;
}
void display_1()
{
display();
//cout << "num:" << num << endl;
cout << "age:" << age << endl;
cout << "addr:" << addr << endl;
}
private:
int age;
char addr[10];
};

int main()
{
Student1 stud;
//stud.getvalue();//报错,私有基类的公用成员函数在外界不可访问
stud.display_1();
//stud.age = 18;//错误,外界无法访问派生类的私有成员
system("pause\n");
return 0;

}

结论:(1)不能通过派生类对象引用从私有基类继承过来的任何成员
(2)派生类的成员函数不能访问私有基类的私有成员,但可以访问私有类的公用成员函数
继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,继承中的构造函数与析构函数

(3)protected(保护继承)

class student
{
private:
void display();

protected:
int num;
char name[10];
char sex[5];
};

class Student1 :protected student
{
public:
void getvalue_1()
{
cin >> num >> name >> sex;
cin >> age >> addr;
}
void display_1()
{
cout << "num:" << num << endl;
cout << "name:" << num << endl;
cout << "sex:" << num << endl;
cout << "age:" << age << endl;
cout << "addr:" << addr << endl;
}
private:
int age;
char addr[10];
};

int main()
{
Student1 stud;
stud.display_1();
//stud.display();//报错,保护基类的公用成员在外界不可访问
stud.display_1();
//stud.age = 18;//报错,基类的保护成员对派生类的外界来说是不可访问的
system("pause\n");
return 0;

}

保护基类的公有成员和保护成员在派生类中都成了保护成员,其私有成员仍为基类私有,也就是把基类原有的公有成员也保护起来,不让类外任意访问。
3,多级派生时的访问属性

class A
{
public:
int i;
protected:
void f1(){}
int j;
private:
int k;
};

class B :public A
{
public:
void f2(){}
protected:
void f3(){}
private:
int m;
};

class C :protected B
{
public:
void f4();
private:
int n;
};

int main()
{
C c;
B b;
b.i = 10;//可访问
//c.f2();//不可访问
}

各成员在不同类中的访问属性
继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,继承中的构造函数与析构函数
无论哪一种继承方式,在派生类中是不能访问基类的私有成员的,私有成员只能被本类的成员函数访问。
4,派生类的构造函数和析构函数
先定义一个简单的派生类的构造函数

class Student
{
public:
Student(int n, string nam, char s)//定义基类构造函数
:num(n), name(nam), sex(s)
{
cout << "Student()" << endl;
}
~Student()
{
cout << "~Student()" << endl;
}
protected:
int num;
string name;
char sex;
};

class Student1 :public Student
{
public:
Student1(int n, string nam, char s, int a, string add)//定义派生类构造函数
:Student(n, nam, s), age(a), addr(add)//在初始化列表初始化
{
cout << "Student1()"<<endl;
}
void show()
{
cout << "num:" << num << endl;
cout << "name:" << name << endl;
cout << "sex:" << sex << endl;
cout << "age:" << age << endl;
cout << "add:" << addr << endl;
}
~Student1()
{
cout << "~Student1()" << endl;
}
private:
int age;
string addr;
};

int main()
{
Student1 stud1(1000, "wangli", 'n', 19, "shanxikejidaxue");
Student1 stud2(1001, "liuyulin", 'n', 18, "xibeihzengfadaxue");
stud1.show();
cout << endl;
stud2.show();
//system("pause\n");
return 0;
}

分别在两个构造函数和两个析构函数处大断点,对程序进行调试,打印的结果如图
继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,继承中的构造函数与析构函数
从结果上看,程序是先调用了基类的构造函数,再调用派生类的构造函数,程序结束后,先析构派生类,再析构基类。
事实上,从程序的调试过程可以看出,程序是先调用派生类的构造函数,此时,执行调用基类构造函数完成对基类的初始化,然后再继续执行派生类构造函数的函数体。此处可上机实践进行验证。

若派生类中有子对象,则在派生类构造函数中对子对象进行初始化。因此,执行派生类构造函数的次序是
(1)调用基类构造函数,对基类数据成员初始化
(2)调用子对象构造函数,对子对象数据成员初始化
(3)再执行派生类构造函数本身,对派生类数据成员初始化

派生类是不能继承基类的析构函数的,也需要通过派生类的析构函数去调用基类的析构函数。
5,多重继承引起的二义性问题及同名覆盖

class A
{
public:
int a;
void display();
};
class B
{
public:
int a;
void display();
};
class C :public A, public B
{
public:
int b;
//void show();
};

int main()
{
C c;
c.a = 10;//报错
c.display();//报错
}

编译系统无法判别要访问的是哪个基类的成员,编译出错,这就是多重继承存在的二义性问题。
如果将C类改为如下形式

class C :public A, public B
{
public:
int a;
void display();
};

int main()
{
C c;
c.a = 10;//编译通过
c.display();//编译通过
}

此时编译通过,那么程序执行时访问的到底是哪一个类中的成员?
此时,访问的是C类的成员,规则:基类的同名成员在派生类中被屏蔽,成为“不可见”的,或者说,派生类新增加的同名成员覆盖了基类中的同名成员。
注意:不同的成员函数,只有在函数名和参数个数相同,类型匹配的情况下才发生同名覆盖
6,菱形继承及虚基类
菱形继承时多重继承中的一种继承方式,也存在二义性问题
继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,继承中的构造函数与析构函数
这样的继承方式成为菱形继承

class A
{
public:
int data;
void fun(){}
};

class B :public A
{
public:
int data;
void fun(){}
int _b;
};

class C :public A
{
public:
int data;
void fun(){}
int _c;
};

class D :public B, public C
{
public:
int _d;
void fun_d(){}
};

继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,继承中的构造函数与析构函数
从上图看出,不同类中相同的成员在D中产生了多份拷贝,不仅占用内存,而且对访问这些成员增加困难。
下面我们介绍虚基类
虚基类可使得在继承间接共同基类时只保留一份成员
将上面的代码改为

class A
{};
class B:virtual public A
{};
class C:virtual public A
{};

继承:单继承、派生类成员的访问属性、多继承、菱形继承、虚继承,继承中的构造函数与析构函数