基类
派生类
继承就是从先辈处得到属性和行为特征
1.不必重新描述派生类B的所有特性,只需让它继承类A的特性;
2. 增加类B与基类A不同的那些特性。
继承方式:
公用(public):访问权限最高;除派生类外,外部函数也可以直接访问(无论是成员变量还是成员函数)。 私有(private):访问权限最低;只能是该类内部函数互相调用,派生类、外部函数都不能访问。(默认) 保护(protect):访问权限中间;该类内部函数、派生类都能访问,外部类、外部函数不能访问,但是可以通过基类提供的公有成员函数访问。
派生类对基类成员的访问形式主要有以下两种: (1)内部访问 由派生类中新增的成员函数(比如print()函数)对基类继承来的成员的访问 (2)对象访问 在派生类外部(比如main函数里面)通过派生类的对象对从基类继承来的成员的访问
(对象只能访问公有成员)
class A { private: int X; public: int Y; protected: int Z; };
class B : private A{ X 基类中的私有成员不允许派生类继承,(注意是否可以被访问?如果能被访问以什么方式出现在派生类?) 即在派生类中是不可直接访问的。 Y 基类中的公有成员在派生类中 是以私有成员的身份出现的。 Z 基类中的保护成员在派生类中 是以私有成员的身份出现的。 };
class B : public A{
X 基类中的私有成员不允许派生类继承,
即在派生类中是不可直接访问的。
Y 基类中的公有成员在派生类中
仍以公有成员的身份出现的。
Z 基类中的保护成员在派生类中
仍以保护成员的身份出现的。
};
class B : protected A{
X 基类中的私有成员不允许派生类继承,
即在派生类中是不可直接访问的。
Y 基类中的公有成员在派生类中
是以保护成员的身份出现的。
Z 基类中的保护成员在派生类中
仍以保护成员的身份出现的。
};
通常情况下: 当创建派生类对象时:
基类的构造函数->派生类的构造函数
当撤消派生类对象时:
派生类的析构函数->基类的析构函数
派生类
derive(int i ):base(i)
1、当基类含有带参数的构造函数时,派生类必须定义构造函数,并缀上“:基类名(参数表) ”,以提供把参数传递给基类构造函数的途径,仅仅起参数的传递作用,可能该派生类的函数体为空。
说明:
(1)可以将派生类构造函数定义在类的外部,而在类体内只写该函数的声明。
在派生类中声明构造函数的原型:
derive(int a,int b);
而在类的外部定义派生类的构造函数:
derive(int a,int b):base(b)
{
cout<<"调用派生类的构造函数\n"<<endl;
j=a;}
派生类的一般构造函数:
派生类名(参数总表):基类名(参数表)
{
派生类新增成员的初始化语句
}
(2)缺省值=默认值
当构造函数参数表中并不全部使用缺省参数时,具有缺省值的参数必须放置于参数表的最后。
例如:
coordinates (int i, int j=100) 的参数表是对的;
coordinates (int i=100, int j) 的参数表是错的。
若基类使用不带参数的构造函数或带缺省参数的构造函数,则在派生类中定义构造函数时可略去“∶基类(参数表)”;
(3)如果派生类的基类也是一个派生类,每个派生类只需负责其直接基类的构造,依次上溯。
(明确函数的成员变量和函数变量)
2、析构函数是独立的,
基类函数有无参数跟派生类的析构函数一点关系都没有
3、当派生类中含有对象成员时,其构造函数的一般形式为:
派生类名(参数总表):基类名(参数表0),对象名成员1(参数表1),…,对象成员名n (参数表n)
{
//派生类新增成员的初始化语句 ..
}
例如: derived(int i):base(i),d(i)
定义派生类对象时,构造函数的执行顺序如下:(若main函数的内容体只有实例化对象的时候就打印构造函数与析构函数)
·调用基类的构造函数;
·调用对象成员的构造函数;
·执行派生类的构造函数体。
撤消对象时,析构函数的调用顺序与构造函数的调用顺序正好相反。
.调用派生类的析构函数;
.调用对象成员的析构函数;
.调用基类的析构函数。
4.3调整基类成员在派生类中的访问属性的其他方法:
4.3.1、如果在派生类中定义了与基类成员同名的成员,则派生类成员覆盖了基类的同名成员
4.3.2、若要访问基类中的f(),可改写成X::f();
4.4.4、访问声明
问题的提出:
能否使私有继承的派生类的对象访问基类的公有成员或保护成员?
解决方法1 :通过公有成员函数间接访问
解决方法2:使用访问声明
例如 A::print; (在B类声明要访问A的print方法,把基类中的数据成员或函数调整为派生类的公有xx)
备注:1、访问声明中只能含不带类型和参数的函数名或变量名。
2、数据成员也可以使用访问声明
3、访问声明不能改变类成员在基类中原来的性质
例如x在A类是public,那么x在B类中的声明访问也要是public
4、对于基类中的重载函数使用访问声明时要慎重
4.4 多重继承
4.4.1 多重继承的声明4.4.2 多重继承的构造函数与析构函数
4.4.3 虚基类
4.4.1多重继承额声明
当一个派生类具有多个基类时,这种派生方法称为多重派生或多重继承。
class z:private x,public y( 类z私有继承了类x,公有继承了类y)
4.4.2 多重继承的构造函数与析构函数
明确单继承与多继承的不同:
多重继承构造函数的执行顺序与单继承构造函数的执行顺序相同
析构函数的执行顺序则刚好与构造函数的执行顺序相反
4.4.3 虚基类
构造函数执行顺序:B()-> B1()-> B()-> B2()-> D()
class D:public B1,public B2{ . . .}
提出问题:派生类D中访问共同基类B中的成员a时,可能会产生二义性,怎么解决二义性?
解决办法1:在程序中注明B1::a和B2::a
解决办法2:使从不同的路径继承的基类的成员在内存中只拥有一个拷贝
虚基类是在派生类的声明,其语法形式如下:
class 派生类名:继承方式 virtual 基类名
{ … }
或
class 派生类名: virtual 继承方式 基类名
{ … }
构造函数执行顺序:B()-> B1()-> B2()-> D()(只调用B()一次,且是在第一次出现时调用。)
B(int sa)
B1(int sa,int sb):B(sa)
B2(int sa,int sc):B(sa)
D(int sa,int sb,int sc,int sd): B(sa),B1(sa,sb),B2(sa,sc)
调用顺序:
先调用虚基类的构造函数,
再调用非虚基类的构造函数,
最后调用派生类构造函数;
对于多个虚基类,构造函数的执行顺序仍然是先左后右,自上而下;
对于非虚基类,构造函数的执行顺序仍是先左后右,自上而下;
总结:
公有派生类具有基类的全部功能,凡是基类能够实现的功能,公有派生类都能实现。
我们可以将派生类对象的值赋给基类对象,在用到基类对象的时候可以用其派生类对象代替---基类和派生类对象之间的赋值兼容规则。
说明:所谓赋值仅仅指对基类的数据成员赋值