再续构造函数(派生)

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值