c++ using namespace std 如何编译成独立可执行文件_C++编程自学宝典:如何编写一个简单的项目程序?...

1.5编写一个简单的项目程序

这个项目将向读者演示在本章学到的C++语言和项目管理的特性。该项目将使用若干源代码文件,以便读者可以了解到依赖项的作用,以及构建工具是如何管理源代码变更的。这个项目非常简单,它将要求你输入名字,然后在命令行上打印输出名字以及相关的时间和日期。

1.5.1项目结构

该项目包含3个函数: main函数,它会调用其他另外两个函数print-name和print_time 。它们分别位于3个独立的源代码文件中,因为 main函数将调用位于其他源代码文件中的两个函数,这意味着main 函数源代码文件中必须包含这些函数的原型声明。在本示例中,这意味着引用每个文件的头文件。该项目还会使用一个预编译头文件,这表示存在一个源代码文件和一个头文件。总之,项目中将包含3个头文件和4个源代码文件。

1.5.2创建预编译头文件

该代码将使用C++标准库的流对象进行信息的输入和输出,因此我们将使用 头文件。最后,它会访问C运行时库的time 和date 函数,因此代码中还将用到头文件。这些都是标准的头文件,我们在进行程序开发时无需对它们进行修改,因此它们是预编译的理想目标。

在Visual Studio中创建一个C++头文件,然后在其中添加如下代码:

#include #include #include 将它另存为utils.n

新建一个C++源代码文件,添加一行代码来引用刚才创建的头文件:

#include "utils.h"

将其另存为utils.cpp ,我们将需要为这个项目创建一个 makefile ,因此在新建文件对话框中,选择文本文件作为文件类型。添加以下规则来构建预编译头文件:

utils pch utils.obj:: utils cpp utils. hCl /EHsc /c utils.cpp/ Ycutils h

将它另存为文件名为"makefile."的文件,注意要在文件末尾加一个句点( . )符号。因为我们是以文本文件的形式创建该文件的, Visual Studio将自动为它添加一个txt文件后缀,但是因为我们不希望使用该文件后缀,所以需要添加一个句点以表明该文件无后缀名。其中第一行表示utils.pch和utils.obj依赖于特定的源代码文件和头文件。第二行(以一个制表符作为前缀)告知编译器去编译C++文件,但是不调用链接器,同时还告知编译器将预编译代码保存到utils.n文件中。该命令将创建utils.pch和

utils.obj文件,即声明的两个目标。

当make实用程序发现有两个目标时,默认的动作(当目标和依赖项之间使用单个冒号时)是为每个目标都调用一次命令(你可以使用宏来决定哪个目标需要被构建)。这意味着相同的编译器命令将执行两次。这是我们不希望看到的,因为两个目标是通过调用一次命令构建的。双冒号:是一个变通方案,它告知nmake不要采用为每个目标调用命令的行为。最终的结果就是,当make程序调用命令一次,并生成了utils.pch文件,然后它会尝试生成utils.0bj文件,不过它发现该文件已经被生成了,所以不需要再次调用命令。

现在测试输出结果。在命令行中,导航到项目文件夹下,输入nmake命令。

如果没有提供makefile的名称,程序维护工具将自动使用名为makefile的文件(如果你希望使用一个其他名字的makefile ,可以使用/f开关并指定文件名) :

C:Beginning C++Chapter_01Code>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++源代码文件并添加如下代码:

#include "utils,h"#include "name,h"#include "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.5.4输入和输出流

目前为止,我们已经了解了如何通过cout 对象将数据输出到控制台。标准库还提供了一个cin流对象,允许读取命令行中输入的值。

创建一个C++源代码文件,并添加如下代码:

#include "utils.h"#include "name.h" void print_name(){std::cout << "Your first name? ";std:;string name;std::cin >> name;std;;cout << name;}

将该文件另存为name.cpp.

其中首先引用的文件是预编译头文件,这将引用两个标准库头文件,即 ,因此我们可以在这些文件中使用类型声明。

函数中第一行的含义是在控制台上打印输出字符串"Your first name? ".

注意,在该问题末尾有一个空格,因此光标将保持在同一行,等待输入信息。

接下来的一行声明了一个C++字符串对象变量。字符串可以包含0个或者多个字符,并且每个字符都会占用内存。字符串类会处理字符串相关的分配和释放内存的所有工作,该类将在第9章详细介绍。cin重载了运算符》>以便获取控制台的输入信息。当用户按下回车"键之后,运算符>>将把输入内容传递给变量name (将空格字符视为分隔符)。

该函数会将变量name中的内容打印输出到控制台,并且不带换行符。

现在为该源代码文件添加一条规则到makefile中,即添加如下代码到该文件的顶部:

name.obj: name, cpp name.h utils,pchCl /EHSC /c name.cpp /Yuutils.h

保存该文件,然后运行make工具,以确认它是否生成了name.obj目标文件。

1.5.5 time函数

最终的源代码文件将会获取时间,并将它打印输出到控制台。创建一个C++源代码文件并添加如下代码

行:

#include "utils.h"#include "time.h"void print_time(){std::time_t now = std::time(nullptr);std::cout <

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程序。用户将发现只有链接器被调用,并且它将链接所有对象文件继而创建可执行文件。

最后,添加一条规则来清理项目。提供一种机制移除编译过程中生成的文件,只保留源代码文件,从而保持项目结构的整洁是一个非常好的习惯。在链接对象文件的代码行之后,添加如下代码:

time_test.exe: main.obj name.obj time.obj utils.objlink /out:@$**clean: @echo Cleaning the project...del main.obj name.obj time.obj utils.obj utils.pckdel time_test.exe

clean任务是一个伪目标,实际上并没有生成文件,因此也不存在依赖项。这说明了make程序的个特性,如果调用mmake工具,并指定目标名称,那么该程序将只生成该目标。如果没有声明目标,那么该程序将生成makefile提及的第一个目标,在这种情况下是time_test.exe

clean 伪目标包含3个命令:第一个命令会将"Cleaning the project.打印输出到控制台,标识符e会告知命令行不要将该命令打印输出到控制台;第二个和第三个命令会调用命令行工具de1删除文件。现在可以在命令行上输入 mmake clean 命令清理项目,然后确认项目目录下是否只包含头文件、源代码文件和makefile文件。

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<

保存该文件,然后运行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++编程自学宝典》

3eca4a2e304a0219375dfe5adb99a28b.png

本书旨在通过全面细致的内容和代码示例,带领读者更加全方位地认识C++语言。全书内容共计10章,由浅入深地介绍了C++的各项特性,包括C++语法、数据类型、指针、函数、类、面向对象特性、标准库容器、字符串、诊断和调试等。本书涵盖了C++11规范及相关的C++标准库,是全面学习C++编程的合适之选。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值