适合具备 C 语言基础的 C 教程(九)
扫描二维码
随时随地手机看文章
前言
在上一则教程中,叙述了关于C
类型转换的相关内容,在本节教程中,将叙述 C
的另一个内容,也就是抽象,这也是 C
相对于 C
语言来说独特的一点,下面我们就来着重叙述这一点。纯虚函数
在介绍抽象类之前,需要弄明白何为纯虚函数,下面假定我们有这样一个需求:做一个“各个国家的人的调查”,调查各个国家的人的:饮食、穿衣、开车要完成这样一个事情,那我们现在就需要实现这样几个类,一个是
Human
类,其他国家的人从 Human
类里派生而来,就比如说是Chinese
和Englishman
,我们再回过头来想,我们所要实现的需求是调查各个国家的人
,那么这个过程中,由Human
类派生得到 Chinese
和 Englishman
,那么在实例化对象的时候,我们实际上是不会用到Human
类去定义一个对象的,考虑到这层因素,我们在 Human
类里使用到了纯虚函数的概念,类实现的代码如下所示:class Human
{
private:
int a;
public:
/*纯虚函数*/
virtual void eating(void) = 0;
virtual void wearing(void) = 0;
virtual void driving(void) = 0;
};
class Englishman : public Human
{
public:
void eating(void) { cout<<"use knife to eat"<<endl; }
void wearing(void) {cout<<"wear english style"<<endl; }
void driving(void) {cout<<"drive english car"<<endl; }
};
class Chinese : public Human
{
public:
void eating(void) { cout<<"use chopsticks to eat"<<endl; }
void wearing(void) {cout<<"wear chinese style"<<endl; }
void driving(void) {cout<<"drive chinese car"<<endl; }
};
我们可以看到在上述的代码中,Human
类的成员函数跟前几讲所写的成员函数有所不同,而如上述 Human
类的成员函数这般写法,也就被称之为是纯虚函数。抽象类
上述引出了纯虚函数的写法,那纯虚函数和抽象类之间有什么关系呢?实际上,抽象类就是具有纯虚函数的类,那这抽象类存在的意义是什么呢?总的来说,其作用也就是:向下定义好框架,向上提供统一的接口,其不能够实例化对象,基于上述几个类的前提下,我们编写主函数的代码:int main(int argc,char **argv)
{
Human h;
return 0;
}
因为抽象类不能够实例化对象,所以上述代码编译结果是错误的,错误信息如下所示:而使用通过抽象类派生得到的派生类实例化对象是可行的,代码如下所示:int main(int argc, char** argv)
{
Englishman e;
Chinese g;
return 0;
}
另外需要注意的是:在派生抽象类的过程中,如果派生得到的子类没有覆写所有的纯虚函数,那么这个子类还是抽象类,比如有如下所示的代码,Human
类沿用的是上述的写法,代码不变,如果我们将上述的 Chinese
类进行更改,更改后的代码如下所示:class Chinese : public Human
{
public:
void eating(void) { cout<<"use chopsticks to eat"<<endl; }
void wearing(void) {cout<<"wear chinese style"<<endl; }
//void driving(void) {cout<<"drive chinese car"<
};
如上述代码所示,我们将 driving()
函数注释掉了,那么也就是说,我们并没有将抽象类的全部纯虚函数进行覆写,那么当前这个Chinese
类也是一个抽象类,也是不能够进行实例化对象的,要使得 Chinese
类有作用,我们必须派生出来另一个类,代码如下所示:class Guangximan : public Chinese
{
void driving(void) {cout<<"drive guangxi car"<<endl; }
};
这个时候,就可以用 Guangximan
这个类来实例化对象了。多文件编程
在前面的教程中,有一则教程说到了多文件编程,在C
中也就是将类的声明放到头文件中,将类的实现放在.cpp
文件中,为了更好地阐述这种方法,我们用实例来进行讲解,首先,来看一下,所涉及到地所有文件有哪些:可以看到上述有6
个文件,我们首先来看 Chinese.h
这个文件,代码如下所示:#ifndef _CHINESE_H
#define _CHINESE_H
#include
#include
#include
using namespace std;
class Chinese
{
public:
void eating(void);
void wearing(void);
void drivering(void);
};
#endif
通过上述地.h
文件可以看出,在这里的Chinese
类中,它只涉及到类成员函数的一个声明,并没有成员函数的实现,我们继续来看Chinese.cpp
的类实现:#include "Chinese.h"
void Chinese::eating(void)
{
cout << "use chopsticks to eat" << endl;
}
void Chinese::wearing(void)
{
cout << "wear chinese style" << endl;
}
void Chinese::drivering(void)
{
cout << "driver china car" << endl;
}
按照上述这样一种方法,我们继续来实现Englishman
类中的代码,首先是Englishman.h
中的代码,代码如下所示:#ifndef _ENGLISHMAN_H
#define _ENGLISHMAN_H
#include
#include
#include
using namespace std;
class Englishman
{
public:
void eating(void);
void wearing(void);
void driver(void);
};
#endif
继续看.cpp
中的代码,代码如下所示:#include "Englishman.h"
void Englishman::eating(void)
{
cout << "use chopsticks to eat" << endl;
}
void Englishman::wearing(void)
{
cout << "wear chinese style" << endl;
}
void Englishman::drivering(void)
{
cout << "driver china car" << endl;
}
至此,除了主函数以外的代码就编写完了,我们继续来看主函数的代码:#include "Englishman.h"
#include "Chinese.h"
int main(int argc, char **argv)
{
Englishman e;
Chinese c;
e.eating();
c.eating();
return 0;
}
在前面的教程中,我们就说过,如果是多文件的话,需要编写 Makefile
文件,Makefile
文件代码如下:Human: main.o Chinese.o Englishman.o Human.o
g -o $@ $^
%.o : %.cpp
g -c -o $@ $<
clean:
rm -f *.o Human
上述代码就不再这里赘述了,跟之前教程中的 Makefile
基本是一样的,有了Makefile
之后,编译代码只需要使用 make
命令就行了,编译结果如下所示:上述代码中,如果我们想要增添功能,比如说Chinese
和Englishman
都有名字,那么就可以增添设置名字和获取名字这两种方法,首先是 Chinese
的代码,代码如下:#ifndef _CHINESE_H
#define _CHINESE_H
#include
#include
#include
using namespace std;
class Chinese{
private:
char *name;
public:
void setName(char *name);
char *getName(void);
void eating(void);
void wearing(void);
void driving(void);
~Chinese();
};
#endif
然后是.cpp
中的代码:#include "Chinese.h"
void Chinese::setName(char *name)
{
this->name = name;
}
char *Chinese::getName(void)
{
return this->name;
}
/*其他成员函数实现同上,这里省略*/
写完了 Chinese
的代码,然后是Englishman
中的代码,首先是Englishman.h
中的代码:#ifndef _ENGLISHMAN_H
#define _ENGLISHMAN_H
#include
#include
#include
using namespace std;
class Englishman {
private:
char *name;
public:
void setName(char *name);
char *getName(void);
void eating(void);
void wearing(void);
void driving(void);
~Englishman();
};
#endif
紧接着,是.cpp
中的代码:#include "Englishman.h"
void Englishman::setName(char *name)
{
this->name = name;
}
char *Englishman::getName(void)
{
return this->name;
}
以这样的方式增添功能,确实是可行的,但是我们假设一下,如果类很多,除了中国人和英国人还有很多个国家的人,如果这些类都要增加相同的功能,这个工作量就比较大了,那要如何解决这个问题呢?这个时候,我们就可以引入一个新类Human
,然后,将每个类相同的部分写在这个类里面,其他类,诸如Englisnman
和Chinese
就可以从Human
类中继承而来,那这个时候,增添的操作,就只需要在 Human
类中增加就好了,不需要改动Chinese
和Englishman
,工作量就小了很多。我们来看 Human
类的代码实现,首先是.h
代码的实现:#ifndef _HUMAN_H
#define _HUMAN_H
#include
#include
#include
using namespace std;
class Human {
private:
char *name;
public:
void setName(char *name);
char *getName(void);
};
#endif
然后是.cpp
代码的实现:#include "Human.h"
void Human::setName(char *name)
{
this->name = name;
}
char *Human::getName(void)
{
return this->name;
}
有了 Human
类之后,我们就可以来实现我们所说的 Englishman
和Chinese
类了,代码如下所示:#ifndef _ENGLISHMAN_H
#define _ENGLISHMAN_H
#include
#include
#include
#include "Human.h"
using namespace std;
class Englishman : public Human
{
public:
void eating(void);
void wearing(void);
void driving(void);
~Englishman();
};
#endif
然后是Chinese
的代码:#ifndef _CHINESE_H
#define _CHINESE_H
#include
#include
#include
#include "Human.h"
using namespace std;
class Chinese : public Human
{
public:
void eating(void);
void wearing(void);
void driving(void);
~Chinese();
};
#endif
可以看到 Englishman
和Chinese
都是继承自Human
类,这个时候,就不需要再自己实现setName
和getName
了。我们继续来完善我们的代码,先从主函数说起,主函数代码如下所示:void test_eating(Human *h)
{
h->eating();
}
int main(int argc, char **argv)
{
Englishman e;
Chinese c;
Human * h[2] = {