一知识总结
继承分为保护继承(protected),公有继承(public),以及私有继承(private)
不论哪种继承都会将基类中的成员(包括数据成员和成员函数,但是析构函数和构造函数不包括)吸收过来,也就是派生类隐含这些成员。
不论种方式继承基类,派生类都不能直接使用基类的私有成员
对于下面的继承关系:
class Father
{
int a,b;
public:
// 成员函数
};
class Son:public Father
{ father类里的数据成员有a,b
int c; son类里的数据成员有a,b,c
Son继承了father
public:
// 成员函数
};
#include<iostream>
#include <string>
using namespace std;
class Person
{
string name;
int age;
string sex;
public:
void set() {
cout<<"name\tage\tsex"<<endl;
cin>>name>>age>>sex;
}
void show() {
cout<<name<<" "<<age<<" "<<sex<<endl;
}
};
class student :public Person
{
string no;
string zhuanye;
string t_class;
float score;
public:
void set(){ //隐藏了基类中的同名成员
Person::set(); //调用继承于基类的成员函数访问继承于基类的数据成员
cout<<"zhuanye\tt_class\tscore"<<endl;
cin>>zhuanye>>t_class>>score;
}
void show() {
Person::show();//这样就可以调用基类的函数,间接的访问了基类的私有成员。
cout<<zhuanye<<" "<<t_class<<" "<<score<<endl;
}
};
重名成员
派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽(hide)了基类的同名成员
在派生类中使用基类的同名成员,显式地使用类名限定符: 类名 :: 成员
如果定义了重名成员,只要不使用类名限定符来调用,都是调用的派生类的成员,只有使用这种格式 类名::成员 才是调用的基类的成员,也可以这样调用基类的成员函数。
#include<iostream>
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 ; }
void printAB()
{ A::print() ; //派生类对象调用基类版本同名成员函数
print() ; //派生类对象调用自身的成员函数
}
};
int main()
{ B b ;
b.A::print();//这里调用的就是基类的print()函数。而不是派生类的print。
b.printAB();
}
基类的静态成员
基类定义的静态成员,将被所有派生类共享(基类和派生类共享基类中的静态成员),不论哪种继承方式,只要是基类的静态成员或者静态成员函数都可以在派生类中使用。
派生类中访问静态成员,用以下形式显式说明:类名 :: 成员 或通过对象访问 对象名 . 成员
派生类构造函数
派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 )
… 对象成员n ( 变元表 ) ;
构造函数执行顺序:先基类 à再对象成员à 后派生类
析构函数执行顺序:先派生类 再对象成员 后基类
例子
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;
}
运行结果:
B()
D()
~D()
~B()
对象d先执行基类的构造函数,由于没有对象成员,在执行派生类的构造函数,执行析构函数时正好相反,由运行结果可知。
#include<iostream.h>
class base {
int n;
public:
base(int a) {
cout<<"constructing base class"<<endl;
n=a; cout<<"n="<<n<<endl; }
~base(){cout<<"destructing base class"<<endl;}
};
class subs:public base {
base bobj; //bobj为对象成员,bobj再基类中为对象,在派生类中为成员,所以称为对象成员。
int m;
public:
subs(int a,int b,int c):base(a),bobj(c) //先执行基类构造函数,在执行对象成员初始化,最后在执行派生类构造函数。
{
cout<<"constructing sub cass"<<endl;
m=b;
cout<<"m="<<m<<endl;}
~subs()
{cout<<"destructing sub class"<<endl;}
};
void main() {
subs s(1,2,3); }
运行结果
constructing base class
n=1
constructing base class
n=3
constructing sub cass
m=2
destructing sub class
destructing base class
destructing base class
多继承(目前只是了解一下)
一个类有多个直接基类的继承关系称为多继承
多继承声明语法
class 派生类名 : 访问控制 基类名1 , 访问控制 基类名2 , … , 访问控制 基类名n
{
数据成员和成员函数声明
};
赋值兼容规则
赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
赋值兼容规则中所指的替代包括以下的情况:
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;
在替代之后,派生类对象就可以作为基类的对象使用,但只能使用从基类继承的成员。
(1)声明为指向基类的指针可以指向它的公有派生类的对象,但不允许指向它的私有派生类的对象。例如:
class B {…};
class D:private B {…};
B b1,*pbl;D d1;
pb1=&b1; //合法,基类B的对象b1和B类的指针
pb1=&d1; //非法,不允许将基类指针指向它的私有派生类对象
(2)允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向其基类的一个对象。
(3) 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。