继承函数总结
Ø 继承:在已有类的基础上创建新类的过程
Ø 一个 B 类继承A类,或称从类 A 派生类 B
类 A 称为基类(父类),类 B 称为派生类(子类)
其语法形式:
class 派生类名 : 基类名表
{
数据成员和成员函数声明
};
基类名表 构成
访问控制 基类名1, 访问控制 基类名2 ,… , 访问控制 基类名n
访问控制 表示派生类对基类的继承方式,使用关键字:
public 公有继承
private 私有继承
protected 保护继承
虽然有这么多的继承方式 但是我们只使用公有继承。
关于访问控制:
不论以何种方式继承,派生类都不能直接使用基类的私有成员。
派生类的生成过程经历了三个步骤:
●吸收基类成员(全部吸收(构造、析构除外),但不一定可见)
●改造基类成员
●添加派生类新成员
1.吸收基类成员
在继承机制中,派生类吸收基类中初构造函数和析构函数之外的全部成员。
2.改造基类成员
通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员。
3.添加新成员
仅仅是继承基类的成员是不够的,需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现。
对于下面的继承关系:
class Father
{
int a,b;
public:
// 成员函数
};
class Son:public Father
{
int c;
public:
// 成员函数
};
n #include <iostream>
n using namespace std;
n class A{
n public:
n inta;
n intb;
n private:
n intc;
n protected:
n intd;
n };
n class B: public A
n { int c; };
n main( ){
n cout<< ” sizeof A is” << sizeof(A);
n cout<< ” sizeof B is” << sizeof(B); }
1. 公有继承
#include<iostream>
using namespace std ;
class A
{ public :
void get_XY() { cout <<"Enter two numbers of x, y : " ; cin >> x >> y ; }
void put_XY() { cout <<"x = "<< x << ", y = " << y << '\n'; }
protected: int x, y ;
};
class B : public A
{ public :
int get_S() { return s ; };
void make_S() { s = x * y ; }; //使用基类数据成员x,y
protected: int s;
};
class C : public B
{ public :
void get_H() { cout <<"Enter a number of h : " ; cin>> h ; }
int get_V() { return v ; }
void make_V() { make_S(); v = get_S()* h ; } //使用基类成员函数
protected: int h, v;
};
int main()
{ A objA ;
B objB ;
C objC ;
cout <<"It is object_A :\n" ;
objA.get_XY();
objA.put_XY();
cout <<"It is object_B :\n" ;
objB.get_XY();
objB.make_S();
cout <<"S = " << objB.get_S() << endl ;
cout <<"It is object_C :\n" ;
objC.get_XY();
objC.get_H();
objC.make_V();
cout <<"V = " << objC.get_V() << endl ;
}
重名成员:
Ø 派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽(hide)了基类的同名成员
Ø 在派生类中使用基类的同名成员,显式地使用类名限定符:
类名 :: 成员
1. 重名数据成员
class base
{ public :
int a , b ;
} ;
class derived :public base
{ public:
int b , c ;
} ;
void f ()
{ derived d ;
d . a = 1 ;
d . base :: b= 2 ;
d . b = 3 ;
d . c = 4 ;
};
#include<iostream> //例8-4
using namespace std ;
class A
{ public:
int a1,a2 ;
A( inti1=0, int i2=0 ) { a1 = i1; a2 = i2; }
voidprint()
{ cout<< "a1=" << a1 << '\t' << "a2="<< a2 << endl ; }
};
class B : public A
{ public:
int b1,b2 ;
B( intj1=1, int j2=1 ) { b1 = j1; b2 = j2; }
voidprint() //定义同名函数
{ cout<< "b1=" << b1 << '\t' << "b2="<< b2 << endl ; }
void printAB()
{A::print() ;//派生类对象调用基类版本同名成员函数
print() ; //派生类对象调用自身的成员函数
}
};
int main()
{ B b ; b.A::print(); b.printAB(); }
派生类中访问静态成员
Ø 基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员)
Ø 根据静态成员自身的访问特性和派生类的继承方式,在类层次体系中具有不同的访问性质
Ø 派生类中访问静态成员,用以下形式显式说明:
类名 :: 成员
或通过对象访问 对象名 . 成员
#include<iostream>
using namespace std ;
class B
{ public:
static voidAdd() { i++ ; }
static inti;
void out() {cout<<"static i="<<i<<endl; }
};
int B::i=0;
class D : private B
{ public:
void f()
{ i=5;
Add();
B::i++;
B::Add();
}
};
int main()
{ B x; D y;
x.Add();
x.out();
y.f();
cout<<"static i="<<B::i<<endl;
cout<<"static i="<<x.i<<endl;
//cout<<"statici="<<y.i<<endl;
}
基类的初始化:
Ø 在创建派生类对象时用指定参数调用基类的构造函数来初始化派生类继承基类的数据
Ø 派生类构造函数声明为
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )
… 对象成员n ( 变元表 ) ;
Ø 构造函数执行顺序:基类 à对象成员à派生类
// 例8-6 调用构造函数顺序测试,构造函数无参数
#include<iostream>
using namespace std ;
class Base
{ public: Base ( ) { cout << "Basecreated.\n" ; }
} ;
class D_class :public Base
{ public: D_class ( ) { cout <<"D_class created.\n" ; }
} ;
int main ( )
{ D_class d1 ; }
基类 à对象成员à派生类
#include<iostream>
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 ( ) ; }
派生类构造函数和析构函数的使用原则
1. 基类的构造函数和析构函数不能被继承
2. 如果基类没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数
3. 如果基类无无参的构造函数,派生类必须定义构造函数
4. 如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造
5. 派生类是否定义析构函数与所属的基类无关
派生类的数据成员既包括基类的数据成员,也包括派生类新增数据成员。Derived为base的派生类。
问题:如何对基类中的成员进行初始化?
调用基类构造函数对基类成员进行初始化。
基类的数据用基类的构造函数进行初始化,而派生类的自定义函数则有派生类的构造函数进行初始化。
派生类构造函数的定义
在C++中,派生类构造函数的一般格式为:
派生类::派生类名(参数总表):基类名(参数表)
{
// 派生类新增成员的初始化语句
}
注意:这是基类有构造函数且含有参数时使用
派生类析构函数
(1)当派生类中不含对象成员时
●在创建派生类对象时,构造函数的执行顺序是:基类的构造函数→派生类的构造函数;
●在撤消派生类对象时,析构函数的执行顺序是:派生类的析构函数→基类的析构函数。
(2)当派生类中含有对象成员时
●在定义派生类对象时,构造函数的执行顺序:基类的构造函数→对象成员的构造函数→派生类的构造函数;
●在撤消派生类对象时,析构函数的执行顺序:派生类的析构函数→对象成员的析构函数→基类的析构函数。
class B
{
public:
B() {cout<<"B()"<<endl; }
~B() { cout<<"~B()"<<endl; }
};
class D : public B
{
public:
D(){cout<<"D()"<<endl; }
~D() {cout<<"~D()"<<endl; }
};
int main()
{
D d;
return 0;
}
分析以下程序的执行结果
#include<iostream.h>
class base
{
public:
base(){cout<<"constructingbase class"<<endl;}//1
~base(){cout<<"destructingbase class"<<endl; }//4
};
class subs:public base
{
public:
subs(){cout<<"constructingsub class"<<endl;}//2
~subs(){cout<<"destructingsub class"<<endl;}//3
};
void main()
{
subs s;
}
分析以下程序的执行结果
#include<iostream.h>
class base {
int n;
public:
base(int a) {
cout<<"constructingbase class"<<endl;
n=a; cout<<"n="<<n<<endl; }
~base(){cout<<"destructingbase class"<<endl;}
};
class subs:public base {
base bobj; int m;
public:
subs(inta,int b,int c):base(a),bobj(c) {
cout<<"constructingsub cass"<<endl;
m=b; cout<<"m="<<m<<endl; }
~subs(){cout<<"destructingsub class"<<endl;}
|
};
void main() {
subss(1,2,3); }
多继承
Ø 一个类有多个直接基类的继承关系称为多继承
Ø 多继承声明语法
class 派生类名 : 访问控制 基类名1 , 访问控制 基类名2 , … , 访问控制 基类名n
{
数据成员和成员函数声明
};
Ø 类 C 可以根据访问控制同时继承类 A 和类 B 的成员,并添加自己的成员
多继承的派生类构造和访问
Ø 多个基类的派生类构造函数可以用初始式调用基类构造函数初始化数据成员。
Ø 执行顺序与单继承构造函数情况类似。多个直接基类构造函数执行顺序取决于定义派生类时指定的各个继承基类的顺序。
Ø 一个派生类对象拥有多个直接或间接基类的成员。不同名成员访问不会出现二义性。如果不同的基类有同名成员,派生类对象访问时应该加以识别。
多继承的构造函数
派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)
{
// 派生类新增成员的初始化语句
}
多继承方式下构造函数的执行顺序
多继承方式下构造函数的执行顺序:
●先执行所有基类的构造函数
●再执行对象成员的构造函数
●最后执行派生类的构造函数
处于同一层次的各基类构造函数的执行顺序取决于定义派生类时所指定的基类顺序
与派生类构造函数中所定义的成员初始化列表顺序没有关系。
内嵌对象成员的构造函数执行顺序与对象在派生类中声明的顺序一致
多继承的析构函数
析构函数名同样与类名相同,无返回值、无参数,而且其定义方式与基类中的析构函数的定义方式完全相同。
功能是在派生类中对新增的有关成员进行必要的清理工作。
析构函数的执行顺序与多继承方式下构造函数的执行顺序完全相反,首先对派生类新增的数据成员进行清理,再对派生类对象成员进行清理,最后才对基类继承来的成员进行清理。
赋值兼容规则
赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
赋值兼容规则中所指的替代包括以下的情况:
a 派生类的对象可以赋给基类对象
b 派生类的对象可以初始化基类的引用
c 派生类的对象的地址可以赋给基类类型的指针
赋值兼容的可行性
通过公有继承,
派生类得到了除了构造、析构函数以外的所有成员
且这些成员的访问控制属性也和基类完全相同。
这样,它便具备了基类的所有功能。
利用赋值兼容规则
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;
赋值兼容规则的特点
●在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
一个派生类对象也是一个基类对象,一个基类对象可派上用场的地方,派生类对象一样可派上用场。反之则不然。
class Person {…};
class Student : publicPerson { … };
void eat(Person &p){};
void study(Student&s){};
void main( )
{
Person p;
Student s;
eat(p); // OK
eat(s); // OK 学生也要吃饭
study(s); // OK
study(p); // error C2664: “study”: 不能将参数 1 从“Person”转换Student&”
}
赋值兼容应该注意的问题
(1)声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。例如:
class B {…};
class D:private B {…};
B b1,*pbl;D d1;
pb1=&b1; //合法,基类B的对象b1和B类的指针
pb1=&d1; //非法,不允许将基类指针指向它的私有派生类对象
(2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
(3) 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。
从word文档中,没能显示图片。