C++派生类使用


上一个章节我们创建了一个派生类,在CTime类的基础之上,定义了一个CCurrentTime类,CCurrentTime类可以从CTime类基础一些数据成员和方法,同时我们在CCurrentTime类新增了一个成员函数init();除了新增成员函数之外,我们还可以增加新的成员变量,甚至我们还可以定义基类中已有的方法。这一节,我们就在上一章节的基础上继续完善CCurrentTime类。



派生类


1.可以继承基类的成员数据或成员函数;


2.可以增加新的成员变量;


3.可以增加新的成员函数;


4.可以重新定义已有的成员函数。


我们可以声明一个与父类成员函数相同的函数,函数名与参数都是相同的函数,如果我们在子类中重新实现了这个函数,当使用子类的对象调用这个函数的时候,父类和子类都有了一个这样的函数,就会调用子类的这个函数。在基类中我们想要使用父类的这个方法,可以使用父类名::方法名调用。


1.派生类对象与子类对象的子类中的成员可以跟基类的成员名称相同。如相同的数据成员或相同名称的成员函数。在定义子类对象时,引用跟基类相同的成员时,默认是引用的子类的成员。


2.派生类的对象也是一个基类对象。


通过定义一个派生类的对象,我们可以看到在这个对象里面包含了一个基类的对象。


apply1.png



类关系的介绍


1.is-a关系


派生类与基类之间有一层特殊的is-a(是一种)关系。


比如动物园的老虎和狮子都是动物的一种,我们可以说老虎是动物,狮子也是动物,但我们不能说动物是狮子或者是老虎。老虎是动物的一个子类,但是动物类也包括了其它的动物,比如大象,马等等….


我们可以说派生类是基类的一种,而不能说基类是派生类的一种。


因此可以将一个派生类对象赋值给基类对象。由于基类对象不包含派生类的成员,因此只能对基类部分进行赋值。不能将基类对象赋值给派生类对象。基类的指针或引用也可以直接引用子类的对象。但只能调用子类中跟基类相同的方法。反过来讲子类的指针指向基类的对象则不可以。因为子类具有的方法,基类不一定有。因此可以将一个基类的指针或引用指向派生类对象。


CCurrentTime currentTime;
CTime *p = ¤tTime; //基类的指针指向派生类,p指针只能调用基类的一些方法
CTime& time = currentTime; //基类的引用派生类


通过基类的指针或引用指向派生类对象后,调用的均是基类的成员,不能访问派生类的成员。即使派生类重新定义了基类的方法,仍然调用基类的方法。指针或引用的类型是谁的类型,就访问谁的方法。


2.has-a关系


汽车与发动机、变速箱的关系就是有一种has-a关系,是一种整体与部分的关系。可以通过包含来实现。即在类中将另一个类的对象作为本类的数据成员。



实例


下面添加一个实例:DateTime:显示当前年月日,时分秒,由于我们已经定义好了一个显示时分秒的类CTime,所以我们只需要定义年月日这些数据成员,时分秒的数据成员直接使用CTime这个类,我们只需要创建一个CTime类的对象作为DateTime类的数据成员就可以了。


1. DateTime.h


#ifndef DATE_TIME_H
#define DATE_TIME_H
#include"time.h"  //需要使用这个类,所以包含它的头文件
class CDateTime
{
public:
CDateTime(); //默认构造函数
CDateTime(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond);
~CDateTime();
 
void display();
 
private:
int m_nYear;
int m_nMonth;
int m_nDay;
 
CTime m_time;  //将Time类的对象作为这个类的数据成员
};
#endif


2.DateTime.cpp


#include"DateTime.h"
#include<iostream>
using namespace std;
 
CDateTime::CDateTime()
{
}
 
CDateTime::~CDateTime()
{
std::cout<<"CDateTime destructor!"<<std::endl;
}
 
CDateTime::CDateTime(int nYear, int nMonth, int nDay, int nHour, int nMinute, int nSecond)
:m_time(nHour, nMinute, nSecond) //成员初始化列表,调用数据成员的构造函数,初始化对象数据成员的时候应该在成员初始化列表里面赋值,如果在该函数体中赋值,首先这个数据成员创建的时候会调用它的类的默认构造函数被赋值,然后在函数体中再次被赋值。而我们采用了初始化列表之后,显式的调用了它的具有三个参数的构造函数,避免两次赋值。
{
m_nYear = nYear;
m_nMonth = nMonth;
m_nDay = nDay;
 
std::cout<<"CDateTime constructor!"<<std::endl;
}
 
void CDateTime::display()
{
cout<<m_nYear<<"-"<<m_nMonth<<"-"<<m_nDay<<" "<<m_time.getHour()<<":"<<m_time.getMinute()<<":"<<m_time.getSecond()<<endl;
}


3.main.cpp


#include<iostream>
#include"Time.h"  //注意要使用的头文件的包含
#include"DateTime.h"
using namespace std;
 
int main()
{
   CDateTime datetime(2015,2,26,19,28,50);
  datetime.diaplay();
return 0;
}


4.运行结果如下:


apply2.png


5.包含关系


我们来看一下它们的构造函数和析构函数的顺序如何:可以看到,作为数据成员的类的构造函数先被调用,析构顺序和构造函数顺序相反。


apply3.png


注意:继承虽然可以是实现代码复用,子类通过继承就可以获得基类的功能,但不要滥用继承。只有明确满足is-a关系的才能使用继承。满足has-a关系的要将类对象作为其他类的数据成员的方式。



【本文由麦子学院独家原创,转载请注明出处并保留原文链接】

logo
© 2012-2016 www.maiziedu.com
蜀ICP备13014270号-4 Version 5.0.0 release20160127

有一位课程导师想与你聊聊

客服热线 400-862-8862

回到顶部