1.区别派生类的构造函数
对于派生,派生类继承了基类的全部数据成员和全部函数成员(构造函数和析构函数除外),并且添加了自己的新的成员。
不同的继承方式派生类成员对基类的成员访问权限课自行理解。
观察下面两个不同的栗子
栗子1
//-----------------------------------test.h
#ifndef _TEST1_H_
#define _TEST1_H_
class Point
{
public:
void InitPoint(float xx=0,float yy=0) {X=xx;Y=yy;}
void Move(float xm,float ym) {X+=xm;Y+=ym;}
float GetX() {return X;}
float GetY() {return Y;}
private:
float X;
float Y;
};
class Rectangle:public Point
{
public:
void InitRectangle(float x,float y,float h,float w)
{InitPoint(x,y);H=h;W=w;}
float GetH() {return H;}
float GetW() {return W;}
private:
float H;
float W;
};
#endif
//-----------------------------------mine.cpp
#include "test1.h"
#include "iostream"
using namespace std;
int main()
{
Rectangle rect;
rect.InitRectangle(2,3,20,10);
rect.Move(3,2);
cout<<"The data of rect(X,Y,H,W): "<<endl;
cout<<rect.GetX()<<"..."<<rect.GetY()<<"..."<<rect.GetH()<<"..."<<rect.GetW()<<endl;
system("PAUSE");
return 0;
}
结果
//栗子2,其他同上,稍作改动
//-----------------------------------test.h
void InitPoint(float xx=0,float yy=0) {X=xx;Y=yy;}
//改成
Point(float xx=0,float yy=0) {X=xx;Y=yy;}
void InitRectangle(float x,float y,float h,float w)
{InitPoint(x,y);H=h;W=w;}
//改成
Rectangle(float x,float y,float h,float w)
{Point(x,y);H=h;W=w;}
//-----------------------------------mine.cpp
Rectangle rect;
rect.InitRectangle(2,3,20,10);
//改成
Rectangle rect(2,3,20,10);
得到的结果却是
对于栗子1,毋庸置疑.主函数首先声明了一个派生类的对象rect,对象生成时调用了默认构造函数,然后通过派生类对象,访问了派生类的公有函数InitR( )、Move( )等,而InitR( )也调用了派生类公有成员函数InitP( )(从基类继承而来的)。
对于栗子2,定义了基类和派生类的构造函数,定义派生类的构造函数时除了对派生类的数据成员进行初始化外,还必须调用基类的构造函数初始化基类的数据成员。这是派生类构造函数的调用顺序。
实际上,这样做对于X,Y没有任何影响,只是初始化为0.
如在栗子2cpp文件中去掉
rect.Move(3,2);
可以发现
但
//将
Rectangle(float x,float y,float h,float w)
{Point(x,y);H=h;W=w;}
//改成初始化表格式时
Rectangle(float x,float y,float h,float w) :Point(x,y),H(h),W(w) {}
可发现如我们所想
2.设置虚基类的意义。
在多继承方式下,由于不同路径继承来的该类成员在内存中拥有两个副本,这产生了继承的二义性。而将直接基类的共同基类设置为虚基类,可以解决同名成员的二义性这个问题。
格式为:
class <派生类名> : virtual <继承方式> <共同基类名>;
//比如class C继承于B1和B2,而B1和B2又继承于A。在中间类B1和B2的声明部分有
class B1:virtual public A;
class B2:virtual public A;
对于虚基类和非虚基类的构造函数的调用顺序(跟一般多继承的初始化顺序相似)
1)先调用虚基类的构造函数,再调用非虚基类的构造函数。
2)若同一层次中包含多个虚基类,其调用顺序为定义时的顺序。
3)若虚基类由非虚基类派生而来,则仍先按先调用基类构造函数,再调用派生类构造函数的顺序。
现就书本上的例题发工资问题说明。
/****************************************************************************************
MISSION:
公司有四类人员:经理、兼职技术员、销售员、销售经理。
要求存储这些人员的编号、姓名、月工资。计算月工资并显示全部信息。
月工资计算办法:
经理:固定月工资8000元
兼职技术员:每小时100元
销售员:销售额提成的 6%
销售经理:固定月工资5000元+销售提成(销售额的 5‰)
******************************************************************************************/
//-----------------------------------test.h------------------------------------
#ifndef _TEST1_H_
#define _TEST1_H_
//--------------------------------基类
class Employee //基类为抽象类,将Pay()和Display()定义wie纯虚函数,所以不能实例化对象
{
public:
Employee();
virtual void Pay() = 0; //纯虚函数,在基类中只给出声明,在派生类中给出不同的定义。该函数在派生类中都是虚函数
virtual void Display() = 0;
protected:
char name[10]; //个人信息
static int num;
float salary;
};
//---------------------------技术兼职员
class Technician:public Employee
{
public:
Technician(float h); //h文件声明(如上)
void Pay();
void Display();
private:
const float hourlyrate; //常成员不可改变,只能通过初始化表定义,h文件声明(如上),cpp文件给出实现,在主cpp文件中初始化
float workhours;
};
//-------------------------------主管
class Manager:virtual public Employee //虚基类,在中间
{
public:
Manager(float m);
void Pay();
void Display();
private:
const float monthpay;
};
//-------------------------------销售员
class Salesman:virtual public Employee
{
public:
Salesman(float c);
void Pay();
void Display();
private:
const float commrate;
float sales;
};
//-----------------------------销售经理
class SalesManager:public Salesman,public Manager
{
public:
SalesManager(float m,float c);
void Pay();
void Display();
private:
const float monthpay;
const float commrate;
float sales;
};
#endif
//-----------------------------------test.cpp----------------------------------
#include "test1.h"
#include "iostream"
using namespace std;
int Employee::num=0;
Employee::Employee()
{
cout<<"请输入一个员工的姓名:";
cin>>name;
salary=0;
num++;
}
//---------------------------技术兼职员
Technician::Technician(float h):hourlyrate(h) {} //cpp文件给出实现, h取100
void Technician::Pay()
{
cout<<name<<"本月工作时数:";
cin>>workhours;
salary=hourlyrate*workhours;
}
void Technician::Display()
{
cout<<"兼职技术员"<<name<<"(编号为"<<num<<")"<<"本月工资:"<<salary<<endl;
}
//-------------------------------主管
Manager::Manager(float m):Employee(),monthpay(m) {} // m取8000
void Manager::Pay()
{
salary=monthpay;
}
void Manager::Display()
{
cout<<"经理"<<name<<"(编号为"<<num<<")"<<"本月工资:"<<salary<<endl;
}
//-------------------------------销售员
Salesman::Salesman(float c):Employee(),commrate(c) {} // c取0.06
//注意这里声明、实现、定义的格式。一定要记住。
//这个实际上是派生类构造函数的实现方法,即先调用基类,初始化基类成员,在初始化自身数据成员
//不然的话系统很容易报错
void Salesman::Pay()
{
cout<<name<<"本月销售额:";
cin>>sales;
salary=sales*commrate;
}
void Salesman::Display()
{
cout<<"销售员"<<name<<"(编号为"<<num<<")"<<"本月工资:"<<salary<<endl;
cout<<endl;
}
//-----------------------------销售经理
SalesManager::SalesManager(float m,float c):Manager(m),Salesman(c),monthpay(m),commrate(c) {} // m取5000, c取0.005
//同上,先调用两个基类的构造函数(这两个都是用户给出的,相当于构造函数的重载),初始化他们的成员,再初始化自身数据成员
//因为假如没有Manager(m),Salesman(c)这句话,系统调用的是默认的构造函数,这时候就会报错,说Manager类和Salesman类没有默认的构造函数
void SalesManager::Pay()
{
cout<<name<<"所管部门月销售额:";
cin>>sales;
salary=monthpay+sales*commrate;
}
void SalesManager::Display()
{
cout<<"销售经理"<<name<<"(编号为"<<num<<")"<<"本月工资:"<<salary<<endl;
cout<<endl;
}
//-----------------------------------mine.cpp----------------------------------
#include "test1.h"
#include "iostream"
using namespace std;
int main()
{
float p=8000; //主管参数
float r=100; //兼职技术员参数
float x=0.06f; //销售员参数
float q=5000,y=0.005f; //销售经理参数
Manager m1(p); //在主cpp文件中初始化
Technician t1(r);
Salesman s1(x);
SalesManager sm1(q,y);
Employee *emp[4]={&m1,&t1,&s1,&sm1}; //Employee虽然不能创建对象,但我们可以创建一个基类指针,指向派生类对象
for(int i=0;i<4;i++) //实现多态
{
emp[i]->Pay();
emp[i]->Display();
}
system("PAUSE");
return 0;
}