继承
类之间的关系
继承:
在已有类的基础上创建新类的过程
一个B类继承A类,或称从类A派生B类——A称为基类(父类),B称为派生类(子类)
基类和派生类
class 派生类名 : 基类名表
{
数据成员和成员函数声明
};
基类名表 :
访问控制 基类名1, 访问控制 基类名2 ,… , 访问控制 基类名n
注意:==种方式继承基类,派生类都不能直接使用基类的私有成员 ==
派生类的生成过程经历了三个步骤:
派生类的对象结构
公有继承
例题
定义一个基类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;//错误,没有输入基类继承过来的成员
cin>>name>>age>>sex>>zhuanye>>t_class>>score;
}
void show_t()
{
show_p();
//cout<<zhuanye<<" "<<t_class<<" "<<score<<endl;//没有显示学号
cout<<name<<" "<<age<<" "<<sex<<zhuanye<<" "<<t_class<<" "<<score<<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;
}
};
重名成员
派生类中访问静态成员
基类初始化
例题
调用构造函数顺序测试,构造函数无参数
#include<iostream>
using namespace std ;
class Base
{
public :
Base ( ) { cout << "Base created.\n" ; }
} ;
class D_class : public Base
{
//派生类构造函数
public :
D_class ( ) { cout << "D_class created.\n" ; }
} ;
int main ( )
{
D_class d1 ;//先调用基类构造函数再本身
}
派生类构造函数
==派生类构造函数和析构函数的定义规则 ==
派生类构造函数和析构函数的使用原则
- 类的构造函数和析构函数不能被继承
- 如果基类没有定义构造函数或有无参的构造函数, 派生类也可以不用定义构造函数
- 如果基类无无参的构造函数,派生类必须定义构造函数
- 如果派生类的基类也是派生类,则每个派生类只负责直接基类的构造
- 派生类是否定义析构函数与所属的基类无关
调用基类构造函数对基类成员进行初始化。
派生类::派生类名(参数总表):基类名(参数表)
{
// 派生类新增成员的初始化语句
}
派生类析构函数
继承的应用实例
class Point
{ friend ostream &operator<< (ostream &, const Point &);
public:
Point( int = 0, int = 0 ) ; // 带默认参数的构造函数
void setPoint( int, int ) ; // 对点坐标数据赋值
int getX() const { return x ; } int getY() const { return y ; }
protected: int x, y; // Point类的数据成员
};
class Circle : public Point
{ friend ostream &operator<< (ostream &, const Circle &); // 友元函数
public:
Circle(double r=0.0, int x=0, int y=0); // 构造函数
void setRadius(double); /*置半径*/ double getRadius() const; /*返回半径*/
double area() const; // 返回面积
protected: double radius; // 数据成员,半径
};
class Cylinder:public Circle
{ friend ostream & operator<<(ostream &, const Cylinder &); // 友元函数
public:
Cylinder(double h=0.0, double r=0.0, int x=0, int y=0); // 构造函数
void setHeight(double); /* 置高度值*/ double getHeight() const; /* 返回高度值*/
double area() const; /* 返回面积*/ double volume() const; /* 返回体积*/
protected: double height; // 数据成员,高度
};
多继承
class 派生类名 : 访问控制 基类名1 , 访问控制 基类名2 , … , 访问控制 基类名n
{
数据成员和成员函数声明
};
多继承的构造函数
派生类名(参数总表):基类名1(参数表1),基类名2(参数表2),…,基类名n(参数表n)
{
// 派生类新增成员的初始化语句
}
多继承方式下构造函数的执行顺序:
虚基类
如果一个派生类从多个基类派生,而这些基类又有一个共同的基类,则在对该基类中声明的名字进行访问时,可能产生二义性。
赋值兼容原则
赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
虚函数和多态
多态性(Polymorphism)是指一个名字,多种语义;或界面相同,多种实现。
静态联编
联编是指一个程序模块、代码之间互相关联的过程。
静态联编,是程序的匹配、连接在编译阶段实现,也称为早期匹配。
重载函数使用静态联编
类指针的关系
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
直接用基类指针引用基类对象;
直接用派生类指针引用派生类对象;
用基类指针引用一个派生类对象;
用派生类指针引用一个基类对象。
虚函数和动态编联
冠以关键字 virtual 的成员函数称为虚函数
注意实现运行时多态的关键首先是要说明虚函数,另外,必须用
基类指针调用派生类的不同实现版本
基类指针虽然获取派生类对象地址,却只能访问派生类从基类继承的成员
#include<iostream>
using namespace std ;
class Base
{ public : Base(char xx) { x = xx; }
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() ;
F_obj.who() ;
( ( Second_d * ) p ) -> who() ;
}
运行结果:
注意
一个虚函数,在派生类层界面相同的重载函数都保持虚特性
虚函数必须是类的成员函数
不能将友元说明为虚函数,但虚函数可以是另一个类的友元
析构函数可以是虚函数,但构造函数不能是虚函数
虚函数的重载
虚析构函数
例题
成员函数调用析构函数(采用动态联编):
class Animal
{
string name;
public:
Animal(string a_name):name(a_name){}
virtual void show(){} //将来被派生类继承
void show_name()
{
cout<< "The name is "<<name<<".“<<endl;
}
};
class Cat :public Animal
{
string kind;
public:
Cat(string a_name,string a_kind):Animal(a_name),kind(a_kind)
{}
void show(); //覆盖函数(同名同参数)
};
void Cat::show()
{
show_name();
cout<<" It's a "<<kind<<endl;
}
class Dog:public Animal
{
string kind;
public:
Dog(string a_name,string a_kind):Animal(a_name),kind(a_kind)
{}
void show();
};
void Dog::show()
{
show_name();
cout<<" It's a "<<kind<<endl;
}
class Tiger:public Cat
{
public:
Tiger(string a_name,string a_kind):Cat(a_name,a_kind)
{}
};
nt main()
{
Animal *p; //基类指针
Cat cat("Tom","cat");
Dog dog("Jerry","Dog");
Tiger tiger("DuDu","Tiger");
p=&cat; //绑定猫对象
p->show();
p=&dog; //绑定狗对象
p->show();
p=&tiger; //绑定老虎对象
p->show();
return 0;
}
纯虚函数抽象类
注意
纯虚函数在派生类中必须重写
虚函数与多态的应用
例题
class Employee
{
public:
Employee(const int,const string );
virtual ~Employee();
const string getName() const;
const int getNumber() const;
virtual double earnings() const=0;//雇员工资纯虚函数
virtual void print() const;
protected:
int number; // 编号
string name; // 姓名
};
class Manager : public Employee
{
public:
Manager(const int , const string, double =0.0);
~Manager() { }
void setMonthlySalary(double); //新增函数
virtual double earnings() const;
virtual void print() const;
private:
double monthlySalary ;
};
class HourlyWorker : public Employee
{
public:
HourlyWorker(const long, const string, double=0.0, int =0 );
~HourlyWorker(){}
void setWage(double);
void setHours(int);
virtual double earnings() const;
virtual void print() const;
private:
double wage;
double hours;
};
class PieceWorker : public Employee
{
public:
PieceWorker(const long , const string, double =0.0, int =0 );
~PieceWorker() { }
void setWage ( double ) ; //完成件数
void setQuantity ( int ) ; //价值
virtual double earnings() const;
virtual void print() const;
private:
double wagePerPiece;
int quantity;
};
学习总结
通过对图书馆管理系统的一步步完善,渐渐把知识运用到了实际应用中,逐步消化慢慢掌握。
作业中用到的知识就是比着抄代码,以后应该多写代码,熟能生巧。
C++结课了,曾经因为C很喜欢编程,后来因为什么渐渐少了热爱。
加下来面临的是课程设计,是一个升华的任务,要重拾热爱,尽所能去完成任务!