1.5编写一个简单的项目程序
这个项目将向读者演示在本章学到的C++语言和项目管理的特性。该项目将使用若干源代码文件,以便读者可以了解到依赖项的作用,以及构建工具是如何管理源代码变更的。这个项目非常简单,它将要求你输入名字,然后在命令行上打印输出名字以及相关的时间和日期。
1.5.1项目结构
该项目包含3个函数: main函数,它会调用其他另外两个函数print-name和print_time 。它们分别位于3个独立的源代码文件中,因为 main函数将调用位于其他源代码文件中的两个函数,这意味着main 函数源代码文件中必须包含这些函数的原型
1.5.2创建预编译头文件
该代码将使用C++标准库的流对象进行信息的输入和输出,因此我们将使用<iostream)头文件。代码中将使用C++的字符串类型处理信息输入,因此还将使用<string> 头文件。最后,它会访问C运行时库的time 和date 函数,因此代码中还将用到<ctime>头文件。这些都是标准的头文件,我们在进行程序开发时无需对它们进行修改,因此它们是预编译的理想目标。
在Visual Studio中创建一个C++头文件,然后在其中添加如下代码:
include <string>#include <ctime>将它另存为utils.n
新建一个C++源代码文件,添加一行代码来引用刚才创建的头文件:
34;utils.h"
将其另存为utils.cpp ,我们将需要为这个项目创建一个 makefile ,因此在新建文件对话框中,选择文本文件作为文件类型。添加以下规则来构建预编译头文件:
utils pch utils.obj:: utils cpp utils. hCl /EHsc /c utils.cpp/ Ycutils h
将它另存为文件名为&34;的文件,注意要在文件末尾加一个句点( . )符号。因为我们是以文本文件的形式创建该文件的,Visual Studio将自动为它添加一个txt文件后缀,但是因为我们不希望使用该文件后缀,所以需要添加一个句点以表明该文件无后缀名。其中第一行表示utils.pch和utils.obj依赖于特定的源代码文件和头文件。第二行(以一个制表符作为前缀)告知编译器去编译C++文件,但是不调用链接器,同时还告知编译器将预编译代码保存到utils.n文件中。该命令将创建utils.pch和
utils.obj文件,即
当make实用程序发现有两个目标时,如何编写一个简单的程序,默认的动作(当目标和依赖项之间使用单个冒号时)是为每个目标都调用一次命令(你可以使用宏来决定哪个目标需要被构建)。这意味着相同的编译器命令将执行两次。这是我们不希望看到的,因为两个目标是通过调用一次命令构建的。双冒号:是一个变通方案,它告知nmake不要采用为每个目标调用命令的行为。最终的结果就是,当make程序调用命令一次,并生成了utils.pch文件,然后它会尝试生成utils.0bj文件,不过它发现该文件已经被生成了,所以不需要再次调用命令。
现在测试输出结果。在命令行中,导航到项目文件夹下,输入nmake命令。
编写程序简单步骤如下:1、桌面空白处右击2,新建3,文本文档,4,新建文本文档的图标。2、修改txt文件的扩展名,右键-重命名。将.txt改为.vbs,修改成.vbs后看到图标变了,这就说明你的系统能够识别他成一个vbs脚本程序。
如果没有提供makefile的名称,程序维护工具将自动使用名为makefile的文件(如果你希望使用一个其他名字的makefile ,可以使用/f开关并指定文件名) :
C:\Beginning C++\Chapter_01\Code>nmakeMicrosoft (R) Program Maintenance Utility Version 14.00.24210.0Copyright (C) Microsoft Corporation. All rights reserredcl/EHsc /c utils.cpp /Ycutils.hMicrosoft (R) C/C++ Optimizing Compiler Version 19,00.24210 for x86Copyright (C) Microsoft Corporation. All rights reservedutils.cpp
查看文件目录列表,以确认utils.pch和utils.obj文件是否已经被创建。
1.5.3创建主文件
现在创建一个C++源代码文件并添加如下代码:
34;utils,h&include &34;34;time.h"void main(){print_name();print_time();}
将此文件另存为main.cpp。
其中引用第一个文件是标准库头文件的预编译头文件。其他两个文件提供了被main 函数调用的另外两个函数的函数原型
现在用户需要为main文件添加一条规则到makefile文件。将下列加粗显示的代码行添加到文件顶
部
main.obj : main.cpp name.h time.h utils.pchcl/EHsc /c main.cpp /Yuutils.hutils.pch utils.obj: utils.cpp utils.hc1 /EHsc /c utils.cpp /Ycutils.h
新增的一行表示目标文件main.obi 依赖于两个头文件:一个源代码文件main.cpp和一个预编译头文utils.pch 。目前 main.cpp文件将无法编译,因为相关的头文件还不存在。所以我们可以测试
makerile文件,创建两个C++头文件。在第一个头文件中,添加函数原型
void print_name();
将该文件另存为name.n 。在第二个头文件中,添加函数原型
void print_time();
将该文件另存为time.h。
现在我们可以运行make 程序,它将只编译main.cpp文件。测试它的输出结果:通过在命令行中输入del main.obj utils.obj utis.pch命令来删除所有目标文件,然后再次运行make程序。这次,用户将发现make 程序首先编译了utils.cpp文件,然后编译了main.cpp文件。这样的编译顺序是因为首个目标是main.obj文件,但是因为它依赖于utils.pch文件,所以 make程序在返回创建main.obj文件规则之前,移动到了下一个规则并采用此规则生成了预编译头文件
需要注意的是,我们还没有定义print_name 和print_time函数,不过编译器并不会对此有异议。因此编译器只创建对象文件,链接器负责链接函数。头文件中的函数原型
[驱动器]:\\?\\程序\ \英语1 \ \资源\ \ [驱动器]:\\?\\程序\ \英语1 \ \发布\ \ 另外,最好建立一个专门的文件夹来存放各种模块,这样代码就可以重用了。这样我们每次写程序都不用重写所有模块,编程速度会。
1.5.4输入和输出流
目前为止,我们已经了解了如何通过cout 对象将数据输出到控制台。标准库还提供了一个cin流对象,允许读取命令行中输入的值。
创建一个C++源代码文件,并添加如下代码:
34;utils.h&include &34; void print_name(){std::cout << &34;;std:;string name;std::cin >> name;std;;cout << name;}
将该文件另存为name.cpp.
其中首先引用的文件是预编译头文件,这将引用两个标准库头文件,即<iostream)和<string> ,因此我们可以在这些文件中使用类型
函数中第一行的含义是在控制台上打印输出字符串&34;.
企业回北京大鱼科技-,小程序开发认证,专注视觉和用户体验,小程序定制服务
注意,在该问题末尾有一个空格,因此光标将保持在同一行,等待输入信息。
接下来的一行
该函数会将变量name中的内容打印输出到控制台,并且不带换行符。
现在为该源代码文件添加一条规则到makefile中,即添加如下代码到该文件的顶部:
name.obj: name,cpp name.h utils,pchCl /EHSC /c name.cpp /Yuutils.h
保存该文件,然后运行make工具,以确认它是否生成了name.obj目标文件。
1.5.5 time函数
最终的源代码文件将会获取时间,并将它打印输出到控制台。创建一个C++源代码文件并添加如下代码
行:
34;utils.h&include &34;void print_time(){std::time_t now = std::time(nullptr);std::cout <<&34;<< std::ctime(&now) << std::endl;}
sta::time 和std::gmtime 这两个函数是C函数,并且std:time_t 是一个C类型,所有这些都是通过C++标准库获得的。std:time 函数获取的时间是1970年1月1日午夜以来的秒数。该函数会返回一个std::time_t类型的值,即一个64位整数。如果用户通过指针传递变量在存储中的存储位置,则该函数可以将上述整数值拷贝到另外一个变量中。
在这个示例中,我们不需要这个工具,因此我们传递一个C++的nullptr 给该函数,以
接下来,我们需要将秒数转换成包含时间和日期的字符格式,以方便用户理解。这也是std::ctime函数的主要用途,它会接收一个指向保存秒数变量的指针作为参数。变量now保存了秒数,运算符&用于获取变量的内存地址。第4章将详细介绍变量和指针的细节。 std::ctime函数会返回一个字符串,不过我们还没有为该字符分配任何内存,也不应该尝试为该字符串分配内存。 std:ctime 函数创建了一个静态分配内存缓冲区,它将被运行在当前执行线程的所有代码共享使用。每次在相同执行线程上调用std:ctime函数时,使用的内存地址是一样的。不过内存中的内容可能会发生变化。
此函数说明检查帮助手册,查看谁负责分配和释放内存是非常重要的。第4章将详细介绍分配内存的细
节。
从std:ctime返回的字符被打印输出到控制台,并且是通过调用若干次运算符<<对输出结果进行格式化的。
现在给makefile添加一条构建规则,即添加如下规则到该文件顶部:
time.obj: time. cpp time.h utils.pchC1 /EHsc /c time.cpp /Yuutils.n
保存该文件并运行make工具,然后确认该构建过程是否生成了time.obj 目标文件。
1.5.6构建可执行文件
现在我们已经拥有了项目所需的所有对象文件,因此下一个任务是将它们链接到一起。为此,将下列代码添加到makefile文件顶部
time_test.exe: main.obj name.obj time.obj utils.objink /out:$@$**
这里的目标是可执行文件,并且依赖项是4个对象文件。命令行中为了构建可执行文件会调用链接器并使用特殊的语法。标识符$@会被make工具解析为使用目标,所以/out开关实际的内容是
out:time_test.out 。标识符$**会被make工具解析为使用所有依赖项,因此所有依赖项都将被链接。
保存该文件并运行make程序。用户将发现只有链接器被调用,并且它将链接所有对象文件继而创建可执行文件。
最后,添加一条规则来清理项目。提供一种机制移除编译过程中生成的文件,只保留源代码文件,从而保持项目结构的整洁是一个非常好的习惯。在链接对象文件的代码行之后,添加如下代码:
clean任务是一个伪目标,实际上并没有生成文件,因此也不存在依赖项。这说明了make程序的个特性,如果调用mmake工具,并指定目标名称,那么该程序将只生成该目标。如果没有
1.5.7 测试代码
再次运行make程序,以便构建可执行文件。在命令行中,可以通过输入time_test命令运行示例程序。你将被要求输入姓名;执行此操作,然后按回车"键,此时将发现自己的名字、当前时间和日期被打印输出到控制台上:
C:Beginning_C++Chapter_01>time_testYour first name? RichardRichard,the time and date are Tue Sep 6 19:32:23 2016
1.5.8 修改项目
现在读者对基本的项目结构有所了解,通过makefile ,你可以对文件进行修改,并确保重新构建项目时,只对发生变更的文件进行编译。为了说明这一点,将修改name.cpp 中的 print_name 函数,以更客气的方式询问你的姓名。修改函数体中第一行代码,比如下列代码中加粗显示的代码行:
void print_name(){std::cout<<&34;;std;;string name;
1、打开excel软件,点击左上角“文件”;2、点击选项;3、在excel选项界面点击自定义功能区,勾选开发工具,点击确定;4、开启开发工具后就可以在主菜单上看到开发工具选项卡了,需要打开VBA就在开发工具选项卡点击visual basi。
保存该文件,然后运行make工具。这一次只有源代码文件main.cpp被编译,生成的name.obj文件会与已有的对象文件链接到一起。
现在修改头文件name.h ,并在其中添加一个注释信息:
/更客气的版本void print_name();
构建该项目。读者发现了什么?这一次,有两个源代码文件被编译,即name.cpp和main.cpp 。并且它们与已有的对象文件链接到一起,从而创建了可执行文件。为了细究这两个文件被编译的原因,可以查看makefile 中的依赖项规则。唯一发生变更的文件是name.h,并且该文件的名字出现在了ame.obj和main.obj依赖项列表上,因此这两个文件被重新构建。由于这两个文件出现在了time_test.exe的依赖项列表上,所以该可执行文件也将被重新构建。
本文节选自《C++编程自学宝典》
本书旨在通过全面细致的内容和代码示例,带领读者更加全方位地认识C++语言。全书内容共计10章,由浅入深地介绍了C++的各项特性,包括C++语法、数据类型、指针、函数、类、面向对象特性、标准库容器、字符串、诊断和调试等。本书涵盖了C++11规范及相关的C++标准库,是全面学习C++编程的合适之选。