• C++编程思想


    前言

    用户定义的数据类型(data type)或类(class),是C++区别于传统过程型语言的地方。

    通常将创建好的类库存放在库(library)中。

    本篇会使用几个C++类库(class libraries),如:一个很重要的标准库是输入/输出流库,可以用它从文件或键盘读取数据,并且将数据写入文件和显示出来;还有string类和vector类。

    语言的翻译过程

    任何一种计算机语言都要从某种人们容易理解的形式(源代码)转化为计算机能执行的形式(机器指令)。

    Q: 翻译的分类?

    A: 通常,翻译器(translators)分为两类:解释器(interpreters)和编译器(compilers)

    Q: 解释器?

    A: 解释器将源代码转化成一些动作(它可由多组机器指令组成)并立即执行这些动作。

    A: 如Shell、Basic语言的解释器。传统的Basic解释器一次翻译和执行一次,然后将这一行的解释丢掉,因为解释器必须重新翻译任何重复的代码,程序执行就变慢了。

    A: 而诸如Python语言的解释器,先把整个程序转化成某种中间语言,然后由执行速度更快的解释器来执行。

    Q: 解释器的优缺点?

    A: 使用解释器有很多好处,从写代码到执行代码的转换几乎能立即完成,并且源代码总是现存的,所以一旦出现错误,解释器能很容易地指出。

    A: 对于解释器,较好的交互性和适于快速程序开发(不必要求可执行程序)也是常被提到的两个优点。

    A: 当做大项目时解释性语言就有某些局限性,但似乎Python是一个例外。解释器必须驻留内存以执行程序,而这样一来,即使是最快的解释器其速度也会变得让人难以接受。

    A: 大部分的解释器要求一次输入整个源代码,这不仅造成内存空间的限制,而且如果语言不提供设施来隔离不同代码段之前的影响,一旦出现错误,就很难调试。

    Q: 编译器?

    A: 编译器直接把源代码转化成汇编语言或机器指令,最终的结果是一个或多个机器代码的文件,这是一个复杂的过程,通常分几步完成。

    A: 仰仗编译器设计者的聪明才智,编译器生成的程序往往只需要少的运行空间,并且执行速度更快。虽然编译后的程序较小、运行速度快是人们应当使用编译器的理由,但在许多时候这却不是最重要的。

    A: 某些语言(如C语言)可以分别编译各段程序,最后使用连接器(linker)把各段程序连接成一个完成的可执行程序,这个过程称为分段编译(separate compilcation)。

    Q: 分段编译有什么好处?

    A: 由于编译器或编译环境的限制,不能一次完成编译的整个程序,可以分段编译。

    A: 每次创建和测试程序的一部分,当这部分程序能正常运行后,就把它作为程序块保存起来。人们把测试通过并能正常运行的程序块收集起来加入库(libraray)中,供其他程序员使用。

    A: 由于独立创建每一段程序,其他各段程序的复杂性便被隐藏起来。

    Q: 编译器的调试功能?

    A: 编译器的调试功能不断地得以改进,早起的编译器只能产生机器代码,要知道程序的运行状态,程序员需要插入打印语句,但这样做并不总是有效的。

    A: 现代编译器能在可执行程序中插入与源代码有关的信息,这个信息由一些强大的源代码层的调试器(source-level debugger)使用,以便通过跟踪程序经过源代码的进展来显示程序的执行情况。

    Q: 内存中编译?

    A: 为了提高编译速度,一些编译器采用内存中编译(in-memory compilcation)。

    A: 大多数编译器,编译时每一步都要读写文件。内存中编译器就是将编译器程序存放在RAM中。对于小程序来说,内存中编译器几乎能和解释器一样响应。

    Q: C++编译过程?

    A: 为了用C/C++编程,应该了解编译过程的步骤和所需工具。

    A: 编译C/C++时,首先要对源代码执行预处理。预处理器(preprocessor)是一个简单的程序,它用程序员定义好的模式代替源代码中的模式

    A: 预处理器指令用来节省输入,增加代码的可读性。

    A: C++程序设计并不鼓励多使用预处理指令,因为它可能会引起一些不易发现的错误。

    A: 预处理过程通常存放在一个中间文件中。

    A: 编译一般分两遍进行:

    1. 对预处理过的代码进行语法分析,编译器把源代码分解成小的单元并把它们按树结构组织起来。如表达式A+B中的A,+,B就是语法分析树的叶子节点;

    2. 由代码生成器(code generator)遍历语法分析树,把树的每个节点转化为汇编语言或机器代码。如果代码生成器生成的是汇编语言,那么还必须用汇编器对其汇编。

    A: 有时候会在编译的第一遍和第二遍之间使用全局优化器(global optimizer)来生成更短、更快的代码。

    A: 两种情况的最后结果都是生成目标模块,通常是以一个.o或.obj为扩展名的文件。

    A: 有时也会在编译的第二遍中使用窥孔优化器(peephole optimizer)从相邻一段代码中查找冗余汇编语句。

    A: 连接器(linker)把一组目标模块连接成为一个可执行程序,操作系统可以装载和运行它。

    A: 当某个目标模块中的函数要引用另一个目标模块中的函数或变量时,由连接器来处理这些引用。

    A: 连接器能搜索成为“库”的特殊文件来处理它的所有引用。库将一组目标模块包含在一个文件中。库由一个被称为库管理器(librarian)的程序来创建和维护。

    Alt text

    A: 更多参考:C/C++程序编译过程详解

    Q: 静态类型检查?

    A: 类型检查(type checking)是编译器在第一遍中完成的,类型检查是检查函数参数是否正确使用,以防止许多程序设计错误。

    A: 由于类型检查是在编译期阶段而不是程序运行阶段进行的,所以称之为静态类型检查(Static type checking)。

    A: 某些面向对象的语言,如Java,也可以在程序运行时部分类型检查,这个叫做动态类型检查(dynamic type checking)。

    A: 动态类型检查和静态类型检查结合使用,比仅仅使用静态类型检查更有效,但它也增加了程序执行的开销。

    A: C++使用静态类型检查,因为C++语言不采用任何特殊的运行时支持来处理错误操作。

    A: 静态类型检查在编译时被告知程序员类型被误用,从而加快了执行时的速度。

    A: 在C++里可以不使用静态类型检查,我们可以自己做动态类型检查,不过这需要写一些代码。

    分段编译工具(Tools for separate compilation)

    Q: 为什么要分段编译?

    A: 针对大型的项目,分段编译尤为重要,在C/C++中可以将大程序构造成许多小程序块,而这些小程序块容易管理,可独立测试。

    A: 在C/C++中,以函数(function)作为子程序块,函数是一段代码段,可以将这些函数放在不同的文件中,并能分别编译。

    A: 另一种解释是,函数是程序的基本单位,因为不能把一个函数分开,让其不同的部分放在不同的文件中,因此整个函数必须完整地放在一个文件里。

    A: 当调用函数时,通常要传给它一些参数(argument),这些参数是我们希望函数执行时使用的值。当函数执行完后,可得到一个返回值(return value),返回值是函数作为执行结果返回的一个值。当然也可以编写不带参数且没有返回值的函数。

    Q: 什么是声明?

    A: 程序可由多个文件构成,一个文件中的函数很可能要访问另一些文件中的函数和数据。编译一个文件时,C/C++编译器必须知道在另一些文件中的函数和数据,特别是它的名字和基本用法。

    A: 编译器就是要确保函数和数据被正确地使用,“告知编译器”外部函数和数据的名称以及它们的模样,这一个过程就叫声明(declaration)。

    A: 一旦声明了一个函数或变量,编译器知道怎么检查对它们的引用,以确保引用正确。

    Q: 声明和定义的区别?

    A: 声明(declaration)和定义(definition)这两个术语在C/C++的学习中必须要准确地区分和理解。

    A: 声明是向编译器介绍名字,即标识符,它告诉编译器“这个函数或者这个变量在某处可找到,它的模样像什么”,而定义是说“在这里建立变量”或“在这里建立函数”,它为名字分配了存储空间。

    A: 对于变量,编译器确定变量的大小,然后在内存中开辟空间来确保变量的数据;对于函数,编译器会生成代码,这些代码最终也要占用一定的内存。因此,无论定义的是函数或者是变量,编译器都要为它们在定义点分配存储空间。

    A: 在C/C++中,可以在不同的地方声明相同的变量或函数,但只能有一个定义,有时这被称为ODR(one-definition rule,单一定义规则)。当连接器连接所有的目标模块时,如果发现一个函数或变量有多个定义,连接器就会报错。

    A: 定义也可以是声明,如果定义int x;之前,编译器没有发现标识符x,编译器则把这一标识符看成是声明并立即为它分配存储空间。

    Q: 函数声明的语法?

    A: C/C++的函数声明就是给函数取名、指定函数的参数类型和返回值。

    A: 例如,下面是一个叫func1()的函数声明,它带了两个整数类型的参数并返回一个整数:

    int func1(int, int);

    分号说明声明结束,在这种情况下,它告诉编译器“就这些,这里没有函数定义”。

    C/C++尽量使声明形式和使用形式一致。上面的语句可以如下方式使用:

    int a;    
    ...
    a = func1(2, 3);      

    因为func1()返回的是一个整数,C/C++编译器要检查func1()的使用情况,以确保a能接收返回值,并且还要检查函数参数的类型匹配情况。

    A: 在函数声明时,可以给参数命名,编译器会忽略这些参数名称,但对程序员来说它们可以帮助记忆。例如下面的形式声明func1():

    int func1(int length, int width);       

    Q: C和C++对空参的理解?

    A: 对于带空参数表的函数,C和C++有很大的不同,在C语言中,声明

    int func2();

    表示“一个可带任意参数(任意数目,任意类型)的函数”,这就妨碍了类型检查。

    A: 而在C++语言中它意味着“不带参数的函数”

    Q: 函数的定义?

    A: 函数定义看起来像函数声明,但它还有函数体。

    A: 函数体是一个用大括号括起来的语句集,大括号表示这段代码的开始和结束。下面是定义了一个空函数体的函数func1()

    int func1(int length, int width) { }

    A: 注意,在函数定义中,大括号代替了分号的作用,因为大括号括起来了一条或一组语句,所以就不需要分号了。

    A: 另外也要注意,如果要在函数体中使用参数的话,函数定义中的参数必须要有名称。

    Q: 变量声明的语法?

    A: 对“变量声明”的解释向来很模糊且自相矛盾,而理解它的准确含义对于正确地理解程序十分重要。

    A: 变量声明告知编译器变量的外表特征,这好像是对编译器说:“我知道你以前没有看到过这个名字,但我保证它一定在某个地方,它是X类型的变量”。

    A: 函数声明包括函数返回值类型、函数名称、参数列表和一个分号,这些信息使得编译器足以认出它是一个函数声明并可识别出它的外部特征,由此推断,变量声明应该是数据类型后面跟一个标识符,例如:

    int a;

    但这也产生了一个矛盾:这段代码有足够的信息让编译器为整数a分配空间,而且编译器也确实给整数a分配了空间。要解决这个矛盾,对于C/C++需要使用一个关键字extern来说明“这只是一个声明,它的定义在别的地方”,extern表示变量是在文件以外定义的,或在文件后面部分才定义。

    A: 在变量定义前加extern关键字表示声明一个变量但不定义它,例如

    extern int a;

    A: extern也可用于函数声明,例如:

    extern int func1(int length, int width);

    这种声明方式和先前的func1()声明方式一样,因为没有函数体,编译器必定把它作为声明而不是函数定义。因此extern关键字对函数来说是多余的、可选的。C语言的设计者并不要求函数声明使用extern。

    A: 如果函数声明也要求使用extern,那么在形式上与变量声明更加一致,从而减少混乱,但这就需要更多的输入,这也许能解释为什么不要求函数使用extern的原因。

    A: 示例:declare.cpp

    A: 函数声明时参数标识符是可选的,函数定义时则要求要有标识符

    Q: 包含头文件?

    A: 头文件(header file)是一个含有某个库的外部声明函数和变量的文件,它通常是扩展名为".h"的文件,可能还会看到一些比较老的程序使用其他的扩展名,如".hxx", ".hpp"

    A: 包含头文件,需使用#include预处理器命令,它告诉预处理器打开指定的头文件并在#include语句所在的地方插入头文件。

    A: #include有两种方式指定文件:尖括号和双引号。

    Q: 尖括号和双引号的区别?

    A: 用尖括号指定头文件,如下示例:

    #include <header>

    用尖括号来指定该文件时,预处理器是以特定的方式来寻找文件,一般是环境中或编译器命令行指定的某种寻找路径。这种设置寻找路径的机制随机器、操作系统、C++实现的不同而不同,要视具体情况而定。

    用双引号指定头文件,如下示例:

    #include "local.h"

    用双引号时,预处理器以“由实现定义的方式”来寻找文件,它通常是从当前目录开始寻找,如果文件没有找到,那么include命令就按与尖括号同样的方式重新开始寻找。

    Q: 标准C++ include语句格式?

    A: 随着C++的不断演化,不同的编译器厂商选用了不同的文件扩展名,而且不同的操作系统对文件名有不同的限制,特别是对文件名长度限制,结果引起了对源代码的可移植性的限制。

    A: 为了消除这些差别,标准使用的格式允许文件名长度可以大于众所周知的8个字符,去除了扩展名。例如,代替老式的包含iostream.h的语句#include <iostream.h>,现在可以写成#include <iostream>了。

    A: 如果需要截断文件名和加上扩展名,翻译器会按照一定的方式来实现包含语句,以适应特定的编译器和操作系统。

    A: 如果想使用这种没有扩展名的风格,但编译器厂商没有提供这种支持,也可以将厂商提供的头文件拷贝成没有扩展名的文件。

    A: 从C继承下来的带有传统".h"扩展名的库仍然可用,然后,也可以用更现代的C++风格使用它们,即在文件名签名加一个字母"C"。这样

    #include <stdio.h>
    #include <stdlib.h>

    就变成了

    #include <cstdio>
    #include <cstdlib>

    这就为读者提供一个区分标志,说明所使用的是C还是C++库。

    A: 新的包含格式和老的效果是不一样的:使用.h的文件是老的、非模板化的版本,而没有.h的文件是新的模板化版本。如果在同一个程序中混用这两种形式,会遇到某些问题。

    Q: 连接目标模块?

    A: 连接器(linker)把由编译器生成的目标模块,一般是带".o"或".obj"扩展名的文件,连接成为操作系统可以加载和执行的程序,它是编译过程的最后阶段。

    A: 连接器的特性随系统不同而不同。

    A: 通常,只需告诉连接器目标模块和要连接的库的名称,及可执行程序的名称,连接器就可以开始执行连接任务了,一些系统要求用户自己调用连接器,很多C++软件包可以让用户通过C++编译器来调用连接器。

    A: 某些早期的连接器对目标文件和库文件只查找一次,这些连接器从左到右查找一遍所给的目标文件和库文件列表,因此目标文件和库文件的顺序就特别重要。如果连接的时候遇到一些莫名其妙的问题,那有可能跟给定的连接器的文件顺序有关。

    Q: 使用库文件?

    A: 使用库文件必须:

    1. 包含库的头文件
    2. 使用库中的函数和变量
    3. 把库连接进可执行程序

    目标模块没有进入库时,也可执行上面的步骤,对于C/C++的分段编译,包含头文件和连接目标模块是基本步骤。

    Q: 连接器如何查找库?

    A: 当C/C++要对函数和变量进行外部引用时,如果还未遇到过这个函数或变量的定义时,连接器就会把它的标识符加到“未解决的引用”列表中。如果连接器遇到过,则这就是已解决的引用。

    A: 如果连接器在目标模块列表中不能找到函数或变量的定义,它将去查找库,库有某种索引方式,连接器不必到库里查找所有的目标模块,而只需浏览索引。当连接器在库中找到定义后,就将整个目标模块而不仅仅是函数定义连接到可执行程序。

    A: 注意,仅仅是库中包含所需定义的目标模块加入连接,而不是整个库参加连接,否则程序会变得毫无意义的庞大。

    A: 如果想尽量减少程序的长度,当构建自己的库时,可以考虑一个源代码只放一个函数。

    A: 因为连接器按照指定的顺序查找文件,假设用户定义的函数与标准的库函数同名,且把带有这种函数的文件插到库文件名列表之前,就能用他自己的函数取代库函数。由于在找到库文件之前,连接器已先用用户所给定的函数来解释引用,因此被使用的是用户的函数而不是库函数。注意,这可能是一个bug,并且C++名字空间禁止这么做。

    Q: 秘密地附加模块?

    A: 当创建C/C++可执行程序时,连接器会秘密连接某些模块,其中之一就是启动模块,它包含了对程序的初始化例程(initialization routines)。

    A: 初始化例程是开始执行C/C++程序时必须首先执行一段程序,它创建了堆栈,并初始化程序中的某些变量。

    A: 连接器总是从标准库中查找程序中调用的经过编译的“标准”函数。由于标准库总可以被找到,所以只要在程序中包含所需的头文件,就可以使用库中的任何模块,并且不必告诉连接器去找标准库。例如标准的C++库由iostream()函数,只需包含<iostream>头文件即可。

    Q: 使用简单的C语言库?

    A: 用C++来编写代码,并不禁止用C的库函数,事实上,整个C的库以默认方式包含在标准的C++库中,这些函数代替用户做了大量的工作,因此使用标准C++库,可以节约许多时间。

    A: 在某些情况下必须使用非标准C++库函数的地方,我们也将尽量使用符合POSIX标准的函数。POSIX是基于UNIX上的一个标准,POSIX包括的函数是C++库没有的。

    编写第一个C++程序

    Q: 使用iostream类?

    A: 示例:hello.cpp,运行结果link

    A: 为了声明iostream类中的函数和外部数据,要用如下语句包含头文件:

    #include <iostream>

    A: iostream包自动定义一个名叫cout的变量(对象),它接受所有与标准输出绑定的数据。

    A: C++允许操作符重载,操作符重载后与某种特殊类型的对象一起使用,它就有了新的含义。和iostream对象在一起,操作符"<<"意思就是"发送到"。
    例如:

    cout << "hello, fireway";  

    意思就是把字符串发送到cout对象,cout是控制台输出(console output)的简写。

    A: cout对象按从左到有的顺序将参数打印出来。

    A: 字符串和常数混合出现在cout语句中,使用cout语句是,操作符"<<"根据所带的参数以不同的含义重载,所以当向cout发送不同的参数时,它能“识别应该对这个参数作何处理”。

    A: 注意endl是一个输入输出流函数,它表示一行结束并在行末加上一个换行符。endl是一个函数指针。它的函数定义如下:

    template<typename _CharT, typename _Traits>
      inline basic_ostream<_CharT, _Traits>&
      endl(basic_ostream<_CharT, _Traits>& __os)
      { return flush(__os.put(__os.widen('
    '))); }

    Q: 名字空间?

    A: 在C语言中,当程序达到一定规模之后,会遇到的一个问题是我们“用完了”函数名和标识符。程序通常分成许多块,每一个块由不同的人或小组来构造和连接,由于所有的函数名和标识符都在同一个程序里,这就要求所有的开发人员都必须非常小心,不要碰巧使用了相同的函数名和标识符,导致冲突。

    A: 标准的C++有预防这种冲突的机制:namespace关键字。库或程序中的每一个C++定义集被封装在一个名字空间中。

    A: 如果其他的定义有相同的名字,但它们在不同的名字空间,就不会产生冲突。

    A: 名字空间是十分方便和有用的工具,但名字空间的出现意味着在写程序之前,必须知道它们,可以用一个关键字来声明:“我要使用这个名字空间中的声明或定义”,这个关键字就是"using"。

    A: 所有的标准C++库都封装在一个名字空间std中,std即standard。使用语句如下:

    using namespace std;

    这意味着打开std名字空间,使它的所有名字都可用。有了这条语句,就不用担心特殊的库组件是在一个名字空间中,因为在使用using指令的地方,它使名字空间在整个文件中都是可用的。

    A: 在人们费尽心机把名字空间的名字隐藏起来之后,再暴露名字空间的所有名字,这看起来是矛盾的,而事实上,应该对这样的轻率做法倍加小心。但是,using指令仅暴露当前文件的名字,所以它并不像起初听起来那样严重,但是,如果再想一想在头文件中这样做,就是鲁莽的举动。

    A: 名字空间和包含头文件的方法之前存在着相互关系。现代头文件的包含命令已标准化了,如不带扩展名“.h”,过去典型包含头文件的方式是带上“.h”,如<iostream.h>。那时名字空间不是语言的一部分,所以,对已经存在的代码要提供向后兼容,如果给出

    #include <iostream.h>

    它相当于

    #include <iostream>
    using namespace std;

    Q: 程序的基本结构?

    A: C/C++程序时变量、函数定义、函数调用的集合。程序开始运行时,它执行初始化代码并调用一个特殊的函数“main()”。

    A: C++语言中,main()总是返回int类型。

    关于输入输出流

    Q: 输出的另外一些格式?

    A: 前面看到的仅仅是输入输出流类最基本的用法,它的输出还有另外的一些格式,比如,对于数值的输出格式有十进制、八进制、十六进制。

    示例:stream.cpp,运行结果link

    A: 浮点数的格式由编译器自动确定。

    A: 通过显示类型转换(cast),任何字符都能转换成char类型,发送到流对象。显示类型转换看起来像函数调用:char()带上字符的ASCII值。本示例中,char(27)就是把“escape”发送到cout。

    Q: 字符数组的拼接?

    A: C预处理器的一个重要功能就是可以进行字符数组的拼接(character array concatenation)。如果两个加引号的字符数组邻接,并且它们之前没有标点,编译器就会把这些字符数组连接成单个字符数组。

    A: 当代码列表宽度有限制时,字符数组的拼接就特别有用。

    示例:concat.cpp,运行结果link

    A: 初看,这个程序好像是错的,因为在每行结束没有分号。请记住C/C++是自由格式语言,虽然一般情况下看到在每行的末尾带有一个分号,但实际要求是在每个语句结束时才加分号,而一个语句很可能要写好几行。

    Q: 读取输入数据?

    A: 用来完成标准输入功能的对象是cin(代表console input),cin通常是指从控制台输入,但这种输入可以重定向来自其他输入源。

    A: 和cin一起使用的输入操作符时“>>”,该操作符接受与参数类型相同的输入。

    A: 示例:numconv.cpp,运行结果link

    Q: 调用其他程序?

    A: 用标准的C语言system()函数,C/C++程序可调用任何程序。system()函数在头文件中已声明。

    A: 示例:callhello.cpp,运行结果link

    A: 从这个程序可以看出,在C++中使用普通的C库函数是一件很容易的事,只要包含头文件和调用所需的库函数就行了。如果已经学习过C语言,那么C和C++向上兼容的特性,会为学习C++带来很大的帮助。

    字符串简介

    Q: string类

    A: 虽然字符数组很有用,但它有一定的限制,简单地说它就是内存中的一组字符,如果要用它做什么事情,必须处理所有的细节,例如,引号内字符数组的大小在编译期就确定了,如果想在这样的字符数组中添增字符,需要了解很多有关的知识,包括动态内存管理,字符数组的拷贝、连接等,才能完成添加任务。这正是我们所希望的有一种对象能替我们完成的事。

    A: 标准C++的string类就是设计用来处理并隐藏对字符数组的低级操作,而这些操作早期是由C程序员来完成的。从有C语言以来这些操作就一直是一个编程费时、容易产生错误的地方。

    A: 为使用string类,需要包含C++头文件<string>。string类在名字空间std中,由于操作符重载,string类的使用很直观。

    A: 示例:hellostrings.cpp,运行结果link

    A: 字符串s1和s2开始时是空的。

    A: s3和s4的两种不同初始化方法效果是相同的(用一个string对象来初始化另一个string对象)

    A: 可以用“=”来给string对象赋值,“=”用其右边的内容代替string对象先前的内容,不必为先前的内容费心,它会被自动处理。

    A: 连接string对象,只需用“+”操作符,“+”也可将string连接到字符数组中。

    A: 如果想将string加到一个string或字符数组之后,可以用“+=”操作符完成这一操作。

    A: 输入输出流知道如何处理string类,所以可直接向cout发送string(或能产生string的表达式,如示例中的s1 + s2 + "!")来打印它。

    文件的读写

    Q: 拷贝文件示例

    A: 在C语言中,完成打开和处理文件这样复杂的操作,需要对C语言有较深的了解。然而,C++语言中的iostream库提供了一种简单的方法来处理文件。

    A: 为了打开文件进行读写操作,必须包含<fstream>。虽然<fstream>会自动包含<iostream>,但如果打算使用cin,cout,最好还是显示地包含<iostream>

    A: 示例:scopy.cpp

    A: 在string库中,一个十分有用的函数是istream& getline (istream& is, string& str);,用它可以把一行读入到string对象中,以换行符结束。getline()第一个形参是ifstream对象,从中读取内容,第二个形参是stream对象,函数调用完成之后,string对象就装载了一行的内容。

    Q: 文件拷贝到单独的string?

    A: 示例:fillstring.cpp,把整个文件拷贝成单独的一个string对象。

    A: string具有动态特性,不必担心string的内存分配,只管添加新内容进去就行了,string会自动扩展以保存新的输入。

    vector简介

    Q: 什么是STL?

    A: 人们经常会把标准C++库的容器与算法,和被称为STL的东西相混淆。STL(标准模板类库,Standard Template Library)是94年春天Alex Stepanov(他当时在惠普工作)在加州San Diego的会议上把他的C++库提交给C++标准委员会时使用的名称,这个名称一直沿用下来,特别是惠普决定允许这个库功公开下载后,使用的人就更多了。

    A: C++标准委员会对STL做了大量的修改,将它整合进标准的C++类库。SGI公司不断对STL进行改进,SGI的STL与标准的C++库在许多细节上是不同的,虽然人们经常产生误解,但实际上C++标准是不包括STL的。由于标准C++的容器与算法,与SGI的STL有相同的来源,通常是同名,因此容易引起误会。所以,本书中,将使用“标准C++库”或“标准库容器”或其他类似的说法,避免使用“STL”这个术语。

    A: vector示例一:fillvector.cpp,运行结果link

    A: vector示例二:getwords.cpp,运行结果link

    小结

    A: 本章主要说明,如果有人已经定义了我们所需要的类,则面向对象编程是很容易的事,这时,只需要简单地包含一个头文件,创建对象,并向对象发送消息。如果所用的类功能很强而且设计完善,那么我们不需要花费很多的力气就能编写出很好的程序。

    A: 本章介绍了标准C++库中一些最基本和十分有用的类型:一系列的输入输出流、string类和vector模板,可以看到使用这些类库是多么简单。尽管面向对象编程尽可能使编程工作在较高的层次上进行,但C语言的某些基本知识是不能不知道的。

    A: 更多C++的学习,可以到http://www.cplusplus.com/

  • 相关阅读:
    WinForm微信扫码登录
    php字符串只替换一次
    laravel6 文档
    MYSQL批量修改
    semanage command not found
    SUID、SGID、SBIT
    passwd命令
    redhat配置yum软件仓库
    mount: no medium found on /dev/sr0 找不到介质
    CentOS7 防火墙(firewall)的操作命令
  • 原文地址:https://www.cnblogs.com/fireway/p/10897670.html
Copyright © 2020-2023  润新知