我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下在Visual C++中进行类设计的通行做法,这一篇帖子先来看看如何把架构搭建起来。程序员新手会去看很多书,但是书中往往教语法多,讲程序员习惯的少。那我们就来谈谈这个话题。
先来看一个重点,下面这一段话一定要记住。
在Visual C++中设计一个类的时候,程序员通常喜欢这样做:
(1)将类的定义及类成员函数的声明放到头文件(*.h文件)中。
(2)将类中成员函数的实现放到源文件(*.cpp文件)中。
我还是延用我在之前的帖子《Visual C++中的虚函数和纯虚函数的原理》中所讲的例子来继续今天的话题。所以大家要先看一下我的这个帖子:
假如主文件是study.cpp,对于Father类另外需要father.h和father.cpp 两个文件。那么对于Son类需要son.h和son.cpp两个文件。对于main()函数,按照程序员的通行做法,是要把他单独放到study.cpp文件中。
下面开始构建这个项目,首先在某一个解决方案中添加一个新项目study,依然选择“Windows控制台应用程序”,这将会自动生成一个study.cpp。接下来我们就将Father类和Son类的定义各自拆分成两个文件:头文件和源文件。在多项目的解决方案中,要将该项目设置为启动项目。
程序员在往一个现有项目中添加头文件(.h文件)或源文件(.cpp文件)有两种方式:
第一种方式
是在“解决方案资源管理器”中,找到要添加文件的项目,在该项目下,在“头文件”节点上单击鼠标右键,在弹出菜单中选择【添加】→【新建项】。在接下来的“添加新项”对话框中,选中“头文件(.h)”,输入文件名:father.h。单击“添加”按钮,完成father.h头文件的添加。
接下来,在“解决方案资源管理器”窗口中,在“源文件”节点上单击鼠标右键,在弹出菜单中选择【添加】→【新建项】,在出现的“添加新项”对话框中,选中“C++文件(.cpp)”,输入文件名:father.cpp,然后单击“添加”按钮完成添加。过程和添加头文件类似。
然后再用相同的做法把Son类的头文件和源文件设置一下。
第二种方式
是直接添加类,Visual Studio 2022开发环境会自动给你提供头文件和源文件。在“解决方案资源管理器”窗口中,直接在项目上单击鼠标右键,在弹出菜单中选择【添加】→【类】,出现添加类对话框。
在类名框中输入Father,在.h文件框中输入Father.h,在.cpp文件Father.cpp。
如果是Son类,还要在基类框中输入Father。访问的话就填写public。
输入类名和基类的名字后,单击“确定”按钮,完成添加。
提示:Visual Studio 2022生成的头文件中包含了#pragma once指令代码,我们先将其注释起来,后面会解释该句代码的作用。
各文件代码
father.h代码如下所示。
//在头文件中包含类的定义及类成员函数的声明
class Father
{
public:
Father();
~Father();
void eat();
void run();
virtual void study();
};
father.cpp代码如下所示。
//在源文件中包含类中成员函数的实现
#include "pch.h"
//因为在编译 father.cpp时,编译器不知道 father到底是什么,所以要包含father.h,这样,
//编译器就知道 father是一种类的类型
#include "father.h"
#include <iostream>
using namespace std;
//::叫作作用域标识符,用于指明一个函数属于哪个类或一个数据成员属于哪个类。::前面如果不跟
//类名,则表示是全局函数(即非成员函数)或全局数据
Father:: Father()
{
}
Father:: ~Father()
{
}
//注意:虽然我们在函数体中什么也没写,但仍然是实现了这个函数
void Father:: eat()
{
}
void Father:: run()
{
}
//注意,在头文件(.h文件)中加了virtual后,在源文件(.cpp文件)中就不必再加virtual了
void Father:: study()
{
cout<<"study"<<endl;
}
这里要说明一下,在包含头文件时,<>和" "有什么区别?<>和" "表示编译器在搜索头文件时的顺序不同,<>表示从系统目录下开始搜索,然后再搜索PATH环境变量所列出的目录,不搜索当前目录;" "表示先从当前目录搜索,然后是系统目录和PATH环境变量所列出的目录。所以如果我们知道头文件在系统目录下,就可以直接用<>,这样可以加快搜索速度。
son.h代码如下所示。
//因Son类从Father类继承而来,要让编译器知道Father是一种类的类型,就要包含Father.h头文件
#include "father.h"
class Son: public Father
{
public:
void study ();
};
son.cpp代码如下所示。
#include "pch.h"
#include "son.h"
#include <iostream>
using namespace std;
void Son:: study ()
{
cout<<"new study"<<endl;
}
study.cpp代码如下所示。
#include "pch.h"
#include "father.h"
#include "son.h"
void fn(Father *p)
{
p->study ();
}
int main()
{
Father *p;
Son st;
p=&st;
fn(p);
return 0;
}
这样,架构就搭好了,那么这样一个框架能搭出来,说明你具有程序员的一个基本素质,就是构建,那么是不是这样就好了呢,就能正常运行了呢?很遗憾,程序很难一次性通过,你们说气不气人,要怎么进行调整和排错呢,请看我的下一篇帖子。
作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。