类5-类的继承、虚函数、纯虚函数、虚析构函数
一、类的继承
就像家谱一样,就是一个继承图。爷爷-父亲-儿子-孙子等.类也一样,上面的类称为基类,也称父类。基类下面的类叫子类也叫派生类。
子类对父类的一些属性等有所继承也有所发展,因此才有了类的继承。
C++中类的继承格式为:
class ChildClass:public ParentClass;
子类后面要加上冒号及类限制符(public\protected\private)和基类。
class CExample
{
public:
...
protected:
...
private:
...
}
在一个类中,会有三个访问限制符。
public 是公共,公开的意思,只要有对象访问CExample类,则它的public部分是可以被此对象访问的。
如果是子类来继承的话,子类可以访问基本中这部分的信息。
protected 是保护的意思,即只有本类和子类可以访问这部分内容,其它的对象是不可以访问的。
private 是私有的,即只有本类可以访问这部分内容,其它的对象没有访问权限。
如果我们在继承基类的时候,如直接这样写:
class CSample:CParent;
此时省略了限制访问类符号,系统默认是private级的,这时就会把基类中protected和public的成员全变成子类的private区内,基类的private 是不能访问的。
如果为protected,则只能访问父类中的protected及public,而public成员也在子类中变成protected属性.
如果为public,则父类中的protected也变成public。
记住以下三点:
如果基类被声明为private,其成员在派生类中永远不可访问。
如果基类被声明为protected, 其public成员在派生类中将成为protected.
如果其类被声明为public,其在派生类中的访问级别保持不变。
二、类的虚函数
什么叫虚函数?就是此函数已在父类中定义,但其在子类中可能要重新再定义。
一般是在函数头前加上 virtual关键字。如 virtual type showinfo() const;
三、类的纯虚函数
在基类中我们声明了一个函数成员,但没有给出具体实现,因为这个类只是一种很泛的抽象,具体实现函数的功能要在派生类中实现。它的格式为:
virtual type showinfo() const=0;
即在虚函数后面加上=0即可。
这样的类也叫抽象类,抽象类不能实例化。
四、类的虚析构函数
类的虚折构函数在动态产生对象后并用delete删除时有用,如果不是动态产生对象就不用做这样的声明。
平常我们是静态编译的,在析构时,程序也是静态连接的,这时系统知道父类和子类之间的关系,就会先析构子类再析构父类。而在动态产生一个对象时,如下面的例子:
Cparent* p=0;
p=new CChild(100);
delete p;
在此类中,Cparent类为CChild的父类,我们此时声明Cparent指针p.因为是动态在自由存储区中分配的,而p又被声明为Cparent类型的指针,但在赋值时却指向子类。但在销毁时,系统认为它是cparent类型的,故会调用Cparent的析构函数,而子类的析构函数不会被执行。这就是我们用虚析构函数的原因。
因此:我们在类的继承时,总是把类的析构函数声明为虚析构函数是个好主意。
但构造函数是没有虚的,切记!
以下为代码:
Box.h
#pragma once
class CBox
{
public:
CBox(double lv,double wv, double hv);
CBox();
virtual double volume() const;
double getlength() const;
protected:
virtual void printclassInfo()=0;
double m_length;
double m_width;
double m_height;
};
Box.cpp
#include "Box.h"
#include <iostream>
using namespace std;
CBox::CBox(double lv,double wv,double hv):m_length(lv),m_width(wv),m_height(hv)
{
printf("Constructor Called!\n");
}
CBox::CBox()
{
printf("Default construct!\n");
}
double CBox::volume() const
{
return m_height*m_width*m_length;
}
double CBox::getlength() const
{
return m_length;
}
ChildBox.h
#pragma once
#include "Box.h"
class CChildBox:public CBox
{
public:
CChildBox(double lv,double wv,double hv);
double volume() const;
virtual void printclassInfo();
};
ChildBox.cpp
#include "ChildBox.h"
#include <iostream>
using namespace std;
CChildBox::CChildBox(double lv,double wv,double hv):CBox()
{
m_length=lv;
m_width=wv;
m_height=hv;
printf("CChildBox Construct Called!\n");
}
double CChildBox::volume() const
{
return 3*3*3;
}
void CChildBox::printclassInfo()
{
printf("CChildBox\n");
}
main.cpp
#include "ChildBox.h"
#include <iostream>
using namespace std;
void output(CBox& abox)
{
cout<<endl
<<abox.volume()<<endl;
}
void main()
{
CChildBox childbox(12,22,33);
//CBox box(3,0.3,3.5);
CBox* p=0;
p=&childbox;
printf("The volume is: %0.2f \n",p->volume());
printf("the getlength is: %0.2f\n",p->getlength());
output(childbox);
childbox.printclassInfo();
/*p=&box;
printf("The volume is: %0.2f \n",p->volume());
output(box);*/
}
本例中,我们把代码的头和定义分开了。
另因为CBox是抽象类,所以不能实例化它,故把main中的部分代码注释掉了,如果要把CBox类的中纯虚函数变成虚函数并实现它,则可以把注释的部分打开就可以编译。