一.
1. 类之间的关系
Ø 继承:在已有类的基础上创建新类的过程
Ø 一个 B 类继承A类,或称从类 A 派生类 B
类A 称为基类(父类),类B 称为派生类(子类)
2. 基类和派生类类继承关系的语法形式
class派生类名 : 基类名表
{
数据成员和成员函数声明
};
基类名表 构成
访问控制 基类名1, 访问控制 基类名2 ,…, 访问控制 基类名n
访问控制表示派生类对基类的继承方式,使用关键字:
public 公有继承
private 私有继承
protected 保护继承
2.1 访问控制
不论种方式继承基类,派生类都不能直接使用基类的私有成员
派生类的生成过程派生类的生成过程经历了三个步骤:
●吸收基类成员(全部吸收(构造、析构除外),但不一定可见)
●改造基类成员
●添加派生类新成员
派生类对象结构
• 基类对象
• 子类对象
• 子类对象空间总是不小于基类对象
• 对于下面的继承关系:
• class Father
• {
• int a,b;
• public:
• // 成员函数
• };
• class Son:public Father
• {
• int c;
• public:
• // 成员函数
• };
• 8.2.2 重名成员
• 派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽(hide)了基类的同名成员
• 在派生类中使用基类的同名成员,显式地使用类名限定符:
类名 :: 成员
2.3 派生类中访问静态成员
Ø 基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员)
Ø 根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质
Ø 派生类中访问静态成员,用以下形式显式说明:
类名 :: 成员
或通过对象访问 对象名 . 成员
3. 基类的初始化
Ø 在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据
Ø 派生类构造函数声明为
派生类构造函数( 变元表) : 基类 ( 变元表 ) , 对象成员1( 变元表)
… 对象成员n ( 变元表 ) ;
Ø 构造函数执行顺序:基类 à 对象成员à 派生类
派生类构造函数和析构函数的定义规则
派生类构造函数和析构函数的使用原则
- 基类的构造函数和析构函数不能被继承
- 如果基类没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数
- 如果基类无无参的构造函数,派生类必须定义构造函数
- 如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造
- 派生类是否定义析构函数与所属的基类无关
派生类构造函数的定义
在C++中,派生类构造函数的一般格式为:
派生类::派生类名(参数总表):基类名(参数表)
{
// 派生类新增成员的初始化语句
}
注意:这是基类有构造函数且含有参数时使用
派生类析构函数
(1)当派生类中不含对象成员时
●在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
●在撤消派生类对象时,析构函数的执行顺序是:派生类的析构函数→基类的析构函数。
(2)当派生类中含有对象成员时
●在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;
●在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→基类的析构函数。
多继承的构造函数
派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)
{
// 派生类新增成员的初始化语句
}
多继承的析构函数
●析构函数名同样与类名相同,无返回值、无参数,而且其定义方式与基类中的析构函数的定义方式完全相同。
●功能是在派生类中对新增的有关成员进行必要的清理工作。
●析构函数的执行顺序与多继承方式下构造函数的执行顺序完全相反,首先对派生类新增的数据成员进行清理,再对派生类对象成员进行清理,最后才对基类继承来的成员进行清理。
赋值兼容规则
●赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
赋值兼容规则中所指的替代包括以下的情况:
a 派生类的对象可以赋给基类对象
b 派生类的对象可以初始化基类的引用
c 派生类的对象的地址可以赋给基类类型的指针
赋值兼容的可行性
n 通过公有继承,
n 派生类得到了除了构造、析构函数以外的所有成员
n 且这些成员的访问控制属性也和基类完全相同。
n 这样,它便具备了基类的所有功能。
n 利用赋值兼容规则
n a 派生类的对象可以赋给基类对象(强制类型转换)
n b 派生类的对象可以初始化基类的引用
n c 派生类的对象的地址可以赋给基类类型的指针
二.
1.
定义一个基类person(不定义构造函数)
姓名、性别、年龄(访问权限设置为私有)
定义公有的成员函数set_p()
定义公有的成员函数display_p(),显示person的信息
再由基类派生出学生类(不定义构造函数,采用公有继承的方式)
增加学号、班级、专业和入学成绩
定义公有成员函数set_t()
定义成员函定义公有的成员函数display_s(),显示所有的信息
#include<iostream>
#include <string>
using namespace std;
class Person
{
stringname;
intage;
stringsex;
public:
voidset_p() {
cout<<"name\tage\tsex"<<endl;
cin>>name>>age>>sex;
}
voidshow_p() {
cout<<name<<" "<<age<<" "<<sex<<endl;
}
};
class student :public Person
{
stringno;
stringzhuanye;
stringt_class;
floatscore;
public:
voidset_t(){
set_p();//调用继承于基类的成员函数访问继承于基类的私有数据成员
cout<<"zhuanye\tt_class\tscore"<<endl;
cin>>zhuanye>>t_class>>score;
}
voidshow_t() {
show_p();
cout<<zhuanye<<" "<<t_class<<" "<<score<<endl;
}
};
2.
// Point 类的成员函数
// 构造函数,调用成员函数对 x,y作初始化
Point::Point ( int a, int b )
{ setPoint ( a , b ) ; }
// 对数据成员置值
void Point :: setPoint ( int a, int b) { x = a ; y = b ; }
// 重载插入算符,输出对象数据
ostream &operator<< ( ostream&output , const Point &p )
{ output << '[' << p.x<< "," << p.y << "]" ;
return output ;
}
3.
// Point 类的成员函数
// 构造函数,调用成员函数对 x,y作初始化
Point::Point ( int a, int b )
{ setPoint ( a , b ) ; }
// 对数据成员置值
void Point :: setPoint ( int a, int b) { x = a ; y = b ; }
// 重载插入算符,输出对象数据
ostream &operator<< ( ostream&output , const Point &p )
{ output << '[' << p.x<< "," << p.y << "]" ;
return output ;
}
4.多继承的简单应用
class Base1
{ public:
Base1(int x) { value = x ; }
int getData() const { return value ; }
protected:
int value;
};
class Base2
{ public:
Base2(char c) { letter=c; }
char getData() const { return letter;}
protected:
char letter;
};
class Derived : public Base1, publicBase2
{ friend ostream &operator<< ( ostream &, const Derived& ) ;
public :
Derived ( int, char, double ) ;
double getReal() const ;
private :
double real ;
};
三.
赋值兼容应注意的问题
(1)声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。例如:
class B {…};
class D:private B {…};
B b1,*pbl;D d1;
pb1=&b1; //合法,基类B的对象b1和B类的指针
pb1=&d1; //非法,不允许将基类指针指向它的私有派生类对象
(2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
(3) 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。