1.继承是在已有类的基础上创建新类的过程,继承方式有公有继承(public)、保护继承(protected)、私有继承(private)。最常用的继承方式为共有继承(public),但不论种方式继承基类,派生类都不能直接使用基类的私有成员 (可以将基类数据成员设为protected)。
举例
class A{
protected: a[2]={1,2};
public:A(){}
void show(){
for(int i=0;i<2;i++){
cout<<a[i]<<" ";
}
}
}
class B:A{
public: B(){}
void show(){
A::show();
}
}
int main(){
A a;
B b;
a.show();
b.show();//两个函数输出的结果相同
}
类B公有继承了类A,两个函数输出的结果相同,类A中数组a也可以设置为私有成员(private),因为类B直接调用了类A的公有方法,不影响最后的结果输出。
派生类的生成过程 :
- 吸收:吸收基类除了构造函数和析构函数以外的所有成员。
- 改造:基类派生类中可能有同名的成员函数,如果用派生类调用该函数,默认调用派生类中的函数。
- 添加:派生类根据具体需求添加所需要的成员函数。
举例
class A{
private: int a,b;
public: A(){}
}
class B:A{
private:int c;
public:B(){}
}
int main(){
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
}
类B继承了类A,虽然代码中类B只用数据成员int c,但类B对象空间也有int a,int b,所以子类对象空间总是不小于基类对象。最后的输出,sizeof(A)应该为8,而sizeof(B)应该为12。
例题
- 定义一个基类person(不定义构造函数)
- 姓名、性别、年龄(访问权限设置为私有)
- 定义公有的成员函数set_p()
- 定义公有的成员函数display_p(),显示person的信息
- 再由基类派生出学生类(不定义构造函数,采用公有继承的方式)
- 增加学号、班级、专业和入学成绩
- 定义公有成员函数set_t()
- 定义成员函定义公有的成员函数display_s(),显示所有的信息
#include<iostream>
#include <string>
using namespace std;
class Person
{
string name;
int age;
string sex;
public:
void set_p() {
cout<<"name\tage\tsex"<<endl;
cin>>name>>age>>sex;
}
void show_p() {
cout<<name<<" "<<age<<" "<<sex<<endl;
}
};
class student :public Person
{
string no;
string zhuanye;
string t_class;
float score;
public:
void set_t(){
set_p(); //调用继承于基类的成员函数访问继承于基类的私有数据成员
cout<<"zhuanye\tt_class\tscore"<<endl;
cin>>zhuanye>>t_class>>score;
}
void show_t() {
show_p();
cout<<zhuanye<<" "<<t_class<<" "<<score<<endl;
}
};
重名成员
派生类和基类中有相同名字的数据成员,在派生类对象访问这些同名的数据成员的时候回屏蔽掉基类的同名的数据成员。
例
class A{
public:int a=1;
A(){}
}
class B{
public:int a=3;
B(){}
}
int main(){
B b;
cout<<b.a<<" "<<b.base::a<<endl;
}
输出结果为:3 1 这就是屏蔽掉基类中的同名成员。
派生类中访问静态成员
基类中定义的静态成员可以被其所有的派生类访问。
例
class A{
public: static int i=1;
}
class B:A{
public:void add(){
i++;
}
}
class C:A{
public:void add(){
i++;
}
int main(){
cout<<B.add()<<" "<<C.add();
}
最后输出的结果为:2 3 派生类B,C都访问了基类A的静态成员 i。
基类的初始化
先执行基类的构造函数,然后再执行派生类的构造函数
例
class A{
public:A(){ cout<<"1"; }
}
class B:A{
public: B(){cout<<"2";}
}
int main(){
B b;
}
最后程序的输出为:1 2 可以看出构造函数的执行顺序。
- 派生类构造函数 ( 变元表 ) : 基类 ( 变元表 ) , 对象成员1( 变元表 ) … 对象成员n ( 变元表 ) ;
例
#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 ( ) ; }
多继承
多继承机制是C++语言的独特机制,就是一个派生类其有多个基类,基类的执行顺序是按照声明顺序执行的。
例
class A{
public:A(){
cout<<"A";
}
}
class B{
public:B(){
cout<<"B";
}
}
class C:public A,public B{
public: C(){
cout<<"C";
}
}
int main(){
C c;
}
最后输出的结果为:A B C 这就是构造函数的执行顺序
多态性
- 是指一个名字,多种语义;或界面相同,多种实现。
- 重载函数是多态性的一种简单形式。
联编
- 虚函数允许函数调用与函数体的联系在运行时才进行,称为动态联编。
- 联编分成两大类:静态联编和动态联编。 静态联编优点:调用速度快,效率高,但缺乏灵活性;动态联编优点:运行效率低,但增强了程序灵活性。
- 为了实现多态性,利用虚函数机制,可部分地采用动态联编。
重载
普通成员函数重载可表达为两种形式:
-
在一个类说明中重载
-
基类的成员函数在派生类重载。有 3 种编译区分方法:
-
(1)根据参数的特征加以区分
void Show ( int , char );
void Show ( char * , float ); 不是同一函数,编译能够区分 -
(2)使用“ :: ”加以区分
A :: Show ( );
有别于 B :: Show ( ); -
(3)根据类对象加以区分
Aobj . Show ( ) 调用 A :: Show ( )
Bobj . Show ( ) 调用 B :: Show ( )
指针
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
-
直接用基类指针引用基类对象;
-
直接用派生类指针引用派生类对象;
-
用基类指针引用一个派生类对象;
-
用派生类指针引用一个基类对象。
例如:
- A * p ; // 指向类型 A 的对象的指针
- A A_obj ; // 类型 A 的对象
- B B_obj ; // 类型 B 的对象
- p = & A_obj ; // p 指向类型 A 的对象
- p = & B_obj ; // p 指向类型 B 的对象,它是 A 的派生类
利用 p,可以通过 B_obj 访问所有从 A 类继承的元素 ,但不能用 p访问 B 类自定义的元素 (除非用了显式类型转换)
例
#include<iostream>
#include<cstring>
using namespace std ;
class A_class
{ char name[20] ;
public : void put_name( char * s ) { strcpy_s( name, s ) ; }
void show_name() { cout << name << "\n" ; }
};
class B_class : public A_class
{ char phone_num[ 20 ] ;
public : void put_phone( char * num ) { strcpy_s ( phone_num , num ) ; }
void show_phone() { cout << phone_num << "\n" ; }
};
int main()
{ A_class * A_p ; A_class A_obj ;
B_class B_obj ;
A_p = & A_obj ;
A_p -> put_name( "Wang xiao hua" ) ; A_p -> show_name() ;
A_p = & B_obj ;
A_p -> put_name( "Chen ming" ) ; A_p -> show_name() ;
B_obj.put_phone ( "5555_12345678" );
( ( B_class * ) A_p ) -> show_phone() ;
}
实现动态联编方式的前提
- 先要声明虚函数
- 类之间满足赋值兼容规则
- 通过指针与引用来调用虚函数。
虚函数
- 冠以关键字 virtual 的成员函数称为虚函数
- 实现运行时多态的关键首先是要说明虚函数,另外,必须用 基类指针调用派生类的不同实现版本
- 一个虚函数,在派生类层界面相同的重载函数都保持虚特性
- 虚函数必须是类的成员函数
- 不能将友元说明为虚函数,但虚函数可以是另一个类的友元
- 析构函数可以是虚函数,但构造函数不能是虚函数
#include<iostream>
using namespace std ;
class Base
{ public : Base(char xx) { x = xx; }
virtual void who() { cout << "Base class: " << x << "\n" ; }
protected: char x;
} ;
class First_d : public Base
{ public : First_d(char xx, char yy):Base(xx) { y = yy; }
void who() { cout << "First derived class: "<< x << ", " << y << "\n" ; }
protected: char y;
} ;
class Second_d : public First_d
{ public :
Second_d( char xx, char yy, char zz ) : First_d( xx, yy ) { z = zz; }
void who() { cout<<"Second derived class: "<<x<<", "<<y<<", "<<z<<"\n" ; }
protected: char z;
} ;
int main()
{ Base B_obj( 'A' ) ; First_d F_obj( 'T', 'O' ) ; Second_d S_obj( 'E', 'N', 'D' ) ;
Base * p ;
p = & B_obj ; p -> who() ;
p = &F_obj ; p -> who() ;
p = &S_obj ; p -> who() ;
}
纯虚函数和抽象类
- 纯虚函数是一种特殊的虚函数,
- 在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。
- 纯虚函数是一个在基类中说明的虚函数,在基类中没有定义, 要求任何派生类都定义自己的版本
- 纯虚函数为各派生类提供一个公共界面
- 纯虚函数说明形式: virtual 类型 函数名(参数表)= 0 ;
- 一个具有纯虚函数的基类称为抽象类。
#include<iostream>
using namespace std ;
class Number
{ public : Number (int i) { val = i ; }
virtual void Show() = 0 ;
protected: int val ;
};
class Hex_type : public Number
{ public: Hex_type(int i) : Number(i) { }
void Show() { cout << "Hexadecimal:" << hex << val << endl ; }
};
class Dec_type : public Number
{ public: Dec_type(int i) : Number(i) { }
void Show() { cout << "Decimal: " << dec << val << endl ; }
};
class Oct_type : public Number
{ public: Oct_type(int i) : Number(i) { }
void Show() { cout << "Octal: " << oct << val << endl ; }
};
void fun( Number & n ) // 抽象类的引用参数
{ n.Show() ; }
int main()
{ Dec_type n1(50);
fun(n1); // Dec_type::Show()
Hex_type n2(50);
fun(n2); // Hex_type::Show()
Oct_type n3(50);
fun(n3); // Oct_type::Show()
}