C++ Primer Plus

第1章 预备知识 (已看)

第2章 开始学习C++ (已看)

第3章 处理数据 (已看)

第4章 复合类型 (已看)

第5章 循环和关系表达式 (已看)

第6章 分支语句和逻辑运算符 (已看)

第7章 函数-C++的编程模块  (已看)

第8章 函数探幽 (已看)

第9章 内存模型和名称空间 (已看)

第10章 对象和类 (已看)

第11章 使用类

扫描二维码关注公众号,回复: 4457759 查看本文章

第12章 类和动态内存分配

第13章 类继承

第14章 C++中的代码重用

第15章 友元,异常和其他

第16章 string类和标准模板库

第17章 输入,输出和文件

第18章 探讨C++新标准

附录A 计数系统

附录B C++保留字

附录C ASCII字符集

附录D 运算符优先级

附录E 其他运算符

附录F 模板类string

附录G 标准模板库方法和函数

附录H 精选读物和网上资源

附录I 转换为ISO标准C++

第1章 预备知识

  1.1 C++简介

C++融合了3中不同的编程方式:C语言代表的过程性语言,C++在C语言基础上添加的类代表的面向对象语言,C++模板支持的泛型编程

  1.2 C++简史

    1.2.1 C语言

20世纪70年代早期,贝尔实验室的Dennis Ritchie致力于开发Unix操作系统.为完成这项工作,Ritchie需要一种语言,它必须简洁,能够生成简洁,快速的程序,并能有效地控制硬件

Ritchie希望有一种语言能将低级语言的效率,硬件访问能力和高级语言的通用性,可移植性融合在一起,于是他在旧语言的基础上开发了C语言

    1.2.2 C语言编程原理

C语言与当前最主流的语言一样,在最初面世时也是过程性(procedural)语言,这意味着它强调的是编程的算法方面

自顶向下(top-down)的设计原则

  在C语言中,其理念是将大型程序分解成小型,便于管理的任务.如果其中的一项任务仍然过大,则将它分解为更小的任务.这一过程将一直持续下去,直到将程序划分为小型的,易于编写的模块

    1.2.3 面向对象编程

OOP提供了一种新方法.与强调算法的过程性编程不同的是,OOP强调的是数据.OOP不像过程性编程那样,试图使问题满足语言的过程性方法,而是试图让语言来满足问题的要求.其理念是设计与问题的本质特性相对应的数据格式

在C++中,类是一种规范,它描述了这种新型数据格式,对象是根据这种规范构造的特定数据结构

OOP程序设计方法首先设计类,它们准确地表示了程序要处理的东西.类定义描述了对每个类可执行的操作,然后您便可以设计一个使用这些类的对象的程序.从低级组织(如类)到高级组织(如程序)的处理过程叫做自下向上(bottom-up)的编程

OOP编程并不仅仅是将数据和方法合并为类定义.例如,OOP还有助于创建可重用的代码,这将减少大量的工作.信息隐藏可以保护数据,使其免遭不适当的访问.多态让您能够为运算符和函数创建多个定义,通过编程上下文来确定使用哪个定义.

继承让您能够使用旧类派生出新类.正如接下来将看到的那样,OOP引入了很多新的理念,使用的编程方法不同于过程性编程.它不是将重点放在任务上,而是放在表示概念上

    1.2.4 C++和泛型编程

泛型编程(generic programming)是C++支持的另一种编程模式.它与OOP的目标相同,即使重用代码和抽象通用概念的技术更简单.不过OOP强调的是编程的数据方面,而泛型编程强调的是独立于特定数据类型.它们的侧重点不同.OOP是一个管理大型项目的工具,

而泛型编程提供了指向常见任务(如对数据排序或合并链表)的工具.术语泛型(generic)指的是创建独立于类型的代码.C++数据表示同类型的数据进行排序,通常必须为每种类型创建一个排序函数.泛型编程需要对语言进行扩展,以便可以只编写一个泛型(即不是特定类型的)

函数,并将其用于各种实际类型.C++模板提供了完成这种任务的机制

    1.2.5 C++的起源

与C语言一样,C++也是在贝尔实验室诞生的,Bjarne Stroustrup于20世纪80年代在这里开发出了这种语言

C++是C语言的超集,这意味着任何有效的C程序都是有效的C++程序.它们之间有些细微的差异,但无足轻重

名称C++来自C语言中的递增运算符++,该运算符将变量加1.名称C++表明,它是C的扩充版本

计算机程序将实际问题转换为计算机能够执行的一系列操作.OOP部分赋予了C++语言将问题所涉及的概念联系起来的能力,C部分则赋予了C++语言紧密联系硬件的能力

  1.3 可移植性和标准

    1.3.1 C++的发展

Stroustrup编写的《The Programming Language》包含65页的参考手册,它成了最初的C++事实标准

下一个事实标准是Ellis和Stroustrup编写的《The Annotated C++ Reference Manual》

C++98标准新增了大量特性,其篇幅将近800页,且包含的说明很少

C++11标准的篇幅长达1350页,对旧标准做了大量的补充

    1.3.2 本书遵循的C++标准

  1.4 程序创建的技巧

    1.4.1 创建源代码文件

    1.4.2 编译和链接

  1.5 总结 

第2章 开始学习C++

  2.1 进入C++

#include <iostream>    // 预处理器编译指令#include

int main() {
    using namespace std;

    cout << "Come up and C++ me some time";
    cout << endl;
    cout << "You won't regret it!" << endl;

    cin.get();

    return 0;
}
View Code

    2.1.1 main()函数

通常,main()被启动代码调用,而启动代码是由编译器添加到程序中的,是程序和操作系统之间的桥梁.事实上,该函数头描述的是main()和操作系统之间的接口

在C语言中,省略返回类型相当于说函数的类型为int.然而,C++逐步淘汰了这种用法

int main(void) // very explicit style

ANSI/ISO C++标准对那些抱怨必须在main()函数最后包含一条返回语句过于繁琐做出了让步.如果编译器到达main()函数末尾时没有遇到返回语句,则认为main()函数以return 0;结尾.这条隐含的返回语句只适用于main()函数,而不适用于其他函数

    2.1.2 C++注释

    2.1.3 C++预处理器和iostream文件

C++和C一样,也使用一个预处理器,该程序在进行主编译之前对源文件进行处理.不必执行任何特殊的操作来调用该预处理器,它会在编译程序时自动运行

#include <iostream>

该编译指令导致预处理器将iostream文件的内容添加到程序中.这是一种典型的预处理器操作:在源代码被编译之前,替换或添加文本

这提出了一个问题:为什么要将iostream文件的内容添加到程序中呢?答案涉及程序与外部世界之间的通信.iostream中的io指的是输入(进入程序的信息)和输出(从程序中发送出去的信息).C++的输入/输出方案涉及iostream文件中的多个定义

为了使用cout来显示消息,程序需要这些定义.#include 编译指令导致iostream文件的内容随源代码文件的内容一起被发送给编译器.实际上,iostream文件的内容将取代程序中的代码行#include<iostream>.原始文件没有被修改,而是将源代码文件和iostream

组合成一个复合文件,编译的下一阶段将使用该文件

    2.1.4 头文件名

像iostream这样的文件叫做包含文件(include file)--由于它们被包含在其他文件中;也叫头文件(header file)--由于它们被包含在文件起始处.C++编译器自带了很多头文件,每个头文件都支持一组特定的工具

C语言的传统是,头文件使用扩展名 h,将其作为一种通过名称标识文件类型的简单方式.例如,头文件math.h支持各种C语言数学函数,但C++的用法变了.现在,对老式C的头文件保留了扩展名 h(C++程序仍可以使用这种文件),而C++头文件则没有扩展名.

有些C头文件被转换为C++头文件,这些文件被重新命名,去掉了扩展名 h(使之成为C++风格的名称),并在文件名称前面加上前缀c(表明来自C语言).例如,C++版本的math.h为cmath.有时C头文件的C版本和C++版本相同,而有时候新版本做了一些修改.

对于纯粹的C++头文件(如 iostream)来说,去掉h不只是形式上的变化,没有h的头文件也可以包含名称空间

由于C使用不同的文件扩展名来表示不同文件类型,因此用一些特殊的扩展名(如.hpp或.hxx)表示C++头文件是有道理的,ANSI/ISO委员会也这样认为.问题在于究竟使用哪种扩展名,因此最终它们一致同意不使用任何扩展名

    2.1.5 名称空间

Microflop::wanda("go dancing?");  // use Microflop namespace version

Piscine::wanda("a fish named Desire");  // use Piscine namespace version

按照这种方式,类,函数和变量便是C++编译器的标准组件,它们现在都被放置在名称空间std中.仅当头文件没有扩展名h时,情况才是如此.这意味着在iostream中定义的用于输出的cout变量实际上是std::cout,而endl实际上是std::endl

using namespace std;

这个using编译指令使得std名称空间中的所有名称都可用.这是一种偷懒的方法,在大型项目中是一个潜在的问题.更好的方法是,只使所需的名称可用.

using std::cout;

using std::endl;

using std::cin;

    2.1.6 使用cout进行C++输出

endl是一个特殊的C++符号,表示一个重要的概念:重起一行.在输出流中插入endl将导致屏幕光标移到下一行.诸如endl等对于cout来说有特殊含义的特殊符号被称为控制符(manipulator).和cout一样,endl也是在头文件iostream中定义的,且位于名称空间std中.

C++还提供了另一种在输出中指示换行的旧式方法:C语言符号\n.\n被视为一个字符,名为换行符

一个差别是,endl确保程序继续运行前刷新输出(将其立即显示在屏幕上);而使用"\n"不能提供这样的保证,这意味着在有些系统中,有时可能在您输入信息后才会出现提示

换行符是一种被称为"转义序列"的按键组合

    2.1.7 C++源代码的格式化

  2.2 C++语句

#include <iostream>

int main() {
    using namespace std;

    int carrots;
    carrots = 25;
    cout << "I have ";
    cout << carrots;
    cout << " carrots.";
    cout << endl;
    carrots = carrots - 1;
    cout << "Crunch, crunch. Now I have " << carrots << " carrots." << endl;
    
    cin.get();
    return 0;
}
View Code

    2.2.1 声明语句和变量

    2.2.2 赋值语句

int steinway;

int baldwin;

int yamaha;

yamaha = baldwin = steinway = 88;

赋值将从右到向左进行.首先,88被赋给steinway;然后,stenway的值(现在是88)被赋给baldwin;然后baldwin的值88被赋给yamah(C++遵循C的爱好,允许外观奇怪的代码)

    2.2.3 cout的新花样

  2.3 其他C++语句

#include <iostream>

int main() {
    using namespace std;

    int carrots;

    cout << "How many carrots do you have?" << endl;
    cin >> carrots;
    cout << "Here are two more.";
    carrots = carrots + 2;
    cout << "Now you have " << carrots << " carrots." << endl;

    cin.get();
    cin.get();
    return 0;
}
View Code

    2.3.1 使用cin

cin >> carrots;

从这条语句可知,信息从cin流向carrots.显然,对这一过程有更为正式的描述.就像C++将输出看作是流出程序的字符流一样,它也将输入看作是流入程序的字符流.iostream文件将cin定义为一个表示这种流的对象.

输出时,<<运算符将字符串插入到输出流中;输入时,cin使用>>运算符从输入流中抽取字符

    2.3.2 使用cout进行拼接

    2.3.3 类简介

  2.4 函数

    2.4.1 使用有返回值函数

表达式sqrt(6.25)将调用sqrt()函数.表达式sqrt(6.25)被称为函数调用,被调用的函数叫做被调用函数(called function),包含函数调用的函数叫做调用函数(calling function)

double sqrt(double);  // function prototype

原型结尾的分号表明它是一条语句,这使得它是一个原型,而不是函数头.如果省略分号,编译器将把这行代码解释为一个函数头,并要求接着提供定义该函数的函数体

应在首次使用函数之前提供其原型.通常的做法是把原型放到main()函数定义的前面

#include <iostream>
#include <cmath>

int main() {
    using namespace std;

    double area;
    cout << "Enter the floor area,in square feet,of your home: ";
    cin >> area;
    double side;
    side = sqrt(area);
    cout << "That's the equivalent of a square " << side
        << " feet to the side." << endl;
    cout << "How fascinating!" << endl;

    cin.get();
    cin.get();
    return 0;
}
View Code

C++还允许在创建变量时对它进行赋值,double side = sqrt(are);这个过程叫做初始化(initialization)    

    2.4.2 函数变体

    2.4.3 用户定义的函数

#include <iostream>

void simon(int);

int main() {
    using namespace std;

    simon(3);
    cout << "Pick an integer: ";
    int count;
    cin >> count;
    simon(count);
    cout << "Done!" << endl;
    
    cin.get();
    cin.get();
    return 0;
}

void simon(int n) {
    using namespace std;
    cout << "Simon says touch your toes " << n << " times." << endl;
}
View Code

main()的返回值并不是返回给程序的其他部分,而是返回给操作系统

    2.4.4 用户定义的有返回值的函数

#include <iostream>

int stonetolb(int);

int main() {
    using namespace std;
    int stone;
    cout << "Enter the weight in stone: ";
    cin >> stone;
    int pounds = stonetolb(stone);
    cout << stone << " stone = ";
    cout << pounds << " pounds." << endl;

    cin.get();
    cin.get();

    return 0;
}

int stonetolb(int sts) {
    return 14 * sts;
}
View Code

    2.4.5 在多函数程序中使用using编译指令

  2.5 总结

  2.6 复习题

  2.7 编程练习

第3章 处理数据

  3.1 简单变量

    3.1.1 变量名

    3.1.2 整型

术语宽度(width)用于描述存储整数时使用的内存量.使用的内存越多,则越宽

char类型最常用来表示字符,而不是数字

    3.1.3 整型short,int,long和long long

short是short int 的简称,而long是long int的简称

#include <iostream>
#include <climits>

int main() {
    using namespace std;

    int n_int = INT_MAX;
    short n_short = SHRT_MAX;
    long n_long = LONG_MAX;
    long long n_llong = LLONG_MAX;

    cout << "int is " << sizeof(int) << " bytes." << endl;
    cout << "short is " << sizeof n_short << " bytes." << endl;
    cout << "long is " << sizeof n_long << " bytes." << endl;
    cout << "long long is " << sizeof n_llong << " bytes." << endl;
    cout << endl;

    cout << "Maximum values:" << endl;
    cout << "int: " << n_int << endl;
    cout << "short: " << n_short << endl;
    cout << "long: " << n_long << endl;
    cout << "long long: " << n_llong << endl << endl;
    cout << "Minimum value = " << INT_MIN << endl;
    cout << "Bits per byte = " << CHAR_BIT << endl;

    cin.get();

    return 0;
}
View Code

#define编译指令的工作方式与文本编辑器或字处理器中的全局搜索并替换命令相似.预处理器查找独立的标记(单独的单词),跳过嵌入的单词

#define编译指令是C语言遗留下来的.C++有一种更好的创建符号常量的方法(使用关键字const),所以不会经常使用#define.然而,有些头文件,尤其是那些被设计成可用于C和C++中的头文件,必须使用#define

    3.1.4 无符号类型

#include <iostream>
#define ZERO 0
#include <climits>

int main() {
    using namespace std;
    short sam = SHRT_MAX;
    unsigned short sue = sam;

    cout << "Sam has " << sam << " dollars and Sue has " << sue;
    cout << " dollars deposited." << endl
        << "Add $1 to each account." << endl << "Now ";
    sam = sam + 1;
    sue = sue + 1;
    cout << "Sam has " << sam << " dollars and Sue has " << sue;
    cout << " dollars deposited.\nPoor Sam!" << endl;
    sam = ZERO;
    sue = ZERO;
    cout << "Sam has " << sam << " dollars and Sue has " << sue;
    cout << " dollars deposited." << endl;
    cout << "Take $1 from each account." << endl << "Now ";
    sam = sam - 1;
    sue = sue - 1;
    cout << "Sam has " << sam << " dollars and Sue has " << sue;
    cout << " dollars deposited." << endl << "Lucky Sue!" << endl;


    cin.get();
    return 0;
}
View Code

通常,int被设置为对目标计算机而言最为"自然"的长度.自然长度(natrual size)指的是计算机处理起来效率最高的长度.如果没有非常有说服力的理由来选择其他类型,则应使用int

C++使用前一(两)位来标识数字常量的基数.如果第一位为1~9,则基数为10(十进制);因此93是以10为基数的.如果第一位是0,第二位为1~7,则基数为8(八进制);因此042的基数是8;它相当于十进制数34

如果前两位为0x或0X,则基数为16(十六进制);因此0x42为十六进制数,相当于十进制数66.对于十六进制数,字符a~f和A~F表示了十六进制位,对应于10~15.

#include <iostream>

int main() {
    using namespace std;

    int chest = 42;
    int waist = 0x42;
    int inseam = 042;

    cout << "Monsieur cuts a striking figure!\n";
    cout << "chest = " << chest << " (42 in decimal)\n";
    cout << "waist = " << waist << " (0x42 in hex)\n";
    cout << "inseam = " << inseam << " (042 in octal)\n";

    cin.get();
    return 0;
}
View Code
#include <iostream>

int main() {
    using namespace std;

    int chest = 42;
    int waist = 42;
    int inseam = 42;

    cout << "Monsieur cuts a striking figure!" << endl;
    cout << "chest = " << chest << " (decimal for 42)" << endl;
    cout << hex;
    cout << "waist = " << waist << " (hexadecimal for 42)" << endl;
    cout << oct;
    cout << "inseam = " << inseam << " (octal for 42)" << endl;

    cin.get();
    return 0;
}
View Code

    3.1.5 选择整型类型

    3.1.6 整型字面值

    3.1.7 C++如何确定常量的类型

    3.1.8 char类型:字符和小整数

#include <iostream>

int main() {
    using namespace std;

    char ch;

    cout << "Enter a character: " << endl;
    cin >> ch;
    cout << "Hola! ";
    cout << "Thank you for the " << ch << " character." << endl;
    
    cin.get();
    cin.get();
    return 0;
}
View Code
#include <iostream>

int main() {
    using namespace std;

    char ch = 'M';
    int i = ch;
    cout << "The ASCII code for " << ch << " is " << i << endl;
    cout << "Add one to the character code:" << endl;

    ch = ch + 1;
    i = ch;
    cout << "The ASCII code for " << ch << " is " << i << endl;

    cout << "Displaying char ch using cout.put(ch): ";
    cout.put(ch);

    cout.put('!');
    cout << endl << "Done" << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

有些字符不能直接通过键盘输入到程序中.例如,按回车键并不能使字符串包含一个换行符;相反,程序编辑器将把这种键击解释为在源代码中开始新的一行.其他一些字符也无法从键盘输入,因此C++语言赋予了它们特殊的含义

对于这些字符,C++提供了一种特殊的表示方法-----转义序列.转义序列的概念可追溯到使用电传打字机与计算机通信的时代,现代系统并非都支持所有的转义序列

C++实现支持一个基本的源字符集,即可用来编写源代码的字符集.它由标准美国键盘上的字符(大写和小写)和数字,C语言中使用的符号(如{和=}以及其他一些字符(如换行符和空格)组成.还有一个基本的执行字符集,它包括在程序执行期间可处理的字符(如可从文件中读取或显示到屏幕上的字符).它增加了一些字符,如退格和振铃.C++标准还允许实现提供扩展源字符集和扩展执行字符集.另外,那些被作为字母的额外字符也可用于标识符名称中.也就是说,德国实现可能允许使用日耳曼语的元音变音,而法国实现则允许使用重元音.C++有一种表示这种特殊字符的机制,它独立于任何特定的键盘,使用的是通用字符名(universal character name)

通用字符名的用法类似于转义序列.通用字符名可以以\u或\U打头.\u后面是8个十六进制位,\U后面则是16个十六进制位.这些位表示的是字符的ISO 10646码点(ISO 10646是一种正在制定的国际标准,为大量的字符提供了数值编码)

int k\u00F6rper;
cout << "Let them eat g\u00E2teau.\n";

与int不同的是,char在默认情况下既不是没有符号,也不是有符号.是否有符号由C++实现决定

char fodo;  // may be sigend,may be unsigned
unsigend char bar;  // definitely unsigend
sigend char snark;  // definitely sigend

如果将char用作数值类型,则unsigend char 和 signed char 之间的差异将非常重要.另一方面,如果使用char变量来存储标准ASCII字符,则char有没有符号都没关系,在这种情况下,可以使用char

cin和cout将输入和输出看作是char流,因此不适于用来处理wchar_t类型.iostream头文件的最新版本提供了作用相似的工具--wcin和wcout,可用于处理wchar_t流.另外,可以通过加上前缀L来表示宽字符常量和宽字符串

wchar_t bob = L'P';  // a wide-character constant

wcout << L"tall" << endl;  // outputting a wide-character string

char16_t ch1 = u'q';  // basic character in 16-bit form

char32_t ch2 = U'\U0000222B';  // universal character name in 32-bit form

    3.19 bool类型

ANSI/ISO C++标准添加了一种名叫bool的新类型(对C++来说是新的)

字面值true和false都可以通过提升转换为int类型,true被转换为1,而false被转换为0

int ans = true;  // ans assigend 1
int promise = false;  // promise assigned 0

另外,任何数字值或指针值都可以隐式转换(即不用显式强制转换)为bool值.任何非零值都被转换为true,而零被转换为false;

bool start = -100;  // start assigned true

bool stop = 0;  // stop assigned false

  3.2 const 限定符

关键字const叫做限定符,因为它限定了声明的含义

如果以前使用过C语言,您可能觉得前面讨论的#define语句已经足够完成这样的工作了.但const比#define好,首先,它能够明确指定类型.其次,可以使用C++的作用域规则将定义限制在特定的函数或文件中.第三,可以将const用于更复杂的类型

ANSI C也使用const限定符,这是从C++借鉴来的.如果熟悉ANSI C版本,则应注意,C++版本稍微有些不同.区别之一是作用域规则.另一个主要的区别是,在C++中可以const值来声明数组长度

  3.3 浮点数

    3.3.1 书写浮点数

d.dddE+n指的是将小数点向右移n位,而d.dddE-n指的是将小数点向左移n位.之所以称为"浮点",就是因为小数点可移动

    3.3.2 浮点类型

和ANSI C一样,C++也有3种浮点类型:float,double和long double.这些类型是按它们可以表示的有效数位和允许的指数最小范围来描述的.有效位(significant figure)是数字中有意义的位.例如,加利福利亚的Shasta山脉的高度位14179英尺,该数字使用了5个有效位

指出了最接近的英尺数.然而,将Shasta山脉的高度写成约14000英尺时,有效位数为2位,因为结果经过四舍五入精确到了千位.在这种情况下,其余的3位只不过是占位符而已.有效位数不依赖于小数点的位置.例如,可以将高度写成14.162千英尺.这样仍有5个有效位,

因为这个值精确到了第5位

事实上,C和C++对于有效位数的要求是,float至少32位,double至少48位,且不少于float,long double至少和double一样多.这三种类型的有效位数可以一样多.然而,通常,float为32位,double为64位,long double为80,96或128位.另外,这3种类型的指数范围至少是-37到37

#include <iostream>

int main() {
    using namespace std;

    cout.setf(ios_base::fixed, ios_base::floatfield);    // fixed-point
    float tub = 10.0 / 3.0;    // good to about 6 places
    double mint = 10.0 / 3.0;    // good to about 15 places
    const float million = 1.0e6;

    cout << "tub = " << tub;
    cout << ", a million tubs = " << million * tub;
    cout << ",\nand ten million tubs = ";
    cout << 10 * million * tub << endl;

    cout << "mint = " << mint << " and a million mints = ";
    cout << million * mint << endl;

    cin.get();

    return 0;
}
View Code

    3.3.3 浮点常量

在默认情况下,像8.24和2.4E8这样的浮点常量都属于double类型.如果希望常量为float类型,请使用f或F后缀.对于long double类型,可使用l或L后缀(由于l看起来像数字1,因此L是更好的选择).

#include <iostream>

int main() {
    using namespace std;

    float a = 2.34E+22f;
    float b = a + 1.0f;

    cout << "a = " << a << endl;
    cout << "b - a = " << b - a << endl;

    cin.get();

    return 0;
}
View Code

    3.3.4 浮点数的优缺点

C++对基本类型进行分类,形成了若干个族.类型signed char,short,int和long统称为符号类型;它们的无符号版本统称为无符号类型;C++11新增了long long,bool,wchar_t,符号整型和无符号整型;C++11新增了char16_t和char32_t.float,double和long double统称为浮点型.

整数和浮点型统称算术(arithmetic)类型

  3.4 C++算术符

#include <iostream>

int main() {
    using namespace std;
    float hats, heads;

    cout.setf(ios_base::fixed, ios_base::floatfield);
    cout << "Enter a number: ";
    cin >> hats;
    cout << "Enter another number: ";
    cin >> heads;

    cout << "hats = " << hats << "; heads = " << heads << endl;
    cout << "hats + heads = " << hats + heads << endl;
    cout << "hats - heads = " << hats - heads << endl;
    cout << "hats * heads = " << hats * heads << endl;
    cout << "hats / heads = " << hats / heads << endl;


    cin.get();
    cin.get();

    return 0;
}
View Code

也许读者对得到的结果心存怀疑.11.17加上50.25应等于61.42,但是输出种却是61.419998.这不是运算问题;而是由于float类型表示有效位数的能力有限.记住,对于float,C++只保证6位有效位.如果将61.419998四舍五入成6位,将得到61.4200,这是保证精度下的正确值.

如果需要更高的精度,请使用double或long double.

    3.4.1 运算符优先级和结合性

C++使用优先级规则来决定首先使用哪个运算符

当两个运算符的优先级相同时,C++将看操作数的结合性(associativity)是从左到右,还是从右到左.从左到右的结合性意味着如果两个优先级相同的运算符被同时用于同一个操作数,则首先应用左侧的运算符.从右到左的结合性则首先应用右侧的运算符

注意,仅当两个运算符被用于同一个操作数时,优先级和结合性规则才有效.

int dues = 20 * 5 + 24 * 6;

运算符优先级表明了两点:程序必须在做加法之前计算20 * 5,必须在做加法之前计算24 * 6.但优先级和结合性都没有指出应先计算哪个乘法.读者可能认为,结合性表明应先做左侧的乘法,但是在这种情况下,两个*运算符并没有用于同一个操作数,所以该规则不适用

事实上,C++把这个问题留给了实现,让它来决定在系统种的最佳顺序

    3.4.2 除法分支

#include <iostream>

int main() {
    using namespace std;

    cout.setf(ios_base::fixed, ios_base::floatfield);
    cout << "Integer division: 9 / 5 = " << 9 / 5 << endl;
    cout << "Floating-point division: 9.0 / 5.0 = ";
    cout << 9.0 / 5.0 << endl;
    cout << "Mixed divison: 9.0 / 5 = " << 9.0 / 5 << endl;
    cout << "double constants: 1e7/9.0 = ";
    cout << 1.e7 / 9.0 << endl;
    cout << "float constants: 1e7f/ 9.0f = ";
    cout << 1.e7f / 9.0f << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    3.4.3 求模运算符

#include <iostream>

int main() {
    using namespace std;

    const int Lbs_per_stn = 14;
    int lbs;

    cout << "Enter your weight in pounds: ";
    cin >> lbs;
    int stone = lbs / Lbs_per_stn;
    int pounds = lbs % Lbs_per_stn;
    cout << lbs << " pound are " << stone
        << " stone, " << pounds << " pound(s).\n";

    cin.get();
    cin.get();
    return 0;
}
View Code

    3.4.4 类型转换

#include <iostream>

int main() {
    using namespace std;

    cout.setf(ios_base::fixed, ios_base::floatfield);
    float tree = 3;    // int converted to float
    int guess(3.9832);    // double converted to int
    int debt = 7.2E12;    // result not defined in C++
    cout << "tree = " << tree << endl;
    cout << "guess = " << guess << endl;
    cout << "debt = " << debt << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

在计算表达式时,C++将bool,char,unsigned char,signed char和short值转换为int.这些转换被称为整型提升(integral promotion)

强制类型转换不会修改变量本身,而是创建一个新的,指定类型的值,可以在表达式种使用这个值.

(typeName) value  // converts value to typeName type

typeName (value)  // converts value to typeName type

第一种格式来自C语言,第二种格式是纯粹的C++.新格式的想法是,要让强制类型转换就像是函数调用.这样对内置类型的强制类型转换就像是为用户定义的类设计的类型转换

#include <iostream>

int main() {
    using namespace std;
    int auks, bats, coots;

    // the following statement adds the values as double,
    // then converts the result to int
    auks = 19.99 + 11.99;

    // these statements add values as int
    bats = (int)19.99 + (int)11.99;    // old C syntax
    coots = int(19.99) + int(11.99);    // new C++ syntax;
    cout << "auks = " << auks << ", bats = " << bats;
    cout << ", coots = " << coots << endl;

    char ch = 'Z';
    cout << "The code for " << ch << " is ";    // print as char
    cout << int(ch) << endl;    // print as int
    cout << "Yes, the code is ";
    cout << static_cast<int>(ch) << endl;    // using static_cast

    cin.get();
    return 0;

}
View Code

    3.4.5 C++11中的auto声明

  3.5 总结

  3.6 复习题

  3.7 编程练习

第4章 复合类型

  4.1 数组

数组之所以被称为复合类型,是因为它是使用其他类型来创建的.不能仅仅将某种东西声明为数组,它必须是特定类型的数组.没有通用的数组类型,但存在很多特定的数组类型,如char数组或long数组.

#include <iostream>

int main() {
    using namespace std;
    int yams[3];
    yams[0] = 7;
    yams[1] = 8;
    yams[2] = 6;

    int yamcosts[3] = { 20,30,5 };

    cout << "Total yams = ";
    cout << yams[0] + yams[1] + yams[2] << endl;

    cout << "The package with " << yams[1] << " yams costs ";
    cout << yamcosts[1] << " cents per yam.\n";
    
    int total = yams[0] * yamcosts[0] + yams[1] * yamcosts[1];
    total = total + yams[2] * yamcosts[2];
    cout << "The total yam expense is " << total << " cents.\n";

    cout << "\nSize of yams array = " << sizeof yams;
    cout << " bytes.\n";
    cout << "Size of one element = " << sizeof yams[0];
    cout << " bytes.\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.1.1 程序说明

    4.1.2 数组的初始化规则

    4.1.3 C++11数组初始化方法

  4.2 字符串

字符串是存储在内存的连续字节中的一系列字符.C++处理字符串的方式有两种.第一种来自C语言,常被称为C-风格字符串(C-style string).另一种基于string类库的方法

    4.2.1 拼接字符串常量

    4.2.2 在数组中使用字符串

#include <iostream>
#include <cstring>

int main() {
    using namespace std;

    const int Size = 15;
    char name1[Size];
    char name2[Size] = "C++owboy";

    cout << "Howdy! I'm " << name2;
    cout << "! What's your name?\n";
    cin >> name1;
    cout << "Well, " << name1 << ", your name has ";
    cout << strlen(name1) << " letters and is stored\n";
    cout << "in an array of " << sizeof(name1) << " bytes.\n";
    cout << "Your initial is " << name1[0] << ".\n";
    name2[3] = '\0';
    cout << "Here are the first 3 characters of my name: ";
    cout << name2 << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.2.3 字符串输入

#include <iostream>

int main() {
    using namespace std;

    const int ArSize = 20;
    char name[ArSize];
    char dessert[ArSize];

    cout << "Enter your name:\n";
    cin >> name;
    cout << "Enter your favorite dessert:\n";
    cin >> dessert;
    cout << "I have some delicious " << dessert;
    cout << " for you, " << name << ".\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.2.4 每次读取一行字符串输入

#include <iostream>

int main() {
    using namespace std;

    const int ArSize = 20;
    char name[ArSize];
    char dessert[ArSize];

    cout << "Enter your name:\n";
    cin.getline(name, ArSize);
    cout << "Enter your favorite dessert:\n";
    cin.getline(dessert, ArSize);
    cout << "I have some delicious " << dessert;
    cout << " for you, " << name << ".\n";

    cin.get();

    return 0;
}
View Code
#include <iostream>

int main() {
    using namespace std;

    const int ArSize = 20;
    char name[ArSize];
    char dessert[ArSize];

    cout << "Enter your name:\n";
    cin.get(name, ArSize).get();
    cout << "Enter your favorite dessert:\n";
    cin.get(dessert, ArSize).get();
    cout << "I have some delicious " << dessert;
    cout << " for you, " << name << ".\n";

    cin.get();

    return 0;
}
View Code

    4.2.5 混合输入字符串和数字

  4.3 string简介

#include <iostream>
#include <string>

int main() {
    using namespace std;

    char charr1[20];
    char charr2[20] = "jaguar";
    string str1;
    string str2 = "panther";

    cout << "Enter a kind of feline: ";
    cin >> charr1;
    cout << "Enter another kind of feline: ";
    cin >> str1;
    cout << "Here are some felines:\n";
    cout << charr1 << " " << charr2 << " "
        << str1 << " " << str2
        << endl;
    cout << "The thrid letter in " << charr2 << " is "
        << charr2[2] << endl;
    cout << "The third letter in " << str2 << " is "
        << str2[2] << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.3.1 C++11字符串初始化

    4.3.2 赋值,拼接和附加

#include <iostream>
#include <string>

int main() {
    using namespace std;

    string s1 = "penguin";
    string s2, s3;

    cout << "You can assign one string object to another: s2 = s1\n";
    s2 = s1;
    cout << "s1: " << s1 << ", s2: " << s2 << endl;
    cout << "You can assign a C-style string to a string object.\n";
    cout << "s2 = \"buzzard\"\n";
    s2 = "buzzard";
    cout << "s2: " << s2 << endl;
    cout << "You can concatenate strings: s3 = s1 + s2\n";
    s3 = s1 + s2;
    cout << "s3: " << s3 << endl;
    cout << "You can append strings.\n";
    s1 += s2;
    cout << "s1 += s2 yields s1 = " << s1 << endl;
    s2 += " for a day";
    cout << "s2 += \" for a day\" yields s2 = " << s2 << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.3.3 string类的其他操作

#include <iostream>
#include <string>
#include <cstring>

int main() {
    using namespace std;

    char charr1[20];
    char charr2[20] = "jaguar";
    string str1;
    string str2 = "panther";

    str1 = str2;
    strcpy_s(charr1, charr2);

    str1 += " paste";
    strcat_s(charr1, " juice");

    int len1 = str1.size();
    int len2 = strlen(charr1);

    cout << "The string " << str1 << " contains "
        << len1 << " characters.\n";
    cout << "The string " << charr1 << " contains "
        << len2 << " characters.\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.3.4 stringl类I/O

#include <iostream>
#include <string>
#include <cstring>

int main() {
    using namespace std;

    char charr[20];
    string str;

    cout << "Length of string in charr before input: "
        << strlen(charr) << endl;
    cout << "Length of string in str before input: "
        << str.size() << endl;
    cout << "Enter a line of text:\n";
    cin.getline(charr, 20);
    cout << "You entered: " << charr << endl;
    cout << "Enter another line of text:\n";
    getline(cin, str);
    cout << "You entered: " << str << endl;
    cout << "Length of string in charr after input: "
        << strlen(charr) << endl;
    cout << "Lenth of string in str after input: "
        << str.size() << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.3.5 其他形式的字符串字面值

wchar_t title[] = L"Chief Astrogator";  // w_char string
char16_t name[] = u"Felonia Ripova";  // char_16 string
char32_t car[] = U"Humber Super Snipe";  // char_32 string
C++11还支持Unicode字符编码方案UTF-8.在这种方案中,根据编码的数字值,字符可能存储为1~4个八位组.C++使用前缀u8来表示这种类型的字符串字面值
cout << R"(Jim "King" Tutt uses "\n" instead of endl.)" << '\n';

  4.4 结构简介

    4.4.1 在程序中使用结构

#include <iostream>

struct inflatable {
    char name[20];
    float volume;
    double price;
};


int main() {
    using namespace std;

    struct inflatable guest = {
        "Glorious Gloria",
        1.88,
        29.99
    };

    inflatable pal = {
        "Audacious Arthur",
        3.12,
        32.99
    };

    cout << "Expand your guest list with " << guest.name;
    cout << " and " << pal.name << "!\n";
    cout << "You can have both for $";
    cout << guest.price + pal.price << "!\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.4.2 C++11结构初始化

    4.4.3 结构可以将string类作为成员吗

    4.4.4 其他结构属性

可以使用赋值运算符(=)将结构赋给另一个同类型的结构,这样结构中每个成员都将被设置位另一个结构中相应成员的值,即使成员是数组.这种赋值被称为成员赋值(memberwise assignment)

#include <iostream>

struct inflatable {
    char name[20];
    float volume;
    double price;
};


int main() {
    using namespace std;

    inflatable bouquet = {
        "sunflowers",
        0.20,
        12.49
    };
    inflatable choice;

    cout << "bouquet: " << bouquet.name << " for $";
    cout << bouquet.price << endl;

    choice = bouquet;
    cout << "choice: " << choice.name << " for $";
    cout << choice.price << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.4.5 结构数组

#include <iostream>

struct inflatable {
    char name[20];
    float volume;
    double price;
};


int main() {
    using namespace std;

    inflatable guests[2] = {
        { "Bambi", 0.5, 21.99 },
        { "Godzilla", 2000, 565.99 }
    };

    cout << "The guests " << guests[0].name << " and " << guests[1].name
        << "\nhave a combined volume of "
        << guests[0].volume + guests[1].volume << " cubic feet.\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.4.6 结构中的位字段

struct torgle_register {
    unsigned int SN : 4;
    unsigned in : 4;
    bool goodIn : 1;
    bool goodTorgle : 1;
};
View Code

  4.5 共用体

  4.6 枚举

    4.6.1 设置枚举量的值

enum bits { one = 1, two = 2, four = 4, eight = 8 };
enum bigstep { first, second = 100, third };
enum  { zero, null = 0, one, numero_uno = 1 };

    4.6.2 枚举的取值范围

  4.7 指针和自由存储空间

#include <iostream>

int main() {
    using namespace std;
    int donuts = 6;
    double cups = 4.5;

    cout << "donuts value = " << donuts;
    cout << " and donuts address = " << &donuts << endl;

    cout << "cups value = " << cups;
    cout << " and cups address = " << &cups << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

面向对象编程与传统的过程性编程的区别在于,OOP强调的是在运行阶段(而不是编译阶段)进行决策.运行阶段指的是程序正在运行时,编译阶段指的是编译器将程序组合起来时.运行阶段决策就好比度假时,选择参观哪些景点取决于天气和当时的心情;而编译阶段决策更像不管什么条件下,都坚持预先设定的日程安排
使用OOP时,您可能在运行阶段确定数组的长度.为使用这种方法,语言必须允许在程序运行时创建数组.C++采用的方法是,使用关键字new请求正确数量的内存以及使用指针来跟踪新分配的内存的位置
在运行阶段做决策并非OOP独有的,但使用C++编写这样的代码比使用C语言简单

#include <iostream>
#include <string>

int main() {
    using namespace std;
    
    int updates = 6;
    int *p_updates;
    p_updates = &updates;

    cout << "Values: updates = " << updates;
    cout << ", *p_updates = " << *p_updates << endl;

    cout << "Addresses: &updates = " << &updates;
    cout << ", p_updates = " << p_updates << endl;

    *p_updates = *p_updates + 1;
    cout << "Now updates = " << updates << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.7.1 声明和初始化指针

*运算符两边的空格是可选的.传统上,C程序员使用这种格式:
int *ptr; 这强调*ptr是一个int类型的值.而很多C++程序员使用这种格式:
int* ptr; 这强调的是:int*是一种类型------指向int的指针.
在哪里添加空格对于编译器来说没有任何区别,您甚至可以这样做: int*ptr;
在C++中,int*是一种复合类型,是指向int的指针.
和数组一样,指针都是基于其他类型的.

#include <iostream>
#include <string>

int main() {
    using namespace std;
    
    int higgens = 5;
    int* pt = &higgens;
    cout << "Value of higgens = " << higgens
        << "; Address of higgens = " << &higgens << endl;
    cout << "Value of *pt = " << *pt
        << "; Value of pt = " << pt << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.7.2 指针的危险

一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的,适当的地址.这是关于使用指针的金科玉律

    4.7.3 指针和数字

指针不是整型,虽然计算机通常把地址当作整数来处理.从概念来看,指针与整数是截然不同的类型,整数是可以执行加,减,乘,除等运算的数字,而指针描述的是位置,将两个地址相乘没有任何意义.从可以对整数和指针执行的操作上看,它们也是彼此不同的.
int *pt;
pt = 0xB8000000;  // type mismatch
int *pt;
pt = (int *)0xB8000000;  // types now match

    4.7.4 使用new来分配内存

#include <iostream>

int main() {
    using namespace std;
    
    int nights = 1001;
    int * pt = new int;
    *pt = 1001;

    cout << "nights value = ";
    cout << nights << ": location " << &nights << endl;
    cout << "int ";
    cout << "value = " << *pt << ": location = " << pt << endl;

    double * pd = new double;
    *pd = 10000001.0;

    cout << "double ";
    cout << "value = " << *pd << ": location = " << pd << endl;
    cout << "location of pointer pd: " << &pd << endl;

    cout << "size of pt = " << sizeof(pt);
    cout << ": size of *pt = " << sizeof(*pt) << endl;
    cout << "size of pd = " << sizeof pd;
    cout << ": size of *pd = " << sizeof(*pd) << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

对于指针,需要指出的另一点是,new分配的内存块通常与常规变量声明分配的内存块不同.变量nights和pd的值都存储在被称为栈(stack)的内存区域中,而new从被称为堆(heap)或自由存储区(free store)的内存区域分配内存.

    4.7.5 使用delete释放内存

int * ps = new int;  // allocate memory with new
...  // use the memory
delete ps;  // free memory with delete when done
int jugs = 5;  // ok
int * pi = &jugs;  // ok
delete pi;  // not allowed,memory not allocated by new
int * ps = new int;  // allocate memory
int * pq = ps;  // set second pointer to same block
delete pq;  // delete with second pointer

一般来说,不要创建两个指向同一个内存块的指针,因为这将增加错误地删除同一个内存块两次的可能性.

    4.7.6 使用new来创建动态数组

#include <iostream>

int main() {
    using namespace std;

    double * p3 = new double[3];
    p3[0] = 0.2;
    p3[1] = 0.5;
    p3[2] = 0.8;
    cout << "p3[1] is " << p3[1] << ".\n";
    p3 = p3 + 1;
    cout << "Now p3[0] is " << p3[0] << " and ";
    cout << "p3[1] is " << p3[1] << ".\n";
    p3 = p3 - 1;
    delete[] p3;



    cin.get();
    cin.get();

    return 0;
}
View Code

  4.8 指针,数组和指针算术

将整数变量加1后,其值将增加1;但将指针变量加1后,增加的量等于它指向的类型的字节数
C++将数组名解释为地址

#include <iostream>

int main() {
    using namespace std;

    double wages[3] = { 100000.0, 20000.0, 30000.0 };
    short stacks[3] = { 3, 2, 1 };

    // here are two ways to get the address of an array
    double * pw = wages;    // name of an array = address
    short * ps = &stacks[0];    // name of an array = address

    cout << "pw = " << pw << ", *pw = " << *pw << endl;
    pw = pw + 1;
    cout << "add 1 to the pw pointer:\n";
    cout << "pw = " << pw << ", *pw = " << *pw << "\n\n";

    cout << "ps = " << ps << ", *ps = " << *ps << endl;
    ps = ps + 1;
    cout << "add 1 to the ps pointer:\n";
    cout << "ps = " << ps << ", *ps = " << *ps << "\n\n";

    cout << "access two elements with array notation\n";
    cout << "stacks[0] = " << stacks[0]
        << ", stacks[1] = " << stacks[1] << endl;
    cout << "access two elements with pointer notation\n";
    cout << "*stacks = " << *stacks
        << ", *(stacks + 1) = " << *(stacks + 1) << endl;

    cout << sizeof(wages) << " = size of wages array\n";
    cout << sizeof(pw) << " = size of pw pointer\n";


    cin.get();
    cin.get();

    return 0;
}
View Code

    4.8.1 程序说明

#include <iostream>

int main() {
    using namespace std;

    short tell[10];
    cout << tell << "," << tell + 1 << endl;    // displays &tell[0]

    short(*pas)[10] = &tell;
    cout << pas << "," << &tell << "," << &tell + 1 << endl;    // displays address of whole array

    cin.get();
    cin.get();

    return 0;
}
View Code

    4.8.2 指针小结

    4.8.3 指针和字符串

在cout和多数C++表达式中,char数组名,char指针以及用引号括起的字符串常量都被解释为字符串第一个字符的地址.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>

int main() {
    using namespace std;

    char animal[20] = "bear";
    const char * bird = "wren";
    char * ps;

    cout << animal << " and ";
    cout << bird << "\n";

    cout << "Enter a kind of animal: ";
    cin >> animal;

    ps = animal;
    cout << ps << "!\n";
    cout << "Before using strcpy():\n";
    cout << animal << " at " << (int *)animal << endl;
    cout << ps << " at " << (int *)ps << endl;

    ps = new char[strlen(animal) + 1];
    strcpy(ps, animal);
    cout << "After using strcpy():\n";
    cout << animal << " at " << (int *)animal << endl;
    cout << ps << " at " << (int *)ps << endl;


    delete[] ps;

    cin.get();
    cin.get();

    return 0;
}
View Code

 一般来说,如果给cout提供一个指针,它将打印地址.但如果指针的类型为char*,则cout将显示指向的字符串.如果要显示的是字符串的地址,则必须将这种指针强制转换为另一种指针类型,如int*

    4.8.4 使用new创建动态结构

#include <iostream>

struct inflatable {
    char name[20];
    float volume;
    double price;
};

int main() {
    using namespace std;
    
    inflatable * ps = new inflatable;

    cout << "Enter name of inflatable item: ";
    cin.get(ps->name, 20);

    cout << "Enter volume in cubic feet: ";
    cin >> (*ps).volume;

    cout << "Enter price: $";
    cin >> ps->price;

    cout << "Name: " << (*ps).name << endl;
    cout << "Volume: " << ps->volume << " cubic feet\n";
    cout << "Price: $" << ps->price << endl;

    delete ps;
    
    cin.get();
    cin.get();

    return 0;
}
View Code
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstring>

using namespace std;

char * getname(void);

int main() {
    char * name;

    name = getname();
    cout << name << " at " << (int *)name << endl;
    delete[] name;

    name = getname();
    cout << name << " at " << (int *)name << "\n";
    delete[] name;


    cin.get();
    cin.get();

    return 0;
}

char * getname() {
    char temp[80];
    cout << "Enter last name: ";
    cin >> temp;
    char * pn = new char[strlen(temp) + 1];
    strcpy(pn, temp);

    return pn;
}
View Code

    4.8.5 自动存储,静态存储和动态存储

根据用于分配内存的方法,C++有3种管理数据内存的方式:自动存储,静态存储和动态存储(有时也叫作自由存储空间或堆)(C++11新增了第四种类型------线程存储)

1.自动存储

在函数内部定义的常规变量使用自动存储空间,被称为自动变量(automatic variable),这意味着它们在所属的函数被调用时自动产生,在该函数结束时消失

2.静态存储

静态存储是整个程序执行期间都存在的存储方式.使变量成为静态的方式有两种:一种是在函数外面定义它,另一种是在声明变量时使用关键字static
static double fee = 56.50
在K&R C中,只能初始化静态数组和静态结构,而C++Release2.0(及后续版本)和ANSI C中,也可以初始化自动数组和自动结构.然而,一些您可能已经发现,有些C++实现还不支持堆自动数组和自动结构的初始化

3.动态存储

new和delete运算符提供了一种比自动变量和静态变量更灵活的方式.它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或堆(heap).该内存池同用于静态变量和自动变量的内存是分开的.
在栈中,自动添加和删除机制使得占用的内存总是连续的,但new和delete的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难

  4.9 类型组合

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstring>

using namespace std;

struct antarctica_years_end {
    int year;
};

int main() {
    antarctica_years_end s01, s02, s03;
    s01.year = 1998;
    antarctica_years_end * pa = &s02;
    pa->year = 1999;
    antarctica_years_end trio[3];
    trio[0].year = 2003;
    std::cout << trio->year << std::endl;
    const antarctica_years_end * arp[3] = { &s01, &s02, &s03 };
    std::cout << arp[1]->year << std::endl;
    const antarctica_years_end ** ppa = arp;
    auto ppb = arp;
    std::cout << (*ppa)->year << std::endl;
    std::cout << (*(ppb + 1))->year << std::endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

  4.10 数组的代替品

    4.10.1 模板类vector

    4.10.2 模板类array(C++11)

    4.10.3 比较数组,vector对象和array对象

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <vector> // STL C++98
#include <array> // C++ 11


int main() {
    using namespace std;
    // C,original C++
    double a1[4] = { 1.2, 2.4, 3.6, 4.8 };

    // C++98 STL
    vector<double> a2(4);    // create vector with 4 elements
    // no simple way to initialize in C98
    a2[0] = 1.0 / 3.0;
    a2[1] = 1.0 / 5.0;
    a2[2] = 1.0 / 7.0;
    a2[3] = 1.0 / 9.0;

    // C++11 -- create and initialize array object
    array<double, 4> a3 = { 3.14, 2.72, 1.62, 1.41 };
    array<double, 4> a4;

    a4 = a3;    // valid for array objects 
    cout << "a1[2]: " << a1[2] << " at " << &a1[2] << endl;
    cout << "a2[2]: " << a2[2] << " at " << &a2[2] << endl;
    cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
    cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;

    a1[-2] = 20.2;
    cout << "a1[-2]: " << a1[-2] << " at " << &a1[-2] << endl;
    cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
    cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

  4.11 总结

  4.12 复习题

  4.13 编程练习

第5章 循环和关系表达式

  5.1 for循环

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;
    int i;
    for (i = 0; i < 5; i++) {
        cout << "C++ knows loops.\n";
    }
    cout << "C++ knows when to stop.\n";


    cin.get();
    cin.get();

    return 0;
}
View Code

    5.1.1 for循环的组成部分

C++并没有将test-expression的值限制为只能为真或假.可以使用任意表达式,C++将把结果强制转换为bool类型.因此,值为0的表达式被转换为bool值false,导致循环结束.如果表达式的值为非零,则被强制转换为bool值true,循环将继续进行

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;
    cout << "Enter the string countdown value: ";
    int limit;
    cin >> limit;
    int i;
    for (i = limit; i; i--) {
        cout << "i = " << i << "\n";
    }
    cout << "Done now that i = " << i << "\n";


    cin.get();
    cin.get();

    return 0;
}
View Code

C++表达式是值或值与运算符的组合,每个C++表达式都有值.C++将赋值表达式的值定义为左侧成员的值.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;
    
    int x;
    cout << "The expression x = 100 has the value ";
    cout << (x = 100) << endl;
    cout << "Now x = " << x << endl;
    cout << "The expression x < 3 has the value ";
    cout << (x < 3) << endl;
    cout << "The expression x > 3 has the value ";
    cout << (x > 3) << endl;
    cout.setf(ios_base::boolalpha);
    cout << "The expression x < 3 has the value ";
    cout << (x < 3) << endl;
    cout << "The expression x > 3 has the value ";
    cout << (x > 3) << endl;


    cin.get();
    cin.get();

    return 0;
}
View Code

    5.1.2 回到for循环

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

const int ArSize = 16;

int main() {
    using namespace std;
    
    long long factorials[ArSize];
    factorials[1] = factorials[0] = 1LL;
    for (int i = 2; i < ArSize; i++) {
        factorials[i] = i * factorials[i - 1];
    }
    for (int i = 0; i < ArSize; i++) {
        std::cout << i << "! = " << factorials[i] << std::endl;
    }

    cin.get();
    cin.get();

    return 0;
}
View Code

    5.1.3 修改步长

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

const int ArSize = 16;

int main() {
    using std::cout;
    using std::cin;
    using std::endl;

    cout << "Enter an integer: ";
    int by;
    cin >> by;
    cout << "Counting by " << by << "s:\n";
    for (int i = 0; i < 100; i = i + by) {
        cout << i << endl;
    }

    cin.get();
    cin.get();

    return 0;
}
View Code

    5.1.4 使用for循环访问字符串

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>

int main() {
    using namespace std;

    cout << "Enter a word: ";
    string word;
    cin >> word;

    for (int i = word.size() - 1; i >= 0; i--) {
        cout << word[i];
    }

    cout << "\nBye.\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    5.1.5 递增运算符(++)和递减运算符(--)

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>

int main() {
    using std::cout;
    using std::cin;

    int a = 20;
    int b = 20;
    cout << "a = " << a << ": b = " << b << "\n";
    cout << "a++ = " << a++ << ": ++b = " << ++b << "\n";
    cout << "a = " << a << ": b = " << b << "\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    5.1.6 副作用和顺序点

副作用(side effect)指的是在计算表达式时对某些东西(如存储在变量中的值)进行了修改;顺序点(sequence point)是程序执行过程中的一个点,在这里,进入下一步之前将确保对所有的副作用都进行了评估.在C++中,语句中的分号就是一个顺序点,这意味着程序处理下一条语句之前,赋值运算符,递增运算符和递减运算符执行的所有修改都必须完成
任何完整的表达式末尾都是一个顺序点.何为完整的表达式呢?它是这样一个表达式:不是另一个更大表达式的子表达式.
y = (4 + x++) + (6 + x++);
表达式4 + x++不是一个完整表达式,因此,C++不保证x的值在计算子表达式4 + x++后立刻增加1.在这个例子中,整条赋值语句是一个完整表达式,而分号标示了顺序点,因此C++只保证程序执行到下一条语句之前,x的值将被递增两次.C++没有规定在计算每个子表达式后将x的值递增,还是在整个表达式计算完毕后才将x的值递增,有鉴于此,您应避免使用这样的表达式
在C++文档中,不再使用术语"顺序点"了,因为这个概念难以用于讨论多线程执行.相反,使用了术语"顺序",它表示有些事件在其他事件前发生.这种描述方法并非要改变规则,而旨在更清晰的描述多线程编程.

    5.1.7 前缀格式和后缀格式

    5.1.8 递增/递减运算符和指针

前缀递增,前缀递减和解除引用运算符的优先级相同,以从右到左的方式进行结合.后缀递增和后缀递减的优先级相同,但比前缀运算符的优先级高,这两个运算符以从左到右的方式进行结合

    5.1.9 组合赋值运算符

    5.1.10 复合语句(语句块)

    5.1.11 其他语法技巧-逗号运算符

    5.1.12 关系表达式

逗号运算符,首先,它确保先计算第一个表达式,然后计算第二个表达式(换句话说,逗号运算符是一个顺序点),其次,C++规定,逗号表达式的值是第二部分的值.

    5.1.13 赋值,比较和可能犯的错误

    5.1.14 C-风格字符串的比较

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cstring>

int main() {
    using namespace std;

    char word[5] = "?ate";
    for (char ch = 'a'; strcmp(word, "mate"); ch++) {
        cout << word << endl;
        word[0] = ch;
    }

    cout << "After loop ends, word is " << word << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    5.1.15 比较string类字符串.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>

int main() {
    using namespace std;

    string word = "?ate";
    for (char ch = 'a'; word != "mate"; ch++) {
        cout << word << endl;
        word[0] = ch;
    }

    cout << "After loop ends, word is " << word << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

  5.2 while循环

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

const int ArSize = 20;

int main() {
    using namespace std;

    char name[ArSize];
    cout << "Your first name, please: ";
    cin >> name;
    cout << "Here is your name, verticalized and ASCIIized:\n";
    int i = 0;
    while (name[i] != '\0') {
        cout << name[i] << ": " << int(name[i]) << endl;
        i++;
    }

    cin.get();
    cin.get();

    return 0;
}
View Code

    5.2.1 for与while

    5.2.2 等待一段时间:编写延时循环

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <ctime>

int main() {
    using namespace std;

    cout << "Enter the delay time, in seconds: ";
    float secs;
    cin >> secs;
    clock_t delay = secs * CLOCKS_PER_SEC;
    cout << "starting\a\n";
    clock_t start = clock();
    while (clock() - start < delay) {
        ;
    }

    cout << "done \a\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

  5.3 do while循环

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;

    int n;

    cout << "Enter numbers in the range 1-10 to find ";
    cout << "my favorite number\n";
    do {
        cin >> n;
    } while (n != 7);

    cout << "Yes, 7 is my favorite.\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

  5.4 基于范围的for循环(C++11)

  5.5 循环和文本输入

    5.5.1 使用原始的cin进行输入

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;

    char ch;
    int count = 0;
    cout << "Enter characters; enter # to quit:\n";
    cin >> ch;
    while (ch != '#') {
        cout << ch << endl;
        ++count;
        cin >> ch;
    }

    cout << endl << count << " characters read\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    5.5.2 使用cin.get(char)进行补救

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;

    char ch;
    int count = 0;

    cout << "Enter characters; enter # to quit:\n";
    cin.get(ch);
    while (ch != '#') {
        cout << ch;
        ++count;
        cin.get(ch);
    }

    cout << endl << count << " characters read\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    5.5.3 使用哪一个cin.get()

    5.5.4 文件尾条件

很多操作系统(包括Unix,Linux和Windows命令提示符模式)都支持重定向,允许用文件替换键盘输入.这样,程序将从文件(而不是键盘)获取输入.
很多操作系统都允许通过键盘来模拟文件尾条件

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;

    char ch;
    int count = 0;
    cin.get(ch);
    while (cin.fail() == false) {
        cout << ch;
        ++count;
        cin.get(ch);
    }

    cout << endl << count << " characters read\n";


    cin.get();
    cin.get();

    return 0;
}
View Code

    5.5.5 另一个cin.get()版本

  5.6 嵌套循环和二维数组

    5.6.1 初始化二维数组

    5.6.2 使用二维数组

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

const int Cities = 5;
const int Years = 4;

int main() {
    using namespace std;

    const char * cities[Cities] = {
        "Gribble City",
        "Gribbletown",
        "New Gribble",
        "San Gribble",
        "Gribble Vista"
    };

    int maxtemps[Years][Cities] = {
        { 96, 100, 87, 101, 105 },
        { 96, 98, 91, 107, 104 },
        { 97, 101, 93, 108, 107 },
        { 98, 103, 95, 109, 108 }
    };

    cout << "Maximum temperatures for 2008 - 2011\n\n";
    for (int city = 0; city < Cities; ++city) {
        cout << cities[city] << ":\t";
        for (int year = 0; year < Years; ++year) {
            cout << maxtemps[year][city] << "\t";
        }
        cout << endl;
    }

    cin.get();
    cin.get();

    return 0;
}
View Code

  5.7 总结

  5.8 复习题

  5.9 编程练习

第6章 分支语句和逻辑运算符

  6.1 if语句

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

const int Cities = 5;
const int Years = 4;

int main() {
    using std::cin;
    using std::cout;

    char ch;
    int spaces = 0;
    int total = 0;
    cin.get(ch);

    while (ch != '.') {
        if (ch == ' ') {
            ++spaces;
        }
        ++total;
        cin.get(ch);
    }

    cout << spaces << " spaces, " << total;
    cout << " characters total in sentence\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    6.1.1 if else 语句

    6.1.2 格式化if else语句

    6.1.3 if else if else 结构

  6.2 逻辑表达式

    6.2.1 逻辑OR运算符:||

C++规定,||运算符是个顺序点(sequence point).也就是说,先修改左侧的值,再对右侧的值进行判定(C++11的说法是,运算符左边的子表达式先于右边的子表达式)

    6.2.2 逻辑AND运算符:&&

和||运算符一样,&&运算符也是顺序点,因此将首先判定左侧,并且再右侧被判定之前产生所有的副作用

    6.2.3 用&&来设置取值范围

    6.2.4 逻辑NOT运算符:!

    6.2.5 逻辑运算符细节

    6.2.6 其他表示方式

  6.3 字符函数cctype

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <cctype>

int main() {
    using namespace std;

    cout << "Enter text for analysis, and type @"
        " to terminate input.\n";
    char ch;
    int whitespace = 0;
    int digits = 0;
    int chars = 0;
    int punct = 0;
    int others = 0;

    cin.get(ch);
    while (ch != '@') {
        if (isalpha(ch))
            chars++;
        else if (isspace(ch))
            whitespace++;
        else if (isdigit(ch))
            digits++;
        else if (ispunct(ch))
            punct++;
        else
            others++;
        cin.get(ch);
    }

    cout << chars << " letters, "
        << whitespace << " whitespace, "
        << digits << " digits, "
        << punct << " punctuations, "
        << others << " others.\n";


    std::cin.get();
    std::cin.get();

    return 0;
}
View Code

  6.4 ?运算符

  6.5 switch语句

    6.5.1 将枚举量用作标签

    6.5.2 switch和if else

  6.6 break和continue语句

  6.7 读取数字的循环

  6.8 简单文件输入/输出

    6.8.1 文本I/O和文本文件

    6.8.2 写入到文本文件中

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <fstream>

int main() {
    using namespace std;

    char automobile[50];
    int year;
    double a_price;
    double d_price;

    ofstream outFile;
    outFile.open("carinfo.txt");

    cout << "Enter the make and model of automobile: ";
    cin.getline(automobile, 50);

    cout << "Enter the model year: ";
    cin >> year;

    cout << "Enter the original asking price: ";
    cin >> a_price;
    d_price = 0.913 * a_price;

    cout << fixed;
    cout.precision(2);
    cout.setf(ios_base::showpoint);
    cout << "Make and model: " << automobile << endl;
    cout << "Year: " << year << endl;
    cout << "Was asking $" << a_price << endl;
    cout << "Now asking $" << d_price << endl;

    outFile << fixed;
    outFile.precision(2);
    outFile.setf(ios_base::showpoint);
    outFile << "Make and model: " << automobile << endl;
    outFile << "Year: " << year << endl;
    outFile << "Was asking $" << a_price << endl;
    outFile << "Now asking $" << d_price << endl;

    outFile.close();

    return 0;
}
View Code

    6.8.3 读取文本文件

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <fstream>
#include <cstdlib> // support for exit()

const int SIZE = 60;

int main() {
    using namespace std;
    
    char filename[SIZE];

    ifstream inFile;
    cout << "Enter name of data file: ";
    cin.getline(filename, SIZE);
    inFile.open(filename);
    if (!inFile.is_open()) {
        cout << "Could not open the file " << filename << endl;
        cout << "Program terminating.\n";
        exit(EXIT_FAILURE);
    }

    double value;
    double sum = 0.0;
    int count = 0;

    inFile >> value;
    while (inFile.good()) {
        ++count;
        sum += value;
        inFile >> value;
    }

    if (inFile.eof()) {
        cout << "End of file reached.\n";
    } else if (inFile.fail()) {
        cout << "Input terminated by data mismatch.\n";
    } else {
        cout << "Input terminated for unknown reason.\n";
    }

    if (count == 0) {
        cout << "No data processed.\n";
    } else {
        cout << "Items read: " << count << endl;
        cout << "Sum: " << sum << endl;
        cout << "Average: " << sum / count << endl;
    }

    inFile.close();

    cin.get();
    cin.get();

    return 0;
}
View Code

  6.9 总结

  6.10 复习题

  6.11 编程练习

第7章 函数-C++的编程模块

  7.1 复习函数的基本知识

    7.1.1 定义函数

    7.1.2 函数原型和函数调用

  7.2 函数参数和按值传递

函数中用于接收传递值的变量被称为形参.传递给函数的值被称为实参.C++标准使用参数(argument)来表示实参,使用参量(parameter)来表示形参
在函数中声明的变量(包括参数)是该函数私有的.在函数被调用时,计算机将为这些变量分配内存;在函数结束时,计算机将释放这些变量使用的内存.这样的变量被称为局部变量,因为它们被限制在函数中.

    7.2.1 多个参数

#include <iostream>

using namespace std;

void n_chars(char, int);

int main() {

    int times;
    char ch;

    cout << "Enter a characters: ";
    cin >> ch;

    while (ch != 'q') {
        cout << "Enter an integer: ";
        cin >> times;
        n_chars(ch, times);
        cout << "\nEnter another character or press the"
            << " q-key to quit: ";
        cin >> ch;
    }

    cout << "The value of times is " << times << ".\n";
    cout << "Bye\n";


    
    cin.get();
    cin.get();

    return 0;
}

void n_chars(char c, int n) {
    while (n-- > 0) {
        cout << c;
    }
}
View Code

    7.2.2 另外一个接受两个参数的函数

  7.3 函数和数组

    7.3.1 函数如何使用指针来处理数组

在C++中,当(且仅当)用于函数头或函数原型中,int * arr和int arr []的含义才是相同的.它们都意味着arr是一个int指针.然而,数组表示法(int arr [])提醒用户,arr不仅指向int,还指向int数组的第一个int

    7.3.2 将数组作为参数意味着什么

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

const int ArSize = 8;

int sum_arr(int arr[], int n);

int main() {
    int cookies[ArSize] = { 1, 2, 4, 8, 16, 32, 64, 128 };

    std::cout << cookies << " = array address, ";
    std::cout << sizeof cookies << " = sizeof cookies\n";

    int sum = sum_arr(cookies, ArSize);
    std::cout << "Total cookies eaten: " << sum << std::endl;

    sum = sum_arr(cookies, 3);
    std::cout << "First three eaters ate " << sum << " cookies.\n";

    sum = sum_arr(cookies + 4, 4);
    std::cout << "Last four eaters ate " << sum << " cookies.\n";

    std::cin.get();
    std::cin.get();

    return 0;
}

int sum_arr(int arr[], int n) {
    int total = 0;
    std::cout << arr << " = arr, ";
    std::cout << sizeof arr << " = sizeof arr\n";
    for (int i = 0; i < n; i++) {
        total = total + arr[i];
    }
    return total;
}
View Code

    7.3.3 更多数组函数示例

    7.3.4 使用数组区间的函数

STL方法使用"超尾“概念来指定区间.也就是说,对于数组而言,标识数组结尾的参数将是指向最后一个元素后面的指针

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

const int ArSize = 8;

int sum_arr(const int * begin, const int * end);

int main() {
    using namespace std;

    int cookies[ArSize] = { 1, 2, 4, 8, 16, 32, 64, 128 };

    int sum = sum_arr(cookies, cookies + ArSize);
    cout << "Total cookies eaten: " << sum << endl;
    sum = sum_arr(cookies, cookies + 3);
    cout << "First three eaters ate " << sum << " cookies.\n";
    sum = sum_arr(cookies + 4, cookies + 8);
    cout << "Last four eaters ate " << sum << " cookies.\n";

    std::cin.get();
    std::cin.get();

    return 0;
}

int sum_arr(const int * begin, const int * end) {
    const int * pt;
    int total = 0;

    for (pt = begin; pt != end; pt++) {
        total = total + *pt;
    }

    return total;
}
View Code

    7.3.5 指针和const

  7.4 函数和二维数组

  7.5 函数和C-风格字符串

    7.5.1 将C-风格字符串作为参数的函数

    7.5.2 返回C-风格字符串的函数

#include <iostream>

char * buildstr(char c, int n);

int main() {
    using namespace std;

    int times;
    char ch;

    cout << "Enter a character: ";
    cin >> ch;
    cout << "Enter an integer: ";
    cin >> times;
    char * ps = buildstr(ch, times);
    cout << ps << endl;
    delete[] ps;
    ps = buildstr('+', 20);
    cout << ps << "-DONE-" << ps << endl;
    delete[] ps;
    
    cin.get();
    cin.get();

    return 0;
}

char * buildstr(char c, int n) {
    char * pstr = new char[n + 1];
    pstr[n] = '\0';
    while (n-- > 0) {
        pstr[n] = c;
    }
    return pstr;
}
View Code

  7.6 函数和结构

为结构编写函数比为数组编写函数要简单得多.虽然结构变量和数组一样,都可以存储多个数据项,但在涉及到函数时,结构变量得行为更接近与基本得单值变量.也就是说,与数组不同,结构将其数据组合成单个实体或数据对象,该实体被视为一个整体

    7.6.1 传递和返回结构

#include <iostream>

struct travel_time {
    int hours;
    int mins;
};

const int Mins_per_hr = 60;

travel_time sum(travel_time t1, travel_time t2);
void show_time(travel_time t);

int main() {
    using namespace std;
    
    travel_time day1 = { 5, 45 };
    travel_time day2 = { 4, 55 };

    travel_time trip = sum(day1, day2);
    cout << "Two-day total: ";
    show_time(trip);

    travel_time day3 = { 4, 32 };
    cout << "Three-day total: ";
    show_time(sum(trip, day3));
    
    cin.get();
    cin.get();

    return 0;
}

travel_time sum(travel_time t1, travel_time t2) {
    travel_time total;

    total.mins = (t1.mins + t2.mins) % Mins_per_hr;
    total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hr;

    return total;
}

void show_time(travel_time t) {
    using namespace std;
    cout << t.hours << " hours, "
        << t.mins << " minutes\n";
}
View Code

    7.6.2 另一个处理结构的函数示例

#include <iostream>
#include <cmath>

struct polar {
    double distance;
    double angle;
};

struct rect {
    double x;
    double y;
};

polar rect_to_polar(rect xypos);
void show_polar(polar dapos);

int main() {
    using namespace std;
    
    rect rplace;
    polar pplace;

    cout << "Enter the x and y values: ";
    while (cin >> rplace.x >> rplace.y) {
        pplace = rect_to_polar(rplace);
        show_polar(pplace);
        cout << "Next two numbers (q to quit): ";
    }
    cout << "Done.\n";
    
    cin.get();
    cin.get();

    return 0;
}


polar rect_to_polar(rect xypos) {
    using namespace std;
    polar answer;

    answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y);
    answer.angle = atan2(xypos.y, xypos.x);
    return answer;
}

void show_polar(polar dapos) {
    using namespace std;
    const double Rad_to_deg = 57.29577951;

    cout << "distance = " << dapos.distance;
    cout << ", angle = " << dapos.angle * Rad_to_deg;
    cout << " degrees\n";
}
View Code

    7.6.3 传递结构的地址

  7.7 函数和string对象

#include <iostream>
#include <string>

using namespace std;
const int SIZE = 5;
void display(const string sa[], int n);


int main() {
    string list[SIZE];
    cout << "Enter your " << SIZE << " favorite astronomical sights:\n";

    for (int i = 0; i < SIZE; i++) {
        cout << i + 1 << ": ";
        getline(cin, list[i]);
    }

    cout << "Your list:\n";
    display(list, SIZE);
    

    cin.get();
    cin.get();

    return 0;
}

void display(const string sa[], int n) {
    for (int i = 0; i < n; i++) {
        cout << i + 1 << ": " << sa[i] << endl;
    }
}
View Code

  7.8 函数与array对象

#include <iostream>
#include <array>
#include <string>

const int Seasons = 4;
const std::array<std::string, Seasons> Snames = { "Spring", "Summer", "Fall", "Winter" };

void fill(std::array<double, Seasons> * pa);
void show(std::array<double, Seasons> da);

int main() {
    std::array<double, Seasons> expenses;
    fill(&expenses);
    show(expenses);

    std::cin.get();
    std::cin.get();

    return 0;
}


void fill(std::array<double, Seasons> * pa) {
    using namespace std;
    for (int i = 0; i < Seasons; i++) {
        cout << "Enter " << Snames[i] << " expenses: ";
        cin >> (*pa)[i];
    }
}

void show(std::array<double, Seasons> da) {
    using namespace std;

    double total = 0.0;
    cout << "\nEXPENSES\n";
    for (int i = 0; i < Seasons; i++) {
        cout << Snames[i] << ": $" << da[i] << endl;
        total += da[i];
    }
    cout << "Total Expenses: $" << total << endl;
}
View Code

  7.9 递归

    7.9.1 包含一个递归调用的递归

    7.9.2 包含多个递归调用的递归

  7.10 函数指针

    7.10.1 函数指针的基础知识

    7.10.2 函数指针示例

#include <iostream>

double betsy(int);
double pam(int);

void estimate(int lines, double(*pf)(int));

int main() {
    using namespace std;

    int code;
    cout << "How many lines of code do you need? ";
    cin >> code;
    cout << "Here's Betsy's estimate:\n";
    estimate(code, betsy);
    cout << "Here's Pam's estimate:\n";
    estimate(code, pam);

    cin.get();
    cin.get();

    return 0;
}

double betsy(int lns) {
    return 0.5 * lns;
}

double pam(int lns) {
    return 0.3 * lns + 0.00004 * lns * lns;
}

void estimate(int lines, double(*pf)(int)) {
    using namespace std;
    cout << lines << " lines will take ";
    cout << (*pf)(lines) << " hour(s)\n";
}
View Code

    7.10.3 深入探讨函数指针

#include <iostream>

const double * f1(const double arr[], int n);
const double * f2(const double[], int);
const double * f3(const double *, int);

int main() {
    using namespace std;

    double av[3] = { 1112.3, 1542.6, 2227.9 };

    const double *(*p1)(const double *, int) = f1;
    auto p2 = f2;
    cout << "Using pointers to functions:\n";
    cout << " Address Value\n";
    cout << (*p1)(av, 3) << ": " << *(*p1)(av, 3) << endl;
    cout << p2(av, 3) << ": " << *p2(av, 3) << endl;


    const double *(*pa[3])(const double *, int) = { f1, f2, f3 };
    auto pb = pa;
    cout << "\nUsing an array of pointers to functions:\n";
    cout << " Address Value\n";
    for (int i = 0; i < 3; i++) {
        cout << pa[i](av, 3) << ": " << *pa[i](av, 3) << endl;
    }
    cout << "\nUsing a pointer to a pointer to a function:\n";
    cout << " Address Value\n";
    for (int i = 0; i < 3; i++) {
        cout << pb[i](av, 3) << ": " << *pb[i](av, 3) << endl;
    }

    cout << "\nUsing pointers to an array of pointers:\n";
    cout << " Address Value\n";
    auto pc = &pa;
    cout << (*pc)[0](av, 3) << ": " << *(*pc)[0](av, 3) << endl;
    const double *(*(*pd)[3])(const double *, int) = &pa;
    const double * pdb = (*pd)[1](av, 3);
    cout << pdb << ": " << *pdb << endl;
    cout << (*(*pd)[2])(av, 3) << ": " << *(*(*pd)[2])(av, 3) << endl;


    cin.get();
    cin.get();

    return 0;
}

const double * f1(const double * ar, int n) {
    return ar;
}

const double * f2(const double ar[], int n) {
    return ar + 1;
}

const double * f3(const double ar[], int n) {
    return ar + 2;
}
View Code

    7.10.4 使用typedef进行简化

  7.11 总结

  7.12 复习题

第8章 函数探幽

  8.1 C++内联函数

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

inline double square(double x) {
    return x * x;
}

int main() {
    using namespace std;
    
    double a, b;
    double c = 13.0;

    a = square(5.0);
    b = square(4.5 + 7.5);
    
    cout << "a = " << a << ", b = " << b << "\n";
    cout << "c = " << c;
    cout << ", c squared = " << square(c++) << "\n";
    cout << "Now c = " << c << "\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

inline工具是C++新增的特性.C语言使用预处理器语句#define来提供宏------内联代码的原始实现

  8.2 引用变量

C++新增了一种复合类型----引用变量.引用是已定义的变量的别名(另一个名称).引用变量的主要用途是用做函数的形参.通过将引用变量用作参数,函数将使用原始数据,而不是其副本.这样除指针之外,引用也为函数处理大型结构提供了一种非常方便的途径

    8.2.1 创建引用变量

C和C++使用&符号来指示变量的地址.C++给&符号赋予了另一个含义,将其用来声明引用.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;
    
    int rats = 101;
    int & rodents = rats;
    cout << "rats = " << rats;
    cout << ", rodents = " << rodents << endl;
    rodents++;
    cout << "rats = " << rats;
    cout << ", rodents = " << rodents << endl;

    cout << "rats address = " << &rats;
    cout << ", rodents address = " << &rodents << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

引用不同于指针,除了表示法不同外,还有其他的差别.例如,差别之一是,必须在声明引用时将其初始化,而不能像指针那样,先声明,再赋值.
引用更接近于const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它.

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int main() {
    using namespace std;
    
    int rats = 101;
    int & rodents = rats;

    cout << "rats = " << rats;
    cout << ", rodents = " << rodents << endl;

    cout << "rats address = " << &rats;
    cout << ", rodents address = " << &rodents << endl;

    int bunnies = 50;
    rodents = bunnies;

    cout << "bunnies = " << bunnies;
    cout << ", rats = " << rats;
    cout << ", rodents = " << rodents << endl;

    cout << "bunnies address = " << &bunnies;
    cout << ", rodents address = " << &rodents << endl;

    cin.get();
    cin.get();

    return 0;
}
View Code

    8.2.2 将引用用作函数参数

引用经常被用作函数参数,使得函数中的变量名成为调用程序中的变量的别名.这种传递参数的方法称为按引用传递.按引用传递允许被调用的函数能够访问调用函数中的变量.C++新增的这项特性是对C语言的超越,C语言只能按值传递,
按值传递导致被调用函数使用调用程序的值的拷贝.当然,C语言也允许避开按值传递的限制,采用按指针传递的方式

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

void swapr(int & a, int & b);
void swapp(int * p, int * q);
void swapv(int a, int b);

int main() {
    using namespace std;
    
    int wallet1 = 300;
    int wallet2 = 350;

    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Using references to swap contents:\n";
    swapr(wallet1, wallet2);
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Using pointers to swap contents again:\n";
    swapp(&wallet1, &wallet2);
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;

    cout << "Trying to use passing by value.\n";
    swapv(wallet1, wallet2);
    cout << "wallet1 = $" << wallet1;
    cout << " wallet2 = $" << wallet2 << endl;


    cin.get();
    cin.get();

    return 0;
}

void swapr(int & a, int & b) {
    int temp;
    temp = a;
    a = b;
    b = temp;
}

void swapp(int * p, int * q) {
    int temp;
    temp = *p;
    *p = *q;
    *q = temp;
}

void swapv(int a, int b) {
    int temp;
    temp = a;
    a = b;
    b = temp;
}
View Code

    8.2.3 引用的属性和特别之处

#include <iostream>

double cube(double a);
double refcube(double & ra);

int main() {
    using namespace std;

    double x = 3.0;

    cout << cube(x);
    cout << " = cube of " << x << endl;
    cout << refcube(x);
    cout << " = cube of " << x << endl;

    cin.get();
    cin.get();

    return 0;
}

double cube(double a) {
    a *= a * a;
    return a;
}

double refcube(double &ra) {
    ra *= ra * ra;
    return ra;
}
View Code

左值参数是可被引用的数据对象,例如,变量,数组元素,结构成员,引用和解除引用的指针都是左值.非左值包括字面常量(用引号括起的字符串除外,它们由其地址表示)和包含多项的表达式.
在C语言中,左值最初指的是可出现在赋值语句左边的实体,但这是引入关键字const之前的情况.现在,常规变量和const变量都可视为左值,因为可通过地址访问它们.但常规变量属于可修改的左值,而const变量属于不可修改的左值.
C++11新增了另一种引用----右值引用(rvalue reference).这种引用可指向右值,是使用&&声明的

    8.2.4 将引用用于结构

#include <iostream>
#include <string>

struct free_throws {
    std::string name;
    int made;
    int attempts;
    float percent;
};

void display(const free_throws & ft);
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws & target, const free_throws & source);

int main() {
    using namespace std;

    free_throws one = { "Ifelsa Branch", 13, 14 };
    free_throws two = { "Andor Knott", 10, 16 };
    free_throws three = { "Minnie Max", 7 , 9 };
    free_throws four = { "Whily Looper", 5, 9 };
    free_throws five = { "Long Long", 6, 14 };
    free_throws team = { "Throwgoods", 0 , 0 };

    free_throws dup;

    set_pc(one);
    display(one);

    accumulate(team, one);
    display(team);

    display(accumulate(team, two));
    accumulate(accumulate(team, three), four);
    display(team);

    dup = accumulate(team, five);
    std::cout << "Displaying team:\n";
    display(team);
    std::cout << "Displaying dup after assignment:\n";
    display(dup);
    set_pc(four);

    accumulate(dup, five) = four;
    std::cout << "Displaying dup after ill-advised assignment:\n";
    display(dup);

    cin.get();
    cin.get();

    return 0;
}

void display(const free_throws & ft) {
    using std::cout;
    cout << "Name: " << ft.name << '\n';
    cout << " Made: " << ft.made << '\t';
    cout << "Attempts: " << ft.attempts << '\t';
    cout << "Percent: " << ft.percent << '\n';
}

void set_pc(free_throws & ft) {
    if (ft.attempts != 0) {
        ft.percent = 100.0f * float(ft.made) / float(ft.attempts);
    } else {
        ft.percent = 0;
    }
}

free_throws & accumulate(free_throws & target, const free_throws & source) {
    target.attempts += source.attempts;
    target.made += source.made;
    set_pc(target);
    return target;
}
View Code

    8.2.5 将引用用于类对象

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <string>

using namespace std;

string version1(const string & s1, const string & s2);
const string & version2(string & s1, const string & s2);
const string & version3(string & s1, const string & s2);

int main() {
    string input;
    string copy;
    string result;

    cout << "Enter a string: ";
    getline(cin, input);
    copy = input;

    cout << "Your string as entered: " << input << endl;

    result = version1(input, "***");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;

    result = version2(input, "###");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;

    cout << "Resetting original string.\n";
    input = copy;
    result = version3(input, "@@@");
    cout << "Your string enhanced: " << result << endl;
    cout << "Your original string: " << input << endl;

    cin.get();
    cin.get();

    return 0;
}

string version1(const string & s1, const string & s2) {
    string temp;
    temp = s2 + s1 + s2;
    return temp;
}

const string & version2(string & s1, const string & s2) {
    s1 = s2 + s1 + s2;
    return s1;
}

const string & version3(string & s1, const string & s2) {
    string temp;

    temp = s2 + s1 + s2;
    return temp;
}
View Code

    8.2.6 对象,继承和引用

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;

void file_it(ostream & os, double fo, const double fe[], int n);
const int LIMIT = 5;


int main() {
    ofstream fout;
    const char * fn = "ep-data.txt";
    fout.open(fn);
    if (!fout.is_open()) {
        cout << "Can't open " << fn << ". Bye.\n";
        exit(EXIT_FAILURE);
    }
    double objective;
    cout << "Enter the focal length of your "
        << "telescope objective in mm: ";
    cin >> objective;
    double eps[LIMIT];
    cout << "Enter the focal lengths, in mm, of " << LIMIT
        << " eyepieces:\n";
    for (int i = 0; i < LIMIT; i++) {
        cout << "Eyepiece #" << i + 1 << ": ";
        cin >> eps[i];
    }
    file_it(fout, objective, eps, LIMIT);
    file_it(cout, objective, eps, LIMIT);
    cout << "Done\n";


    cin.get();
    cin.get();

    return 0;
}

void file_it(ostream & os, double fo, const double fe[], int n) {
    ios_base::fmtflags initial;
    initial = os.setf(ios_base::fixed);
    os.precision(0);
    os << "Focal length of objective: " << fo << "mm\n";
    os.setf(ios::showpoint);
    os.precision(1);
    os.width(12);
    os << "f.1. eyeprece";
    os.width(15);
    os << "magnification" << endl;
    for (int i = 0; i < n; i++) {
        os.width(12);
        os << fe[i];
        os.width(15);
        os << int(fo / fe[i] + 0.5) << endl;
    }
    os.setf(initial);
}
View Code

    8.2.7 何时使用引用参数

使用引用参数的主要原因有两个

  1.  程序员能够修改调用函数中的数据对象

  2.  通过传递引用而不是整个数据对象,可以提高程序的运行速度

对于使用传递的值而不作修改的函数

  1.  如果数据对象很小,如内置数据类型或小型结构,则按值传递

  2. 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针

  3. 如果数据对象是较大的结构,则使用const指针或const引用

  4. 如果数据对象是类对象,则使用const引用.类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因.因此,传递类对象参数的标准方式是按引用传递

对于修改调用函数中数据的函数

  1. 如果数据对象是数组,则只能使用指针

  2. 如果数据对象是结构,则使用引用或指针

  3. 如果数据对象是类对象,则使用引用

当然,这只是一些指导原则,很可能有充分的理由做出其他的选择

  8.3 默认参数

#include <iostream>

const int ArSize = 80;
char * left(const char * str, int n = 1);

int main() {
    using namespace std;

    char sample[ArSize];
    cout << "Enter a string:\n";
    cin.get(sample, ArSize);
    char *ps = left(sample, 4);
    cout << ps << endl;
    delete[] ps;
    ps = left(sample);
    cout << ps << endl;
    delete[] ps;


    cin.get();
    cin.get();

    return 0;
}


char * left(const char * str, int n) {
    if (n < 0) {
        n = 0;
    }

    char * p = new char[n + 1];

    int i;
    for (i = 0; i < n && str[i]; i++) {
        p[i] = str[i];
    }
    while (i <= n) {
        p[i++] = '\0';
    }

    return p;
}
View Code

  8.4 函数重载

术语"函数重载"指的是可以有多个同名的参数,因此对名称进行了重载.可以通过重载来设计一些列函数----它们完成相同的工作,但使用不同的参数列表
函数重载的关键是函数的参数列表----也称为函数特征标(function signature).如果两个函数的参数数目和类型相同,则它们的特征标相同,而变量名是无关紧要的.C++允许定义名称相同的函数
条件是它们的特征标不同.如果参数数目和/或参数类型不同,则特征标也不同.
编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标

    8.4.1 重载示例

#include <iostream>

unsigned long left(unsigned long num, unsigned ct);
char * left(const char * str, int n = 1);

int main() {
    using namespace std;

    const char * trip = "Hawaii!!";
    unsigned long n = 12345678;
    int i;
    char * temp;

    for (i = 1; i < 10; i++) {
        cout << left(n, i) << endl;
        temp = left(trip, i);
        cout << temp << endl;
        delete[] temp;
    }


    cin.get();
    cin.get();

    return 0;
}

unsigned long left(unsigned long num, unsigned ct) {
    unsigned digits = 1;
    unsigned long n = num;

    if (ct == 0 || num == 0) {
        return 0;
    }

    while (n /= 10) {
        digits++;
    }

    if (digits > ct) {
        ct = digits - ct;
        while (ct--) {
            num /= 10;
        }
        return num;
    } else {
        return num;
    }


}


char * left(const char * str, int n) {
    if (n < 0) {
        n = 0;
    }
    char * p = new char[n + 1];
    int i;
    for (i = 0; i < n && str[i]; i++) {
        p[i] = str[i];
    }
    while (i <= n) {
        p[i++] = '\0';
    }
    return p;
}
View Code

    8.4.2 何时使用函数重载

  8.5 函数模板

现在的C++编译器实现了C++新增的一项特性----函数模板.函数模板是通用的函数描述,也就是说,它们使用泛型来定义函数,其中的泛型可用具体的类型(如int或double)替换.通过将类型作为参数传递给模板,可使编译器生成该类型的函数.
由于模板允许以泛型(而不是具体类型)的方式编写程序,因此有时也被称为通用编程.由于类型是用参数表示的,因此模板特性有时也被称为参数化类型(parameterized types).

#include <iostream>

template <typename T> void Swap(T & a, T &b);

int main() {
    using namespace std;

    int i = 10;
    int j = 20;
    cout << "i, j = " << i << ", " << j << ".\n";
    cout << "Using compiler-generated int swapper:\n";
    Swap(i, j);
    cout << "Now i, j = " << i << ", " << j << ".\n";

    double x = 24.5;
    double y = 81.7;
    cout << "x, y = " << x << ", " << y << ".\n";
    Swap(x, y);
    cout << "Now x,y = " << x << ", " << y << ".\n";

    cin.get();
    cin.get();

    return 0;
}

template <typename T> void Swap(T & a, T & b) {
    T temp;
    temp = a;
    a = b;
    b = temp;
}
View Code

    8.5.1 重载的模板

#include <iostream>

template <typename T> void Swap(T & a, T & b);
template <typename T> void Swap(T * a, T * b, int n);

void Show(int a[]);
const int Lim = 8;

int main() {
    using namespace std;

    int i = 10, j = 20;
    cout << "i, j = " << i << ", " << j << ".\n";
    cout << "Using compiler-generated int swapper:\n";
    Swap(i, j);
    cout << "Now i, j = " << i << ", " << j << ".\n";

    int d1[Lim] = { 0, 7, 0, 4, 1, 7, 7, 6 };
    int d2[Lim] = { 0, 7, 2, 0, 1, 9, 6, 9 };
    cout << "Original arrays:\n";
    Show(d1);
    Show(d2);
    Swap(d1, d2, Lim);
    cout << "Swapped arrays:\n";
    Show(d1);
    Show(d2);

    cin.get();
    cin.get();

    return 0;
}

template <typename T> void Swap(T & a, T & b) {
    T temp;
    temp = a;
    a = b;
    b = temp;
}

template <typename T> void Swap(T a[], T b[], int n) {
    T temp;
    for (int i = 0; i < n; i++) {
        temp = a[i];
        a[i] = b[i];
        b[i] = temp;
    }
}


void Show(int a[]) {
    using namespace std;
    cout << a[0] << a[1] << "/";
    cout << a[2] << a[3] << "/";
    for (int i = 4; i < Lim; i++) {
        cout << a[i];
    }
    cout << endl;
}
View Code

    8.5.2 模板的局限性

    8.5.3 显示具体化

#include <iostream>

template <typename T> void Swap(T & a, T & b);

struct job {
    char name[40];
    double salary;
    int floor;
};

template<> void Swap<job>(job & j1, job & j2);

void Show(job & j);

int main() {
    using namespace std;

    cout.precision(2);
    cout.setf(ios::fixed, ios::floatfield);
    int i = 10, j = 20;
    cout << "i, j= " << i << ", " << j << ".\n";
    cout << "Using compiler-generated int swapper:\n";
    Swap(i, j);
    cout << "Now i, j  = " << i << ", " << j << ".\n";

    job sue = { "Susan Yaffe", 73000.60, 7 };
    job sidney = { "Sidney Taffe", 78060.72, 9 };
    cout << "Before job swapping:\n";
    Show(sue);
    Show(sidney);
    Swap(sue, sidney);
    cout << "After job swapping:\n";
    Show(sue);
    Show(sidney);

    cin.get();
    cin.get();

    return 0;
}

template <typename T> void Swap(T & a, T & b) {
    T temp;
    temp = a;
    a = b;
    b = temp;
}

template<> void Swap<job>(job & j1, job & j2) {
    double t1;
    int t2;

    t1 = j1.salary;
    j1.salary = j2.salary;
    j2.salary = t1;
    t2 = j1.floor;
    j1.floor = j2.floor;
    j2.floor = t2;
}

void Show(job & j) {
    using namespace std;
    cout << j.name << ": $" << j.salary
        << " on floor " << j.floor << endl;
}
View Code

    8.5.4 实例化和具体化

    8.5.5 编译器选择使用哪个函数版本

    8.5.6 模板函数的发展

  8.6 总结

  8.7 复习题

  8.8 编程练习

第9章 内存模型和名称空间

  9.1 单独编译

#ifndef COORDIN_H_
#define COORDIN_H_

struct polar {
    double distance;
    double angle;
};

struct rect {
    double x;
    double y;
};

polar rect_to_polar(rect xypos);
void show_polar(polar dapos);

#endif

#include <iostream>
#include "coordin.h"

using namespace std;

int main() {
    rect rplace;
    polar pplace;

    cout << "Enter the x and y values: ";
    while (cin >> rplace.x >> rplace.y) {
        pplace = rect_to_polar(rplace);
        show_polar(pplace);
        cout << "Next two numbers (q to quit): ";
    }
    cout << "Bye!\n";

    cin.get();
    cin.get();

    return 0;
}

#include <iostream>
#include <cmath>
#include "coordin.h"

polar rect_to_polar(rect xypos) {
    using namespace std;
    polar answer;

    answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y);
    answer.angle = atan2(xypos.y, xypos.x);
    return answer;
}

void show_polar(polar dapos) {
    using namespace std;

    const double Rad_to_deg = 57.29577951;

    cout << "distance = " << dapos.distance;
    cout << ", angle = " << dapos.angle * Rad_to_deg;
    cout << " degrees\n";
}
View Code

  9.2 存储连续性,作用域和链接性

C++使用三种(在C++11中是四种)不同的方案来存储数据,这些方案的区别就在于数据保留在内存中的时间

  1. 自动存储持续性:在函数定义中声明的变量(包括函数参数)的存储性为自动的.它们在程序开始执行其所属的函数或代码块时被创建,在执行完函数或代码块时,它们使用的内存被释放.C++有两种存储持续性为自动的变量

  2. 静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态.它们在程序整个运行过程中都存在.C++有3种存储持续性为静态的变量

  3. 线程存储持续性(C++11):当前,多核处理器很常见,这些CPU可同时处理多个执行任务.这让程序能够将计算放在可并行处理的不同线程中.如果变量是使用关键字thread_local声明的,则其生命周期与所属的线程一样长.

  4. 动态存储连续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止.这种内存的存储持续性为动态,有时被称为自由存储(free store)或堆(heap)

    9.2.1 作用域和链接

作用域(scope)描述了名称在文件(翻译单元)的多大范围内可见.链接性(linkage)描述了名称如何在不同单元间共享.链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件中的函数共享.自动变量的名称没有链接性,因为它们不能共享

C++变量的作用域有多种.作用域为局部的变量只在定义它的代码块中可用.代码块是由花括号括起的一系列语句.作用域为全局(也叫文件作用域)的变量在定义位置到文件结尾之间都可用.自动变量的作用域为局部,静态变量的作用域是全局还是局部取决于它是如何被定义的

在函数原型作用域(function prototype scope)中使用的名称只在包含参数列表的括号内可用(这就是为什么这些名称是什么以及是否出现都不重要的原因).在类中声明的成员的作用域为整个类.在名称空间中声明的变量的作用域为整个名称空间(由于名称空间已经引入

C++语言中,因此全局作用域是名称空间作用域的特例)

不同的C++存储方式是通过存储持续性,作用域和链接性来描述的

    9.2.2 自动存储持续性

在默认情况下,在函数中声明的函数参数和变量的存储持续性为自动,作用域为局部,没有链接性

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

void oil(int x);

int main() {
    using namespace std;

    int texas = 31;
    int year = 2011;

    cout << "In main(), texas = " << texas << ", &texas = ";
    cout << &texas << endl;
    cout << "In main(), year = " << year << ", &year = ";
    cout << &year << endl;
    oil(texas);
    cout << "In main(), texas = " << texas << ", &texas = ";
    cout << &texas << endl;
    cout << "In main(), year = " << year << ", &year = ";
    cout << &year << endl;

    cin.get();
    cin.get();

    return 0;
}

void oil(int x) {
    using namespace std;
    int texas = 5;

    cout << "In oil(), texas = " << texas << ", &texas = ";
    cout << &texas << endl;
    cout << "In oil(), x = " << x << ", &x = ";
    cout << &x << endl;

    {
        int texas = 113;
        cout << "In block, texas = " << texas;
        cout << ", &texas = " << &texas << endl;
        cout << "In block, x = " << x << ", &x = ";
        cout << &x << endl;
    }

    cout << "Post-block texas = " << texas;
    cout << ", &texas = " << &texas << endl;
}
View Code

由于自动变量的数目随函数的开始和结束而增减,因此程序必须在运行时对自动变量进行管理.常用的方法是留出一段内存,并将其视为栈,以管理变量的增减

    9.2.3 静态持续变量

和C语言一样,C++也为静态存储持续性变量提供了3中链接性:外部链接性(可在其他文件中访问),内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块中访问).这3种链接性都在整个程序执行期间存在,与自动变量相比,它们的寿命更长.

由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理它们.编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存在.另外,如果没有显式地初始化静态变量,编译器将把它设置为0,

在默认情况下,静态数组和结构将每个元素或成员的所有位都设置为0

想要创建链接性为外部的静态持续变量,必须在代码块的外面声明它;要创建链接性为内部的静态持续变量,必须在代码块的外面声明它,并使用static限定符,要创建没有链接性的静态持续变量,必须在代码块内声明它,并使用statci限定符

int gobal = 1000;    // static duration,external linkage
static int one_file = 50;    // static duration,internal linkage
int main() {

}
void funct1(int n) {
    static int count = 0;    // static duration, on linkage
}
View Code

 表9.1指出了关键字static的两种方法,但含义有些不同:用于局部声明,以指出变量是无链接性的静态变量时,static表示的是存储连续性;而用于代码块外的声明时,static表示内部链接性,而变量已经是静态连续性.有人称之为关键字重载即关键字的含义取决于上下文

    9.2.4 静态持续性,外部链接性

链接性为外部的变量通常简称为外部变量,它们的存储持续性为静态,作用域为整个文件.外部变量是在函数外部定义的,因此对所有函数而言都是外部的.可以在文件种位于外部变量后面的任何函数种使用它,因此外部变量也称全局变量(相对于局部的自动变量)

一方面,在每个使用外部变量的文件中,都必须声明它;另一方面,C++有"单定义规则"(One Defiition Rule,ODR),该规则指出,变量只能有一次定义.为满足这种需求,C++提供了两种变量声明.一种是定义声明(defining declaration)或简称定义(definition),它给变量分配存储空间;

另一种是引用声明(referencing declaration)或简称为声明(declaration),它不给变量分配存储空间,因为它引用已有的变量.

引用声明使用关键字extern,且不进行初始化;否则,声明为定义,导致分配存储空间

double up;    // definition, up is 0
extern int blem;    // blem defined elsewhere
extern char gr = 'z';    // definition because initialized

// file01.cpp
extern int cats = 20;    // definition because of initialization
int dogs = 22;    // also a definition
int fleas;    // also a definition

// file02.cpp
extern int cats;    // not definition because they use
extern int dogs;    // extern and have no initialization

// file98.cpp
// use cats, dogs, and fleas from file01.cpp
extern int cats;
extern int dogs;
exterrn int fleas;
View Code
#include <iostream>

extern double warming;    // use warming from another file

void update(double dt);
void local();

using std::cout;
void update(double dt) {    // modifies global variable
    extern double warming;    // optional redeclaration
    warming += dt;

    cout << "Updating global warming to " << warming;
    cout << " degrees.\n";
}

void local() {
    double warming = 0.8;    // new variable hides external one

    cout << "Local warming = " << warming << " degrees.\n";
    // access global variable with the scope resolution operator
    cout << "But global warming = " << ::warming;
    cout << " degrees.\n";

}

#include <iostream>

using namespace std;

double warming = 0.3;

void update(double dt);
void local();

int main() {
    cout << "Global warming is " << warming << " degress.\n";
    update(0.1);    // call function to change warming
    cout << "Global warming is " << warming << " degrees.\n";
    local();    // call function with local warming
    cout << "Global warming is " << warming << " degrees.\n";

    cin.get();
    cin.get();

    return 0;
}
View Code

    9.2.5 静态持续性,内部链接性

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

int tom = 3;
int dick = 30;
static int harry = 300;

void remove_access();

int main() {
    using namespace std;

    cout << "main() reports the following addresses:\n";
    cout << &tom << " = &tom, " << &dick << " = &dick, ";
    cout << &harry << " = &harry\n";
    remove_access();


    cin.get();
    cin.get();

    return 0;
}

#include <iostream>

extern int tom;
static int dick = 10;
int harry = 200;

void remote_access() {
    using namespace std;

    cout << "remote_access() reports the following addresses:\n";
    cout << &tom << " = &tom, " << &dick << " = &dick, ";
    cout << &harry << " = &harry\n";
}
View Code

    9.2.6 静态存储持续性,无链接性

#define _CRT_SECURE_NO_WARNINGS

#include <iostream>

const int ArSize = 10;

void strcount(const char * str);

int main() {
    using namespace std;

    char input[ArSize];
    char next;

    cout << "Enter a line:\n";
    cin.get(input, ArSize);

    while (cin) {
        cin.get(next);
        while (next != '\n') {
            cin.get(next);
        }
        strcount(input);
        cout << "Enter next line (empty line to quit):\n";
        cin.get(input, ArSize);
    }

    cout << "Bye\n";

    cin.get();
    cin.get();

    return 0;
}

void strcount(const char * str) {
    using namespace std;
    static int total = 0;
    int count = 0;

    cout << "\"" << str << "\" contains ";
    while (*str++) {
        count++;
    }
    total += count;
    cout << count << " characters\n";
    cout << total << " characters total\n";
}
View Code

    9.2.7 说明符和限定符

    9.2.8 函数和链接性

    9.2.9 语言链接性

extern "C" void spiff(int);    // use C protocol for name look-up
extern void spoff(int);    // use C++ protocol for name look-up
extern "C++" void spaff(int);    // use C++ protocol for name look-up

    9.2.10 存储方案和动态分配

前面介绍C++用来为变量(包括数组和结构)分配内存的5种方案(线程内存除外),它们不适用于使用C++运算符new(或C函数malloc())分配的内存,这种内存被称为动态内存.动态内存由运算符new和delete控制,而不是由作用域和链接性规则控制.因此,可以在一个函数种分配动态内存,而在另一个函数中将其释放.与自动内存不同,动态内存不是LIFO,其分配和释放顺序要取决于new和delete在何时以何种方式被使用.通常,编译器使用三块独立的内存:一块用于静态变量(可能再细分),一块用于自动变量,另外i一块用于动态存储

虽然存储方案概念不适用于动态内存,但适用于用来跟踪动态内存的自动和静态指针变量.

在程序结束时,由new分配的内存通常都将被释放,不过情况也并不总是这样.例如,在不那么健壮的操作系统中,在某些情况下,请求大型内存块将导致该代码块在程序结束不会被自动释放.最佳的做法是,使用delete来释放new分配的内存

int * pi = new int(6);    // *pi set to 6
double * pd = new double(99.99);    // *pd set to 99.99
struct where {double x; double y; double z;}
where * one = new where{2.5,5.3,7.2};
int * ar = new int [4] { 2,4,6,7 };
double * pdo = new double { 99.99 };    // *pd set to 99.99
void * operator new(std::size_t);    // used by new
void * operator new[](std::size_t);    // used by new[]
void operator delete(void *);
void operator delete[](void *);
int * pi = new int;
int * pi = new(sizeof(int));
int * pa = new int[40];
int * pa = new(40 * sizeof(int));
delete pi;
delete (pi);
View Code

通常,new负责在堆(heap)中找到一个足以能够满足要求的内存块.new运算符还有另一种变体,被称为定位(palcement)new运算符,它让您能够指定要使用的位置.程序员可能使用这种特性来设置其内存管理规程,处理需要通过特定地址进行访问的硬件或在特定位置创建对象

#include <new>
struct chaff {
    char dross[20];
    int slag;
};
char buffer[50];
char buffer2[500];

int main() {
    chaff * p1, * p2;
    int * p3, * p4;
// first, the regular forms of new
    p1 = new chaff;    // place structure in heap
    p3 = new int[2];    // place int array in heap
// now, the two forms of placement new
    p2 = new (buffer1) chaff;    // place structure in buffer1
    p4 = new (buffer2) int[2];    // place int array in buffer2
View Code
#define _CRT_SECURE_NO_WARNINGS

#include <iostream>
#include <new>

const int BUF = 512;
const int N = 5;
char buffer[BUF];


int main() {
    using namespace std;
    double * pd1, * pd2;
    int i;
    cout << "Calling new and placement new:\n";
    pd1 = new double[N];    // use heap
    pd2 = new (buffer) double[N];    // use buffer array

    for (i = 0; i < N; i++) {
        pd2[i] = pd1[i] = 1000 + 20.0 * i;
    }

    cout << "Memory address:\n" << " heap: " << pd1
        << " static: " << (void *)buffer << endl;

    cout << "Memory contents:\n";

    for (i = 0; i < N; i++) {
        cout << pd1[i] << " at " << &pd1[i] << "; ";
        cout << pd2[i] << " at " << &pd2[i] << endl;
    }

    cout << "\nCalling new and placement new a second time:\n";
    double * pd3, * pd4;
    pd3 = new double[N];    // find new address
    pd4 = new (buffer) double[N];    // overwrite old data
    for (i = 0; i < N; i++) {
        pd4[i] = pd3[i] = 1000 + 40.0 * i;
    }
    cout << "Memory contents:\n";
    for (i = 0; i < N; i++) {
        cout << pd3[i] << " at " << &pd3[i] << "; ";
        cout << pd4[i] << " at " << &pd4[i] << endl;
    }
    cout << "\nCalling new and placement new a third time:\n";
    delete[] pd1;
    pd1 = new double[N];
    pd2 = new (buffer + N * sizeof(double)) double[N];
    for (i = 0; i < N; i++) {
        pd2[i] = pd1[i] = 1000 + 60.0 * i;
    }
    cout << "Memory contents:\n";
    for (i = 0; i < N; i++) {
        cout << pd1[i] << " at " << &pd1[i] << "; ";
        cout << pd2[i] << " at " << &pd2[i] << endl;
    }
    delete[] pd1;
    delete[] pd3;

    cin.get();
    cin.get();

    return 0;
}
View Code

  9.3 名称空间

    9.3.1 传统的C++名称空间

    9.3.2 新的名称空间特性

    9.3.3 名称空间示例

// namesp.h
#include <string>

namespace pers {
    struct Person {
        std::string fname;
        std::string lname;
    };

    void getPerson(Person &);
    void showPerson(const Person &);
}

namespace debts {
    using namespace pers;
    struct Debt {
        Person name;
        double amount;
    };
    void getDebt(Debt &);
    void showDebt(const Debt &);
    double sumDebts(const Debt ar[], int n);
}

// namesp.cpp
#include <iostream>
#include "namesp.h"

namespace pers {
    using std::cout;
    using std::cin;
    void getPerson(Person & rp) {
        cout << "Enter first name: ";
        cin >> rp.fname;
        cout << "Enter last name: ";
        cin >> rp.lname;
    }
    void showPerson(const Person & rp) {
        std::cout << rp.lname << ", " << rp.fname;
    }
}

namespace debts {
    void getDebt(Debt & rd) {
        getPerson(rd.name);
        std::cout << "Enter debt: ";
        std::cin >> rd.amount;
    }
    void showDebt(const Debt & rd) {
        showPerson(rd.name);
        std::cout << ": $" << rd.amount << std::endl;
    }
    double sumDebts(const Debt ar[], int n) {
        double total = 0;
        for (int i = 0; i < n; i++) {
            total += ar[i].amount;
        }
        return total;
    }
}

// usenmsp.cpp
#include <iostream>
#include "namesp.h"

void other(void);
void another(void);

int main(void) {
    using debts::Debt;
    using debts::showDebt;

    Debt golf = { { "Benny", "Goatsniff" }, 120.0 };
    showDebt(golf);
    other();
    another();

    return 0;
}

void other(void) {
    using std::cout;
    using std::endl;
    using namespace debts;
    Person dg = { "Doodles", "Glister" };
    showPerson(dg);
    cout << endl;
    Debt zippy[3];
    int i;
    for (i = 0; i < 3; i++) {
        getDebt(zippy[i]);
    }
    for (i = 0; i < 3; i++) {
        showDebt(zippy[i]);
    }
    cout << "Total debt: $" << sumDebts(zippy, 3) << endl;

    return;
}

void another(void) {
    using pers::Person;
    Person collector = { "Milo", "Rightshift" };
    pers::showPerson(collector);
    std::cout << std::endl;
}
View Code

    9.3.4 名称空间及其前途

  9.4 总结

  9.5 复习题

  9.6 编程练习

第10章 对象和类

  10.1 过程性编程和面向对象编程

  10.2 抽象和类

    10.2.1 类型是什么

    10.2.2 C++中的类

#ifndef STOCK00_H_
#define STOCK00_H_

#include <string>

class Stock {
private:
    std::string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot() { total_val = shares * share_val; }
public:
    void accuire(const std::string & co, long n, double pr);
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
    void show();
};

#endif
View Code

类描述看上去很像是包含成员函数以及public和private可见性标签的结构声明.实际上,C++对结构进行了扩展,使之具有与类相同的特性.它们之间唯一的区别是,结构的默认访问类型是public,而类为private.C++程序员通常使用类来实现类描述,而把结构限制为只表示纯粹的数据对象(常被称为普通老师数据(POD,Plain Old Data)结构)

    10.2.3 实现类成员函数

#include <iostream>
#include "stock00.h"

void Stock::accquire(const std::string & co, long n, double pr) {
    company = co;
    if (n < 0) {
        std::cout << "Number of shares can't be negative; "
            << company << " shares set to 0.\n";
        shares = 0;
    } else {
        shares = n;
    }
    share_val = pr;
    set_tot();
}

void Stock::buy(long num, double price) {
    if (num < 0) {
        std::cout << "Number of shares purchased can't be negative."
            << "Transaction is aborted.\n";
    } else {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price) {
    using std::cout;
    if (num < 0) {
        cout << "Number of shares sold can't be negative."
            << "Transaction is aborted.\n";
    } else if (num > shares) {
        cout << "You can't sell more than you have! "
            << "Transaction is aborted.\n";
    } else {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::show() {
    std::cout << "Company: " << company
        << " Shares: " << shares << '\n'
        << " Share Price: $" << share_val
        << " Total Worth: $" << total_val << '\n';
}
View Code

    10.2.4 使用类   

#include <iostream>
#include "stock00.h"


int main() {
    Stock fluffy_the_cat;
    fluffy_the_cat.acquire("NanoSmart", 20, 12.50);
    fluffy_the_cat.show();
    fluffy_the_cat.buy(15, 18.125);
    fluffy_the_cat.show();
    fluffy_the_cat.sell(400, 20.00);
    fluffy_the_cat.show();
    fluffy_the_cat.buy(300000, 40.125);
    fluffy_the_cat.show();
    fluffy_the_cat.sell(300000, 0.125);
    fluffy_the_cat.show();

    return 0;
}
View Code

    10.2.5 修改实现

    10.2.6 小结

  10.3 类的构造函数和析构函数

    10.3.1 声明和定义构造函数

    10.3.2 使用构造函数

    10.3.3 默认构造函数

    10.3.4 析构函数

    10.3.5 改进Stock类

#ifndef STOCK10_H_
#define STOCK10_H_

#include <string>

class Stock {
private:
    std::string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot() {
        total_val = shares * share_val;
    }
public:
    Stock();
    Stock(const std::string & co, long n = 0, double pr = 0.0);
    ~Stock();
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
    void show();
};

#endif

#include <iostream>
#include "stock10.h"

Stock::Stock() {
    std::cout << "Default constructor called\n";
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

Stock::Stock(const std::string & co, long n, double pr) {
    std::cout << "Constructor using " << co << " called\n";
    company = co;

    if (n < 0) {
        std::cout << "Number of shares can't be negative; "
            << company << " shares set to 0.\n";
        shares = 0;
    } else {
        shares = n;
    }

    share_val = pr;
    set_tot();
}

Stock::~Stock() {
    std::cout << "Bye, " << company << "!\n";
}

void Stock::buy(long num, double price) {
    if (num < 0) {
        std::cout << "Number of shares purchased can't be negative. "
            << "Transaction is aborted.\n";
    } else {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price) {
    using std::cout;
    if (num < 0) {
        cout << "Number of shares sold can't be negative. "
            << "Transaction is aborted.\n";
    } else if (num > shares) {
        cout << "You can't sell more than you have! "
            << "Transaction is aborted.\n";
    } else {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price) {
    share_val = price;
    set_tot();
}

void Stock::show() {
    using std::cout;
    using std::ios_base;

    ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
    std::streamsize prec = cout.precision(3);

    cout << "Company: " << company
        << " Shares: " << shares << '\n';
    cout << " Share Price: $" << share_val;

    cout.precision(2);
    cout << " Total Worth: $" << total_val << '\n';

    cout.setf(orig, ios_base::floatfield);
    cout.precision(prec);
}

#include <iostream>
#include "stock10.h"


int main() {
    {
        using std::cout;
        cout << "Using constructors to create new objects\n";
        Stock stock1("NanoSmart", 12, 20.0);
        stock1.show();
        Stock stock2 = Stock("Boffo Objects", 2, 2.0);
        stock2.show();

        cout << "Assigning stock1 to stock2:\n";
        stock2 = stock1;
        cout << "Listing stock1 and stock2:\n";
        stock1.show();
        stock2.show();

        cout << "Using a constructor to reset an object\n";
        stock1 = Stock("Nifty Foods", 10, 50.0);
        cout << "Revised stock1:\n";
        stock1.show();
        cout << "Done\n";


    }

    std::cin.get();
    std::cin.get();

    return 0;
}
View Code

    10.3.6 构造函数和析构函数小结

  10.4 this指针

#ifndef STOCK20_H_
#define STOCK20_H_

#include <string>

class Stock {
private:
    std::string company;
    int shares;
    double share_val;
    double total_val;
    void set_tot() { total_val = shares * share_val; }
public:
    Stock();
    Stock(const std::string & co, long n = 0, double pr = 0.0);
    ~Stock();
    void buy(long num, double price);
    void sell(long num, double price);
    void update(double price);
    void show() const;
    const Stock & topval(const Stock & s) const;
};

#endif

#include <iostream>
#include "stock20.h"

Stock::Stock() {
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

Stock::Stock(const std::string & co, long n, double pr) {
    company = co;
    if (n < 0) {
        std::cout << "Number of shares can't be negative; "
            << company << " shares set to 0.\n";
        shares = 0;
    } else {
        shares = n;
    }
    share_val = pr;
    set_tot();
}

Stock::~Stock() {

}

void Stock::buy(long num, double price) {
    if (num < 0) {
        std::cout << "Number of shares purchased can't be negative. "
            << "Transaction is aborted.\n";
    } else {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price) {
    using std::cout;
    if (num < 0) {
        cout << "Number of shares sold can't be negative. "
            << "Transaction is aborted.\n";
    } else if (num > shares) {
        cout << "You can't sell more than you have! "
            << "Transaction is aborted.\n";
    } else {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price) {
    share_val = price;
    set_tot();
}

void Stock::show() const {
    using std::cout;
    using std::ios_base;
    ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);
    std::streamsize prec = cout.precision(3);

    cout << "Company: " << company
        << " Shares: " << shares << '\n';
    cout << " Share Price: $" << share_val;

    cout.precision(2);
    cout << " Total Worth: $" << total_val << '\n';

    cout.setf(orig, ios_base::floatfield);
    cout.precision(prec);
}

const Stock & Stock::topval(const Stock & s) const {
    if (s.total_val > total_val) {
        return s;
    } else {
        return *this;
    }
}
View Code

  10.5 对象数组

  10.6 类作用域

    10.6.1 作用域为类的常量

class Bakery {
private:
    enum {Months = 12};
    double costs[Months];
...

class Bakery {
private:
    static const int Months = 12;
    double costs[Months];
...
View Code

    10.6.2 作用域内枚举(C++11)

  10.7 抽象数据类型

#ifndef STACK_H_
#define STACK_H_

typedef unsigned long Item;

class Stack {
private:
    enum { MAX = 10 };
    Item items[MAX];
    int top;
public:
    Stack();
    bool isempty() const;
    bool isfull() const;

    bool push(const Item & item);
    bool pop(Item & item);
};

#endif

#include "stack.h"

Stack::Stack() {
    top = 0;
}

bool Stack::isempty() const {
    return top == 0;
}

bool Stack::isfull() const {
    return top == MAX;
}

bool Stack::push(const Item & item) {
    if (top < MAX) {
        items[top++] = item;
        return true;
    } else {
        return false;
    }
}

bool Stack::pop(Item & item) {
    if (top > 0) {
        item = items[--top];
        return true;
    } else {
        return false;
    }
}

#include <iostream>
#include <cctype>
#include "stack.h"

int main() {
    using namespace std;
    Stack st;
    char ch;
    unsigned long po;
    cout << "Please enter A to add a purchase order,\n"
        << "P to process a PO, or Q to quit.\n";
    while (cin >> ch && toupper(ch) != 'Q') {
        while (cin.get() != '\n') {
            continue;
        }
        if (!isalpha(ch)) {
            cout << '\a';
            continue;
        }
        switch (ch) {
        case 'A':
        case 'a':
            cout << "Enter a PO number to add: ";
            cin >> po;
            if (st.isfull()) {
                cout << "stack already full\n";
            } else {
                st.push(po);
            }
            break;
        case 'P':
        case 'p':
            if (st.isempty()) {
                cout << "stack already empty\n";
            } else {
                st.pop(po);
                cout << "PO #" << po << " popped\n";
            }
            break;
        }
        cout << "Please enter A to add a purchase order,\n"
            << "P to process a PO, or Q to quit.\n";
    }
    cout << "Bye\n";

    return 0;
}
View Code

  10.8 总结

  10.9 复习题

  10.10 编程练习

第11章 使用类

  11.1 运算符重载

  11.2 计算时间:一个运算符重载示例

    11.2.1 添加加法运算符

// mytime0.h
#ifndef MYTIME0_H_
#define MYTIME0_H_

class Time {
private:
    int hours;
    int minutes;
public:
    Time();
    Time(int h, int m = 0);
    void AddMin(int m);
    void AddHr(int h);
    void Reset(int h = 0, int m = 0);
    Time Sum(const Time & t) const;
    void Show() const;
};

#endif

// mytime0.cpp
#include <iostream>
#include "mytime0.h"

Time::Time() {
    hours = minutes = 0;
}

Time::Time(int h, int m) {
    hours = h;
    minutes = m;
}

void Time::AddMin(int m) {
    minutes += m;
    hours += minutes / 60;
    minutes %= 60;
}

void Time::AddHr(int h) {
    hours += h;
}

void Time::Reset(int h, int m) {
    hours = h;
    minutes = m;
}

Time Time::Sum(const Time & t) const {
    Time sum;
    sum.minutes = minutes + t.minutes;
    sum.hours = hours + t.hours + sum.minutes / 60;
    sum.minutes %= 60;
    return sum;
}

#include <iostream>
#include "mytime0.h"

int main() {
    using std::cout;
    using std::endl;

    Time palnning;
    Time coding(2, 40);
    Time fixing(5, 55);
    Time total;

    cout << "planning time = ";
    palnning.Show();
    cout << endl;

    cout << "coding time = ";
    coding.Show();
    cout << endl;

    cout << "fixing time = ";
    fixing.Show();
    cout << endl;

    total = coding.Sum(fixing);
    cout << "coding.Sum(fixing) = ";
    total.Show();
    cout << endl;

    return 0;
}

// mytime1.h
#ifndef MYTIME0_H_
#define MYTIME0_H_

class Time {
private:
    int hours;
    int minutes;
public:
    Time();
    Time(int h, int m = 0);
    void AddMin(int m);
    void AddHr(int h);
    void Reset(int h = 0, int m = 0);
    Time operator+(const Time & t) const;
    void Show() const;
};

#endif

// mytime1.cpp
#include <iostream>
#include "mytime1.h"

Time::Time() {
    hours = minutes = 0;
}

Time::Time(int h, int m) {
    hours = h;
    minutes = m;
}

void Time::AddMin(int m) {
    minutes += m;
    hours += minutes / 60;
    minutes %= 60;
}

void Time::AddHr(int h) {
    hours += h;
}

void Time::Reset(int h, int m) {
    hours = h;
    minutes = m;
}

Time Time::operator+(const Time & t) const {
    Time sum;
    sum.minutes = minutes + t.minutes;
    sum.hours = hours + t.hours + sum.minutes / 60;
    sum.minutes %= 60;
    return sum;
}

void Time::Show() const {
    std::cout << hours << " hours, " << minutes << " minutes";
}

#include <iostream>
#include "mytime1.h"

int main() {
    using std::cout;
    using std::endl;
    Time planning;
    Time coding(2, 40);
    Time fixing(5, 55);
    Time total;

    cout << "planning time = ";
    planning.Show();
    cout << endl;

    cout << "coding time = ";
    coding.Show();
    cout << endl;

    cout << "fixing time = ";
    fixing.Show();
    cout << endl;

    total = coding + fixing;
    cout << "coding + fixing = ";
    total.Show();
    cout << endl;

    Time morefixing(3, 28);
    cout << "more fixing time = ";
    morefixing.Show();
    cout << endl;
    total = morefixing.operator+(total);
    cout << "morefixing.operator+(total) = ";
    total.Show();
    cout << endl;

    return 0;
}
View Code

    11.2.2 重载限制

1. 重载后的运算符必须至少有一个操作数是用户定义的类型,这将防止用户为标准类型重载运算符.

2. 使用运算符时不能违反运算符原来的句法规则,同样,不能修改运算符的优先级.

3. 不能创建新运算符

4. 不能重载下面的运算符

  sizeof: sizeof运算符

  .: 成员运算符

  .*: 成员指针运算符

  ::: 作用域解析运算符

  ?:: 条件运算符

  typeid: 一个RTTI运算符

  const_cast: 强制类型转换运算符

  dynamic_cast: 强制类型转换运算符

  reinterpret_cast: 强制类型转换运算符

  static_cast: 强制类型转换运算符

    11.2.3 其他重载运算符

  11.3 友元

友元有3种:

  1. 友元函数

  2. 友元类

  3. 友元成员函数

    11.3.1 创建友元

友元是否有悖于OOP

乍一看,您可能会认为友元违反了OOP数据隐藏的原则,因为友元机制允许非成员函数访问私有数据.然而,这个观点太片面了.相反,应将友元函数看作类的扩展接口的组成部分.例如,从概念上看,double乘以Time和Time乘以double是完全相同的.也就是说,前一个要求有友元函数,后一个使用成员函数,这是C++句法的结果,而不是概念上的差别.通过使用友元函数和类方法,可以用同一个用户接口表达这两种操作.另外请记住,只有类声明可以决定哪一个函数是友元,因此类声明仍然控制了哪些函数可以访问私有数据.总之,类方法和友元只是表达类接口的两种不同机制

    11.3.2 常用的友元:重载<<运算符

#ifndef MYTIME3_H_
#define MYTIME3_H_

#include <iostream>

class Time {
private:
    int hours;
    int minutes;
public:
    Time();
    Time(int h, int w = 0);
    void AddMin(int m);
    void AddHr(int h);
    void Reset(int h = 0, int m = 0);
    Time operator+(const Time & t) const;
    Time operator-(const Time & t) const;
    Time operator*(double n) const;
    friend Time operator*(double m, const Time & t) {
        return t * m;
    }
    friend std::ostream & operator<<(std::ostream & os, const Time & t);
};

#endif

#include "mytime3.h"

Time::Time() {
    hours = minutes = 0;
}

Time::Time(int h, int m) {
    hours = h;
    minutes = m;
}

void Time::AddMin(int m) {
    minutes += m;
    hours += minutes / 60;
    minutes %= 60;
}

void Time::AddHr(int h) {
    hours += h;
}

void Time::Reset(int h, int m) {
    hours = h;
    minutes = m;
}

Time Time::operator+(const Time & t) const {
    Time sum;
    sum.minutes = minutes + t.minutes;
    sum.hours = hours + t.hours + sum.minutes / 60;
    sum.minutes %= 60;
    return sum;
}

Time Time::operator-(const Time & t) const {
    Time diff;
    int tot1, tot2;
    tot1 = t.minutes + 60 * t.hours;
    tot2 = minutes + 60 * hours;
    diff.minutes = (tot2 - tot1) % 60;
    diff.hours = (tot2 - tot1) / 60;
    return diff;
}

Time Time::operator*(double mult) const {
    Time result;
    long totalminutes = hours * mult * 60 + minutes * mult;
    result.hours = totalminutes / 60;
    result.minutes = totalminutes % 60;
    return result;
}

std::ostream & operator<<(std::ostream & os, const Time & t) {
    os << t.hours << " hours, " << t.minutes << " minutes";
    return os;
}

#include <iostream>
#include "mytime3.h"

int main() {
    using std::cout;
    using std::endl;
    Time aida(3, 35);
    Time tosca(2, 48);
    Time temp;

    cout << "Aida and Tosca:\n";
    cout << aida << "; " << tosca << endl;
    temp = aida + tosca;
    cout << "Aida + Tosca: " << temp << endl;
    temp = aida * 1.17;
    cout << "Aida * 1.17: " << temp << endl;
    cout << "10.0 * Tosca: " << 10.00 * tosca << endl;

    std::cin.get();

    return 0;
}
View Code

  11.4 重载运算符:作为成员函数还是非成员函数

  11.5 再谈重载:一个矢量类

    11.5.1 使用状态成员

    11.5.2 为Vector类重载算术运算符

    11.5.3 对实现的说明

    11.5.4 使用Vector类来模拟随机漫步

  11.6 类的自动转换和强制类型转换

    11.6.1 转换函数

    11.6.2 转换函数和友元函数

  11.7 总结

  11.8 复习题

  11.9 编程练习

第12章 类和动态内存分配

  12.1 动态内存和类

    12.1.1 复习示例和静态类成员

    12.1.2 特殊成员函数

    12.1.3 回到Stringbad: 复制构造函数的哪里出了问题

    12.1.4 Stringbad的其他问题: 赋值运算符

  12.2 改进后的新String类

    12.2.1 修订后的默认构造函数

    12.2.2 比较成员函数

    12.2.3 使用中括号表示法访问字符

    12.2.4 静态类成员和函数

    12.2.5 进一步重载赋值运算符

  12.3 在构造函数中使用new时应该注意的事项

    12.3.1 应该和不应该

    12.3.2 包含类成员的类的逐成员复制

  12.4 有关返回对象的说明

    12.4.1 返回指向const对象的引用

    12.4.2 返回指向非const对象的引用

    12.4.3 返回对象

    12.4.4 返回const对象

  12.5 使用指向对象的指针

    12.5.1 再谈new和delete

    12.5.2 指针和对象小结

    12.5.3 再谈定位new运算符

  12.6 复习各种技术

    12.6.1 重载<<运算符

    12.6.2 转换函数

    12.6.3 其构造函数使用new的类

  12.7 队列模拟

    12.7.1 队列类

    12.7.2 Customer类

    12.7.3 ATM模拟

  12.8 总结

  12.9 复习题

  12.10 编程练习

第13章 类继承

  13.1 一个简单的基类

    13.1.1 派生一个类

    13.1.2 构造函数:访问权限的考虑

    13.1.3 使用派生类

    13.1.4 派生类和基类之间的特殊关系

  13.2 继承: is-a 关系

  13.3 多态公有继承

    13.3.1 开发Brass类和BrassPlus类

  13.4 静态联编和动态联编

    13.4.1 指针和引用类型的兼容性

    13.4.2 虚成员函数和动态联编

    13.4.3 有关虚函数注意事项

  13.5 访问控制:protected

  13.6 抽象基类

    13.6.1 应用ABC概念

    13.6.2 ABC概念

  13.7 继承和动态内存分配

    13.7.1 第一种情况:派生类不使用new

    13.7.2 第二种情况:派生类使用new

    13.7.3 使用动态内存分配和友元的继承示例

  13.8 类设计回顾

    13.8.1 编译器生成的成员函数

    13.8.2 其他的类方法

    13.8.3 公有继承的考虑因素

    13.8.4 类函数小结

  13.9 总结

  13.10 复习题

  13.11 编程练习

第14章 C++中的代码重用

  14.1 包含对象成员的类

    14.1.1 valarry类简介

    14.1.2 Student类的设计

    14.1.3 Student类示例

  14.2 私有继承

    14.2.1 Student类示例(新版本)

    14.2.2 使用包含还是私有继承

    14.2.3 保护继承

    14.2.4 使用using重新定义访问权限

  14.3 多重继承

    14.3.1 有多少Worker

    14.3.2 哪个方法

    14.3.3 MI小结

  14.4 类模板

    14.4.1 定义类模板

    14.4.2 使用模板类

    14.4.3 深入探讨模板类

    14.4.4 数组模板示例和非类型参数

    14.4.5 模板多功能性

    14.4.6 模板的具体化

    14.4.7 成员模板

    14.4.8 将模板用作参数

    14.4.9 模板类和友元

    14.4.10 模板别名(C++11)

  14.5 总结

  14.6 复习题

  14.7 编程练习

第15章 友元,异常和其他

   15.1 友元

    15.1.1 友元类

    15.1.2 友元成员函数

    15.1.3 其他友元关系

    15.1.4 共同的友元

  15.2 嵌套类

    15.2.1 嵌套类和访问权限

    15.2.2 模板中的嵌套

  15.3 异常

    15.3.1 调用abort()

    15.3.2 返回错误码

    15.3.3 异常机制

    15.3.4 将对象用作异常类型

    15.3.5 异常规范和C++11

    15.3.6 栈解退

    15.3.7 其他异常特性

    15.3.8 exception类

    15.3.9 异常,类和继承

    15.3.10 异常何时会迷失方向

    15.3.11 有关异常的注意事项

  15.4 RTTI

    15.4.1 RTTI的用途

    15.4.2 RTTI的工作原理

  15.5 类型转换运算符

  15.6 总结

  15.7 复习题

  15.8 编程练习

第16章 string类和标准模板库

  16.1 string类

    16.1.1 构造字符串

    16.1.2 string类输入

    16.1.3 使用字符串

    16.1.4 sting还提供了哪些功能

    16.1.5 字符串种类

  16.2 智能指针模板类

    16.2.1 使用智能指针

    16.2.2 有关智能指针的注意事项

    16.2.3 unique_ptr为何优于auto_ptr

    16.2.4 选择智能指针

  16.3 标准模板库

    16.3.1 模板类vector

    16.3.2 可对矢量执行的操作

    16.3.3 对矢量可执行的其他操作

    16.3.4 基于范围的for循环(C++11)

  16.4 泛型编程

    16.4.1 为何使用迭代器

    16.4.2 迭代器类型

    16.4.3 迭代器层次结构

    16.4.4 概念,改进和模型

    16.4.5 容器种类

    16.4.6 关联容器

    16.4.7 无序关联容器(C++11)

  16.5 函数对象

    16.5.1 函数符概念

    16.5.2 预定义的函数符

    16.5.3 自适应函数符和函数适配器

  16.6 算法

    16.6.1 算法组

    16.6.2 算法的通用特征

    16.6.3 STL和string类

    16.6.4 函数和容器方法

    16.6.5 使用STL

  16.7 其他库

    16.7.1 vector,valarray和array

    16.7.2 模板initializer_list(C++11)

    16.7.3 使用initalizer_list

  16.8 总结

  16.9 复习题

  16.10 编程练习

第17章 输入,输出和文件

  17.1 C++输入和输出概述

    17.1.1 流和缓冲区

    17.1.2 流,缓冲区和iostream文件

    17.1.3 重定向

  17.2 使用cout进行输出

    17.2.1 重载的<<运算符

    17.2.2 其他ostream方法

    17.2.3 刷新输出缓冲区

    17.2.4 用cout进行格式化

  17.3 使用cin进行输入

    17.3.1 cin>>如何检查输入

    17.3.2 流状态

    17.3.3 其他istream类方法

    17.3.4 其他istream方法

  17.4 文件输入和输出

    17.4.1 简单的文件I/O

    17.4.2 流状态检查和is_open()

    17.4.3 打开多个文件

    17.4.5 文件模式

    17.4.6 随机存取

  17.5 内核格式化

  17.6 总结

  17.7 复习题

  17.8 编程练习

第18章 探讨C++新标准

  18.1 复习前面介绍过的C++11功能

    18.1.1 新类型

    18.1.2 统一的初始化

    18.1.3 声明

    18.1.4 智能指针

    18.1.5 异常规范方面的修改

    18.1.6 作用域内枚举

    18.1.7 对类的修改

    18.1.8 模板和STL方面的修改

    18.1.9 右值引用

  18.2 移动语义和右值引用

    18.2.1 为何需要移动语义

    18.2.2 一个移动示例

    18.2.3 移动构造函数解析

    18.2.4 赋值

    18.2.5 强制移动

  18.3 新的类功能

    18.3.1 特殊的成员函数

    18.3.2 默认的方法和禁用的方法

    18.3.3 委托构造函数

    18.3.4 继承构造函数

    18.3.5 管理虚方法:override和final

  18.4 Lambda函数

    18.4.1 比较函数指针,函数符和Lambda函数

    18.4.2 为何使用lambda

  18.5 包装器

    18.5.1 包装器function及模板的低效性

    18.5.2 修复问题

    18.5.3 其他方式

  18.6 可变参数模板

    18.6.1 模板和函数参数包

    18.6.2 展开参数包

    18.6.3 在可变参数模板函数中使用递归

  18.7 C++11新增的其他功能

    18.7.1 并行编程

    18.7.2 新增的库

    18.7.3 低级编程

    18.7.4 杂项

  18.8 语言变化

    18.8.1 Boost项目

    18.8.2 TR1

    18.8.3 Boost

  18.9 接下来的任务

  18.10 总结

  18.11 复习题

  18.12 编程练习

附录A 计数系统

  A.1 十进制数

  A.2 八进制整数

  A.3 十六进制数

  A.4 二进制数

  A.5 二进制和十六进制

附录B C++保留字

  B.1 C++关键字

  B.2 替代标记

  B.3 C++库保留名称

  B.4 有特殊含义的标识符

附录C ASCII字符集

附录D 运算符优先级

附录E 其他运算符

  E.1 按位运算符

    E.1.1 移位运算符

    E.1.2 逻辑按位运算符

    E.1.3 按位运算符的替代表示

    E.1.4 几种常用的按位运算符技术

  E.2 成员解除引用运算符

  E.3 alignof(C++11)

  E.4 noexcept(C++11)

附录F 模板类string

  F.1 13种类型和一个常量

  F.2 数据信息,构造函数及其他

    F.2.1 默认构造函数

    F.2.2 使用C-风格字符串的构造函数

    F.2.3 使用部分C-风格字符串的构造函数

    F.2.4 使用左值引用的构造函数

    F.2.5 使用右值引用的构造函数(C++11)

    F.2.6 使用一个字符的n个副本的构造函数

    F.2.7 使用区间的构造函数

    F.2.8 使用初始化列表的构造函数(C++11)

    F.2.9 内存杂记

  F.3 字符串存取

  F.4 基本赋值

  F.5 字符串搜索

    F.5.1 find()系列

    F.5.2 rfind()系列

    F.5.3 find_first_of()系列

    F.5.4 find_last_of()系列

    F.5.5 find_first_not_of()系列

    F.5.6 find_last_not_of()系列

  F.6 比较方法和函数 

  F.7 字符串修改方法

    F.7.1 用于追加和相加的方法

    F.7.2 其他赋值方法

    F.7.3 插入方法

    F.7.4 清除方法

    F.7.5 替换方法

    F.7.6 其他修改方法:copy()和swap()

  F.8 输出和输入

附录G 标准模板库方法和函数

  G.1 STL和C++11

    G.1.1 新增的容器

    G.1.2 对C++98容器所做的修改

  G.2 大部分容器都有的成员

  G.3 序列容器的其他成员

  G.4 set和map的其他操作

  G.5 无序关联容器(C++11)

  G.6 STL函数

    G.6.1 非修改式序列操作

    G.6.2 修改式序列操作

    G.6.3 排序和相关操作

    G.6.4 数值运算

附录H 精选读物和网上资源

  H.1 精选读物

  H.2 网上资源

附录I 转换为ISO标准C++

  I.1 使用一些预处理器编译指令的替代品

    I.1.1 使用const而不是#define来定义常量

    I.1.2 使用inline而不是#define来定义小型函数

  I.2 使用函数原型

  I.3 使用类型转换

  I.4 熟悉C++特性

  I.5 使用新的头文件

  I.6 使用名称空间

  I.7 使用智能指针

  I.8 使用string类

  I.9 使用STL

猜你喜欢

转载自www.cnblogs.com/revoid/p/9763188.html