目录
知识点总结+应用
继承
继承机制使用已经定义的类作为基础建立新的类定义,新的类是原有类的数据集及操作与心累所增加的数据及操作的组合。新的类把原有类作为积累引用,而不需要修改原有类的定义。新定义的类作为派生类引用,这样可以扩充、可重用技术大大降低了大型软件的开发难度。
类之间的关系
一个大的应用程序,通常有多个类构成,类与类之间互相协同工作。
- 包含关系
- 一个类部分地使用另一个类
- 分类方式(在已有类的基础上创建新的类)——继承
基类和派生类
派生类不可访问基类地私有成员
继承地时候,可继承一个类(单继承),也可继承多个类(多继承).
继承一般是,派生类对基类内容的扩充或者修改——一版在一个大的程序中,用于版本的更新等等。
派生类的生成过程经历了三个步骤:
●吸收基类成员(全部吸收(构造、析构除外),但不一定可见)
在C++的继承机制中,派生类吸收基类中除构造函数和析构函数之外的全部成员。
●改造基类成员
通过在派生类中定义同名成员(包括成员函数和数据成员)来屏蔽(隐藏)在派生类中不起作用的部分基类成员。
●添加派生类新成员
仅仅继承基类的成员是不够的,需要在派生类中添加新成员,以保证派生类自身特殊属性和行为的实现。
在派生类中,基类成员的调用:用类名直接调用
派生类定义了与基类同名的成员,在派生类中访问同名成员时屏蔽(hide)了基类的同名成员
基类中的静态成员,静态成员被整个体系共享
类成员的访问特性和类的继承性质决定了类成员的作用域和可见性。
基类地初始化——初始化列表
基类的私有数据成员在派生类中不可见,但这些数据存储单元依旧被创建,创建派生类对象时,派生类的构造函数总是先调用基类构造函数来建立和初始化派生类中的基类调用基类带参的构造函数,可以通过参数初始化列表实现数据成员的初始化。调用析构函数的次序和调用构造函数的持续相反
构造函数与析构函数
派生类构造函数和析构函数的使用原则
1. 基类的构造函数和析构函数 不 能被继承2. 如果基类 没有定义构造函数 或 有无参的构造函数 , 派生类也 可以不用 定义构造函数3. 如果基类 无 无参的构造函数 ,派生类 必须 定义构造函数4. 如果派生类的基类也是派生类,则每个派生类 只负责直接 基类的构造5. 派生类是否定义析构函数与所属的基类无关
继承代码实例
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; // 数据成员,高度
};
// Point 类的成员函数
// 构造函数,调用成员函数对 x,y作初始化
Point::Point ( int a, int b )
{ setPoint ( a , b ) ; }
// 对数据成员置值
void Point :: setPoint ( int a, int b ) { x = a ; y = b ; }
// 重载插入算符,输出对象数据
ostream &operator<< ( ostream &output , const Point &p )
{ output << '[' << p.x << "," << p.y << "]" ;
return output ;
}
// Circle 类的成员函数
// 带初始化式构造函数,首先调用基类构造函数
Circle::Circle( double r, int a, int b ): Point( a, b ) { setRadius ( r ); }
// 对半径置值
void Circle::setRadius ( double r ) { radius = ( r >= 0 ? r : 0 ); }
// 返回半径值
double Circle::getRadius() const { return radius; }
// 计算并返回面积值
double Circle::area() const { return 3.14159 * radius * radius ; }
// 输出圆心坐标和半径值
ostream & operator<< ( ostream &output, const Circle &c)
{ output << "Center = " << '[' << c.x << "," << c.y << "]" << "; Radius = "
<< setiosflags(ios::fixed|ios::showpoint) << setprecision(2) << c.radius ;
return output ;
}
Cylinder::Cylinder(double h, double r, int x, int y):Circle(r,x,y) { setHeight(h); }
void Cylinder::setHeight(double h) { height = ( h >= 0 ? h : 0 ); }
double Cylinder::getHeight() const { return height; }
double Cylinder::area()const{ return 2*Circle::area()+2*3.14159*radius*height; }
double Cylinder::volume() const { return Circle::area()*height; }
ostream &operator<< ( ostream &output, const Cylinder &cy )
{ output << "Center = " << '[' << cy.x << "," << cy.y << "]" << "; Radius = "
<< setiosflags(ios::fixed|ios::showpoint) << setprecision(2) << cy.radius
<< "; Height = " << cy.height << endl ;
return output;
}
#include<iostream>
using namespace std ;
#include <iomanip.h>
int main(){
Point p ( 72, 115 ) ; //定义点对象并初始化
cout << "The initial location of p is " << p << endl ;
p.setPoint ( 10, 10 ) ; //置点的新数据值
cout << "\nThe new location of p is " << p << endl ; //输出数据
Circle c ( 2.5, 37, 43 ) ; //定义圆对象并初始化
cout<<"\nThe initial location and radius of c are\n"<<c<<"\nArea = "<<c.area()<<"\n" ;
c.setRadius ( 4.25 ) ; c.setPoint ( 2, 2 ) ;
cout<<"\nThe new location and radius of c are\n"<<c<<"\nArea = "<<c.area()<< "\n" ;
Cylinder cyl ( 5.7, 2.5, 12, 23 ) ; //定义圆柱体对象并初始化
cout << "\nThe initial location, radius ang height of cyl are\n" << cyl<< "Area = " << cyl.area() << "\nVolume = " << cyl.volume() << '\n';
cyl.setHeight ( 10 ) ; cyl.setRadius ( 4.25 ) ; cyl.setPoint ( 2, 2 ) ;
cout << "\nThe new location, radius ang height of cyl are\n" << cyl << "Area = " << cyl.area() << "\nVolume = "<<cyl.volume()<< "\n" ;
}
虚函数
在面向对象程序设计中,多态性是指一个名字多种语义或者界面相同多种实现,重载函数是多态性的一种简单形式,C++为体系提供了一种灵活的多态机制,也就是虚函数,虚函数允许函数调用与函数体的联系,在运行时才进行称为动态连编类继承多态提供了对软件重用性,扩充性重要的表达能力。
虚函数地关键字为virtual的成员函数。派生类可以重载积累的虚函数,只要接口相同,函数的虚特性不变
复制兼容规则:赋值兼容规则指在程序中需要使用基类对象的任何地方,都可以用公有派生类的对象来替代。
赋值兼容规则中所指的替代包括以下的情况:
a 派生类的对象可以赋给基类对象
b 派生类的对象可以初始化基类的引用
c 派生类的对象的地址可以赋给基类类型的指针
多态的实现
多态性的实现和联编这一概念有关。所谓联编(Binding,绑定)就是把函数名与函数体的程序代码连接(联系)在一起的过程。
联编分成两大类:静态联编和动态联编。
静态联编优点:调用速度快,效率高,但缺乏灵活性;动态联编优点:运行效率低,但增强了程序灵活性。
C++为了兼容C语言仍然是编译型的,采用静态联编。为了实现多态性,利用虚函数机制,可部分地采用动态联编。
多态从实现的角度来讲可以划分为两类:编译时的多态和运行时的多态。
编译时的多态是通过静态联编来实现的。静态联编就是在编译阶段完成的联编。编译时多态性主要是通过函数重载和运算符重载实现的。
运行时的多态是用动态联编实现的。动态联编是运行阶段完成的联编。运行时多态性主要是通过虚函数来实现的。 l
类指针的关系
基类指针和派生类指针与基类对象和派生类对象4种可能匹配:
派生类指针只有经过强制类型转换之后,才能引用基类对象
根据赋值兼容,用 基类类型的指针指向派生类 ,就可以 通过 这个 指针 来使用 类 ( 基类或派生类 )的成员函数。如果这个函数是 普通 的成员函数,通过基类类型的指针访问到的只能是 基类的同名成员 。而如果将它设置为 虚函数 ,则可以使用基类类型的指针访问到指针正在指向的 派生类的同名函数 。从而实现 运行过程 的 多态 。
实现动态联编的前提
●先要声明虚函数
●类之间满足赋值兼容规则
●通过指针与引用来调用虚函数。
抽象类
纯虚函数:是在说明时的代码,初始化值为零的虚函数,纯虚函数本身没有实现,有它的派生类定义所需的实现版本
具有纯虚函数的类称为抽象类,抽象类只能作为基类,不能建立实例化对象,如果抽象类的派生类不提供纯虚函数的实现,则依旧是抽象类,定义了纯虚函数实现的派生类称为具体类,可以用于建立对象
尽管不能建立抽象类的对象,但抽象类机制提供软件抽象和可扩展性的手段,抽象类指针使得派生的具体类对象具有多态操作能力
纯虚函数是一种特殊的虚函数,在许多情况下,在基类中不能对虚函数给出有意义的实现,而把它声明为纯虚函数,它的实现留给该基类的派生类去做。这就是纯虚函数的作用。
多态的应用实例
虚函数和多态性能够使成员函数根据调用对象爱国的类型产生不同的动作,这给程序设计赋予很大的灵活性。多态性特备是用于实现分层结构的软件系统,比那与对问题抽象的定义共性,实现时定义区别。
//Employee.h
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;
};
void test1()
{
Manager m1 ( 10135, "Cheng ShaoHua", 1200 ) ;
Manager m2 ( 10201, "Yan HaiFeng");
m2.setMonthlySalary ( 5300 ) ;
HourlyWorker hw1 ( 30712, "Zhao XiaoMing", 5, 8*20 ) ;
HourlyWorker hw2 ( 30649, "Gao DongSheng" ) ;
hw2.setWage ( 4.5 ) ;
hw2.setHours ( 10*30 ) ;
PieceWorker pw1 ( 20382, "Xiu LiWei", 0.5, 2850 ) ;
PieceWorker pw2 ( 20496, "Huang DongLin" ) ;
pw2.setWage ( 0.75 ) ;
pw2.setQuantity ( 1850 ) ;
// 使用抽象类指针,调用派生类版本的函数
Employee *basePtr;
basePtr=&m1; basePtr->print();
basePtr=&m2; basePtr->print();
basePtr=&hw1; basePtr->print();
basePtr=&hw2; basePtr->print();
basePtr=&pw1; basePtr->print();
basePtr=&pw2; basePtr->print();
}
int main()
{
test1();
}
课程感想
对于这两章的课程,还是挺有感触的,刚开始学的时候,只是看到C++的边边角角,我怎么也想象不出别人写软件是如何写的,学了之后,我只能想到,一个简单的物体,如何去描述它的属性,学了继承和多态之后,我对面向对象也是有了深一点的了解,
知道如何去封装一个小程序,有了这一些了解,我也可以去更深一步的学习,去扩展更多知识。