*不论种方式继承基类,派生类都不能直接使用基类的私有成员;
*派生类的生成过程经历了三个步骤:
-吸收基类成员(全部吸收(构造、析构除外),但不一定可见)
-改造基类成员
-添加派生类新成员
(1)吸收基类成员:在C++的继承机制中,派生类吸收基类中除构造函数和析构函数之外的全部成员。
(2)改造基类成员:通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员。
(3)添加新成员:仅仅继承基类的成员是不够的,需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现。
派生类对象结构示例:
#include
using namespace std;
class A{
public:
int a;
int b;
private:
int c;
protected:
int d;
};
class B: public A
{ int c; };
main( ){
cout << ” size of A is” << sizeof(A);
cout << ” size of B is” << sizeof(B); }
*派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽(hide)了基类的同名成员
*在派生类中使用基类的同名成员,显式地使用类名限定符:
类名 :: 成员
1.重名数据成员:
示例:
#include
using namespace std ;
class A
{ public:
int a1, a2 ;
A( int i1=0, int i2=0 ) { a1 = i1; a2 = i2; }
void print()
{ cout << "a1=" << a1 << '\t' << "a2=" << a2 << endl ; }
};
class B : public A
{ public:
int b1, b2 ;
B( int j1=1, int j2=1 ) { b1 = j1; b2 = j2; }
void print() //定义同名函数
{ cout << "b1=" << b1 << '\t' << "b2=" << b2 << endl ; }
#include
using namespace std ;
class B
{ public:
static void Add() { i++ ; }
static int i;
void out() { cout<<"static i="<*在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据
*派生类构造函数声明为:
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )
… 对象成员n ( 变元表 ) ;
*构造函数执行顺序:基类--对象成员--派生类
示例:
#include
using namespace std ;
class parent_class
{ int data1 , data2 ;
public :
parent_class ( int p1 , int p2 ) { data1 = p1; data2 = p2; }
int inc1 () { return ++ data1; }
int inc2 () { return ++ data2 ; }
void display () {cout << "data1=" << data1 << " , data2=" << data2 << endl ; }
};
class derived_class : private parent_class
{ int data3 ;
parent_class data4 ;
public:
derived_class ( int p1 , int p2 , int p3 , int p4 , int p5 ): parent_class ( p1 , p2 ) , data4 ( p3 , p4 )
{ data3 = p5 ; }
int inc1 ( ) { return parent_class :: inc1 ( ) ; }
int inc3 ( ) { return ++ data3 ; }
void display ( )
{ parent_class :: display ( ) ; data4.display ( ) ;
cout << "data3=" << data3 << endl ;
}
} ;
int main ( )
{ derived_class d1 ( 17 , 18 , 1 , 2 , -5 ) ; d1 . inc1 ( ) ; d1 . display ( ) ; }
**派生类构造函数和析构函数的定义规则:
派生类构造函数和析构函数的使用原则:
-基类的构造函数和析构函数不能被继承;
-如果基类没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数;
-如果基类无无参的构造函数,派生类必须定义构造函数;
-如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造;
-派生类是否定义析构函数与所属的基类无关.
四.多继承:
*一个类有多个直接基类的继承关系称为多继承
*多继承声明语法
class 派生类名 : 访问控制 基类名1 , 访问控制 基类名2 , … , 访问控制 基类名n
{
数据成员和成员函数声明
};
1.多继承的派生类构造和访问:
*多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员。
*执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。
*一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别。
示例:
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, public Base2
{ friend ostream &operator<< ( ostream &, const Derived & ) ;
public :
Derived ( int, char, double ) ;
double getReal() const ;
private :
double real ;
};
int main()
{ Base1 b1 ( 10 ) ;
Base2 b2 ( 'k' ) ;
Derived d ( 5, 'A', 2.5 ) ;
:
return ;
}
2.多继承方式下构造函数的执行顺序:
多继承方式下构造函数的执行顺序:
●先执行所有基类的构造函数
●再执行对象成员的构造函数
●最后执行派生类的构造函数
*处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的基类顺序与派生类构造函数中所定义的成员初始化列表顺序没有关系。
*内嵌对象成员的构造函数执行顺序与对象在派生类中声明的顺序一致。
3.赋值兼容的可行性:
通过公有继承:
*派生类得到了除了构造、析构函数以外的所有成员
*且这些成员的访问控制属性也和基类完全相同。
*这样,它便具备了基类的所有功能。
利用赋值兼容规则:
a.派生类的对象可以赋给基类对象(强制类型转换)
b.派生类的对象可以初始化基类的引用
c.派生类的对象的地址可以赋给基类类型的指针
--例如,下面声明的两个类:
class Base{
…
};
class Derived:public Base{
…
};
根据赋值兼容规则, 以下几种情况是合法的:
(1).可以用派生类对象给基类对象赋值。例如:
Base b;
Derived d;
b=d;
这样赋值的效果是,对象b中所有数据成员都将具有对象d中对应数据成员的值。
(2).可以用派生类对象来初始化基类的引用。例如:
Derived d;
Base &br=d;
(3).可以把派生类对象的地址赋值给指向基类的指针。例如:
Derived d;
Base *bptr=&d;
这种形式的转换,是在实际应用程序中最常见到的。
(4).可以把指向派生类对象的指针赋值给指向基类对象的指针。例如:
Derived *dptr,obj; dptr=&obj;
Base *bptr=dptr;
4.赋值兼容应注意的问题:
(1)声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。例如:
class B {…};
class D:private B {…};
B b1,*pbl;D d1;
pb1=&b1; //合法,基类B的对象b1和B类的指针
pb1=&d1; //非法,不允许将基类指针指向它的私有派生类对象
(2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
(3)声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。
五.总结:
C++的继承是重写面对类与对象程序时的重要优化手段,将各个功能相似的类联系起来,重组成一个基类,并在基类的基础上进行编写,可是算短编码的长度,提高效率。但是,继承的掌握有一定的难度,首先要确定基类及每个基类应该包含的内容。所以,要通过反复的练习来总结规律,从而扎实掌握,提高程序的可行性可用性。