第1章 预备知识 (已看)
第2章 开始学习C++ (已看)
第3章 处理数据 (已看)
第4章 复合类型 (已看)
第5章 循环和关系表达式 (已看)
第6章 分支语句和逻辑运算符 (已看)
第7章 函数-C++的编程模块 (已看)
第8章 函数探幽 (已看)
第9章 内存模型和名称空间 (已看)
第10章 对象和类 (已看)
第11章 使用类 (已看)
第12章 类和动态内存分配 (已看)
第13章 类继承 (已看)
第14章 C++中的代码重用 (已看)
第15章 友元,异常和其他 (已看)
第16章 string类和标准模板库 (已看)
第17章 输入,输出和文件 (已看)
第18章 探讨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(); ; }
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 = ; cout << "I have "; cout << carrots; cout << " carrots."; cout << endl; carrots = carrots - ; cout << "Crunch, crunch. Now I have " << carrots << " carrots." << endl; cin.get(); ; }
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 + ; cout << "Now you have " << carrots << " carrots." << endl; cin.get(); cin.get(); ; }
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(); ; }
C++还允许在创建变量时对它进行赋值,double side = sqrt(are);这个过程叫做初始化(initialization)
2.4.2 函数变体
2.4.3 用户定义的函数
#include <iostream> void simon(int); int main() { using namespace std; simon(); cout << "Pick an integer: "; int count; cin >> count; simon(count); cout << "Done!" << endl; cin.get(); cin.get(); ; } void simon(int n) { using namespace std; cout << "Simon says touch your toes " << n << " times." << endl; }
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(); ; } int stonetolb(int sts) { * sts; }
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(); ; }
#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 + ; sue = sue + ; 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 - ; sue = sue - ; cout << "Sam has " << sam << " dollars and Sue has " << sue; cout << " dollars deposited." << endl << "Lucky Sue!" << endl; cin.get(); ; }
通常,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 waist = 0x42; ; 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(); ; }
#include <iostream> int main() { using namespace std; ; ; ; 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(); ; }
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(); ; }
#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 + ; 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(); ; }
有些字符不能直接通过键盘输入到程序中.例如,按回车键并不能使字符串包含一个换行符;相反,程序编辑器将把这种键击解释为在源代码中开始新的一行.其他一些字符也无法从键盘输入,因此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 << * million * tub << endl; cout << "mint = " << mint << " and a million mints = "; cout << million * mint << endl; cin.get(); ; }
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(); ; }
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(); ; }
也许读者对得到的结果心存怀疑.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 << / << endl; cout << "Floating-point division: 9.0 / 5.0 = "; cout << 9.0 / 5.0 << endl; cout << << endl; cout << "double constants: 1e7/9.0 = "; cout << .e7 / 9.0 << endl; cout << "float constants: 1e7f/ 9.0f = "; cout << .e7f / 9.0f << endl; cin.get(); cin.get(); ; }
3.4.3 求模运算符
#include <iostream> int main() { using namespace std; ; 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(); ; }
3.4.4 类型转换
#include <iostream> int main() { using namespace std; cout.setf(ios_base::fixed, ios_base::floatfield); ; // 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(); ; }
在计算表达式时,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(); ; }
3.4.5 C++11中的auto声明
3.5 总结
3.6 复习题
3.7 编程练习
第4章 复合类型
4.1 数组
数组之所以被称为复合类型,是因为它是使用其他类型来创建的.不能仅仅将某种东西声明为数组,它必须是特定类型的数组.没有通用的数组类型,但存在很多特定的数组类型,如char数组或long数组.
#include <iostream> int main() { using namespace std; ]; yams[] = ; yams[] = ; yams[] = ; ] = { ,, }; cout << "Total yams = "; cout << yams[] + yams[] + yams[] << endl; cout << ] << " yams costs "; cout << yamcosts[] << " cents per yam.\n"; ] * yamcosts[] + yams[] * yamcosts[]; total = total + yams[] * yamcosts[]; cout << "The total yam expense is " << total << " cents.\n"; cout << "\nSize of yams array = " << sizeof yams; cout << " bytes.\n"; cout << ]; cout << " bytes.\n"; cin.get(); cin.get(); ; }
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; ; 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 << ] << ".\n"; name2[] = '\0'; cout << "Here are the first 3 characters of my name: "; cout << name2 << endl; cin.get(); cin.get(); ; }
4.2.3 字符串输入
#include <iostream> int main() { using namespace std; ; 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(); ; }
4.2.4 每次读取一行字符串输入
#include <iostream> int main() { using namespace std; ; 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(); ; }
#include <iostream> int main() { using namespace std; ; 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(); ; }
4.2.5 混合输入字符串和数字
4.3 string简介
#include <iostream> #include <string> int main() { using namespace std; ]; ] = "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[] << endl; cout << "The third letter in " << str2 << " is " << str2[] << endl; cin.get(); cin.get(); ; }
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(); ; }
4.3.3 string类的其他操作
#include <iostream> #include <string> #include <cstring> int main() { using namespace std; ]; ] = "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(); ; }
4.3.4 stringl类I/O
#include <iostream> #include <string> #include <cstring> int main() { using namespace std; ]; 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, ); 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(); ; }
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 { ]; 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(); ; }
4.4.2 C++11结构初始化
4.4.3 结构可以将string类作为成员吗
4.4.4 其他结构属性
可以使用赋值运算符(=)将结构赋给另一个同类型的结构,这样结构中每个成员都将被设置位另一个结构中相应成员的值,即使成员是数组.这种赋值被称为成员赋值(memberwise assignment)
#include <iostream> struct inflatable { ]; 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(); ; }
4.4.5 结构数组
#include <iostream> struct inflatable { ]; float volume; double price; }; int main() { using namespace std; inflatable guests[] = { { "Bambi", 0.5, 21.99 }, { , 565.99 } }; cout << ].name << ].name << "\nhave a combined volume of " << guests[].volume + guests[].volume << " cubic feet.\n"; cin.get(); cin.get(); ; }
4.4.6 结构中的位字段
struct torgle_register { unsigned ; unsigned ; ; ; };
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; ; 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(); ; }
面向对象编程与传统的过程性编程的区别在于,OOP强调的是在运行阶段(而不是编译阶段)进行决策.运行阶段指的是程序正在运行时,编译阶段指的是编译器将程序组合起来时.运行阶段决策就好比度假时,选择参观哪些景点取决于天气和当时的心情;而编译阶段决策更像不管什么条件下,都坚持预先设定的日程安排
使用OOP时,您可能在运行阶段确定数组的长度.为使用这种方法,语言必须允许在程序运行时创建数组.C++采用的方法是,使用关键字new请求正确数量的内存以及使用指针来跟踪新分配的内存的位置
在运行阶段做决策并非OOP独有的,但使用C++编写这样的代码比使用C语言简单
#include <iostream> #include <string> int main() { using namespace std; ; 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 + ; cout << "Now updates = " << updates << endl; cin.get(); cin.get(); ; }
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* 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(); ; }
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 * pt = new int; *pt = ; 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(); ; }
对于指针,需要指出的另一点是,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; ]; p3[] = 0.2; p3[] = 0.5; p3[] = 0.8; cout << ] << ".\n"; p3 = p3 + ; cout << ] << " and "; cout << ] << ".\n"; p3 = p3 - ; delete[] p3; cin.get(); cin.get(); ; }
4.8 指针,数组和指针算术
将整数变量加1后,其值将增加1;但将指针变量加1后,增加的量等于它指向的类型的字节数
C++将数组名解释为地址
#include <iostream> int main() { using namespace std; ] = { 100000.0, 20000.0, 30000.0 }; ] = { , , }; // here are two ways to get the address of an array double * pw = wages; // name of an array = address ]; // name of an array = address cout << "pw = " << pw << ", *pw = " << *pw << endl; pw = pw + ; cout << "add 1 to the pw pointer:\n"; cout << "pw = " << pw << ", *pw = " << *pw << "\n\n"; cout << "ps = " << ps << ", *ps = " << *ps << endl; ps = ps + ; cout << "add 1 to the ps pointer:\n"; cout << "ps = " << ps << ", *ps = " << *ps << "\n\n"; cout << "access two elements with array notation\n"; cout << ] << ] << endl; cout << "access two elements with pointer notation\n"; cout << "*stacks = " << *stacks << ) << endl; cout << sizeof(wages) << " = size of wages array\n"; cout << sizeof(pw) << " = size of pw pointer\n"; cin.get(); cin.get(); ; }
4.8.1 程序说明
#include <iostream> int main() { using namespace std; ]; cout << tell << << endl; // displays &tell[0] ] = &tell; cout << pas << << endl; // displays address of whole array cin.get(); cin.get(); ; }
4.8.2 指针小结
4.8.3 指针和字符串
在cout和多数C++表达式中,char数组名,char指针以及用引号括起的字符串常量都被解释为字符串第一个字符的地址.
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> int main() { using namespace std; ] = "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 = ]; 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(); ; }
一般来说,如果给cout提供一个指针,它将打印地址.但如果指针的类型为char*,则cout将显示指向的字符串.如果要显示的是字符串的地址,则必须将这种指针强制转换为另一种指针类型,如int*
4.8.4 使用new创建动态结构
#include <iostream> struct inflatable { ]; float volume; double price; }; int main() { using namespace std; inflatable * ps = new inflatable; cout << "Enter name of inflatable item: "; cin.); 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(); ; }
#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(); ; } char * getname() { ]; cout << "Enter last name: "; cin >> temp; ]; strcpy(pn, temp); return pn; }
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 = ; antarctica_years_end * pa = &s02; pa->year = ; antarctica_years_end trio[]; trio[].year = ; std::cout << trio->year << std::endl; ] = { &s01, &s02, &s03 }; std::cout << arp[]->year << std::endl; const antarctica_years_end ** ppa = arp; auto ppb = arp; std::cout << (*ppa)->year << std::endl; std::cout << (*(ppb + ))->year << std::endl; cin.get(); cin.get(); ; }
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++ ] = { 1.2, 2.4, 3.6, 4.8 }; // C++98 STL vector<); // create vector with 4 elements // no simple way to initialize in C98 a2[] = 1.0 / 3.0; a2[] = 1.0 / 5.0; a2[] = 1.0 / 7.0; a2[] = 1.0 / 9.0; // C++11 -- create and initialize array object array<> a3 = { 3.14, 2.72, 1.62, 1.41 }; array<> a4; a4 = a3; // valid for array objects cout << ] << ] << endl; cout << ] << ] << endl; cout << ] << ] << endl; cout << ] << ] << endl; a1[-] = 20.2; cout << ] << ] << endl; cout << ] << ] << endl; cout << ] << ] << endl; cin.get(); cin.get(); ; }
4.11 总结
4.12 复习题
4.13 编程练习
第5章 循环和关系表达式
5.1 for循环
#define _CRT_SECURE_NO_WARNINGS #include <iostream> int main() { using namespace std; int i; ; i < ; i++) { cout << "C++ knows loops.\n"; } cout << "C++ knows when to stop.\n"; cin.get(); cin.get(); ; }
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(); ; }
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 = ) << endl; cout << "Now x = " << x << endl; cout << "The expression x < 3 has the value "; cout << (x < ) << endl; cout << "The expression x > 3 has the value "; cout << (x > ) << endl; cout.setf(ios_base::boolalpha); cout << "The expression x < 3 has the value "; cout << (x < ) << endl; cout << "The expression x > 3 has the value "; cout << (x > ) << endl; cin.get(); cin.get(); ; }
5.1.2 回到for循环
#define _CRT_SECURE_NO_WARNINGS #include <iostream> ; int main() { using namespace std; long long factorials[ArSize]; factorials[] = factorials[] = 1LL; ; i < ArSize; i++) { factorials[i] = i * factorials[i - ]; } ; i < ArSize; i++) { std::cout << i << "! = " << factorials[i] << std::endl; } cin.get(); cin.get(); ; }
5.1.3 修改步长
#define _CRT_SECURE_NO_WARNINGS #include <iostream> ; 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"; ; i < ; i = i + by) { cout << i << endl; } cin.get(); cin.get(); ; }
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; ; i >= ; i--) { cout << word[i]; } cout << "\nBye.\n"; cin.get(); cin.get(); ; }
5.1.5 递增运算符(++)和递减运算符(--)
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <string> int main() { using std::cout; using std::cin; ; ; cout << "a = " << a << ": b = " << b << "\n"; cout << "a++ = " << a++ << ": ++b = " << ++b << "\n"; cout << "a = " << a << ": b = " << b << "\n"; cin.get(); cin.get(); ; }
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; ] = "?ate"; for (char ch = 'a'; strcmp(word, "mate"); ch++) { cout << word << endl; word[] = ch; } cout << "After loop ends, word is " << word << endl; cin.get(); cin.get(); ; }
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[] = ch; } cout << "After loop ends, word is " << word << endl; cin.get(); cin.get(); ; }
5.2 while循环
#define _CRT_SECURE_NO_WARNINGS #include <iostream> ; int main() { using namespace std; char name[ArSize]; cout << "Your first name, please: "; cin >> name; cout << "Here is your name, verticalized and ASCIIized:\n"; ; while (name[i] != '\0') { cout << name[i] << ": " << int(name[i]) << endl; i++; } cin.get(); cin.get(); ; }
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(); ; }
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; } ); cout << "Yes, 7 is my favorite.\n"; cin.get(); cin.get(); ; }
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; ; 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(); ; }
5.5.2 使用cin.get(char)进行补救
#define _CRT_SECURE_NO_WARNINGS #include <iostream> int main() { using namespace std; char ch; ; 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(); ; }
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; ; cin.get(ch); while (cin.fail() == false) { cout << ch; ++count; cin.get(ch); } cout << endl << count << " characters read\n"; cin.get(); cin.get(); ; }
5.5.5 另一个cin.get()版本
5.6 嵌套循环和二维数组
5.6.1 初始化二维数组
5.6.2 使用二维数组
#define _CRT_SECURE_NO_WARNINGS #include <iostream> ; ; int main() { using namespace std; const char * cities[Cities] = { "Gribble City", "Gribbletown", "New Gribble", "San Gribble", "Gribble Vista" }; int maxtemps[Years][Cities] = { { , , , , }, { , , , , }, { , , , , }, { , , , , } }; cout << "Maximum temperatures for 2008 - 2011\n\n"; ; city < Cities; ++city) { cout << cities[city] << ":\t"; ; year < Years; ++year) { cout << maxtemps[year][city] << "\t"; } cout << endl; } cin.get(); cin.get(); ; }
5.7 总结
5.8 复习题
5.9 编程练习
第6章 分支语句和逻辑运算符
6.1 if语句
#define _CRT_SECURE_NO_WARNINGS #include <iostream> ; ; int main() { using std::cin; using std::cout; char ch; ; ; 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(); ; }
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; ; ; ; ; ; 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(); ; }
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; ]; 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, ); 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(); 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(); 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(); ; }
6.8.3 读取文本文件
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <fstream> #include <cstdlib> // support for exit() ; 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; ; 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"; } ) { 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(); ; }
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(); ; } void n_chars(char c, int n) { ) { cout << c; } }
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> ; int sum_arr(int arr[], int n); int main() { , , , , , , , }; 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, ); std::cout << "First three eaters ate " << sum << " cookies.\n"; sum = sum_arr(cookies + , ); std::cout << "Last four eaters ate " << sum << " cookies.\n"; std::cin.get(); std::cin.get(); ; } int sum_arr(int arr[], int n) { ; std::cout << arr << " = arr, "; std::cout << sizeof arr << " = sizeof arr\n"; ; i < n; i++) { total = total + arr[i]; } return total; }
7.3.3 更多数组函数示例
7.3.4 使用数组区间的函数
STL方法使用"超尾“概念来指定区间.也就是说,对于数组而言,标识数组结尾的参数将是指向最后一个元素后面的指针
#define _CRT_SECURE_NO_WARNINGS #include <iostream> ; int sum_arr(const int * begin, const int * end); int main() { using namespace std; , , , , , , , }; int sum = sum_arr(cookies, cookies + ArSize); cout << "Total cookies eaten: " << sum << endl; sum = sum_arr(cookies, cookies + ); cout << "First three eaters ate " << sum << " cookies.\n"; sum = sum_arr(cookies + , cookies + ); cout << "Last four eaters ate " << sum << " cookies.\n"; std::cin.get(); std::cin.get(); ; } int sum_arr(const int * begin, const int * end) { const int * pt; ; for (pt = begin; pt != end; pt++) { total = total + *pt; } return total; }
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(); cout << ps << "-DONE-" << ps << endl; delete[] ps; cin.get(); cin.get(); ; } char * buildstr(char c, int n) { ]; pstr[n] = '\0'; ) { pstr[n] = c; } return pstr; }
7.6 函数和结构
为结构编写函数比为数组编写函数要简单得多.虽然结构变量和数组一样,都可以存储多个数据项,但在涉及到函数时,结构变量得行为更接近与基本得单值变量.也就是说,与数组不同,结构将其数据组合成单个实体或数据对象,该实体被视为一个整体
7.6.1 传递和返回结构
#include <iostream> struct travel_time { int hours; int mins; }; ; travel_time sum(travel_time t1, travel_time t2); void show_time(travel_time t); int main() { using namespace std; travel_time day1 = { , }; travel_time day2 = { , }; travel_time trip = sum(day1, day2); cout << "Two-day total: "; show_time(trip); travel_time day3 = { , }; cout << "Three-day total: "; show_time(sum(trip, day3)); cin.get(); cin.get(); ; } 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"; }
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(); ; } 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"; }
7.6.3 传递结构的地址
7.7 函数和string对象
#include <iostream> #include <string> using namespace std; ; void display(const string sa[], int n); int main() { string list[SIZE]; cout << "Enter your " << SIZE << " favorite astronomical sights:\n"; ; i < SIZE; i++) { cout << i + << ": "; getline(cin, list[i]); } cout << "Your list:\n"; display(list, SIZE); cin.get(); cin.get(); ; } void display(const string sa[], int n) { ; i < n; i++) { cout << i + << ": " << sa[i] << endl; } }
7.8 函数与array对象
#include <iostream> #include <array> #include <string> ; 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(); ; } void fill(std::array<double, Seasons> * pa) { using namespace std; ; 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"; ; i < Seasons; i++) { cout << Snames[i] << ": $" << da[i] << endl; total += da[i]; } cout << "Total Expenses: $" << total << endl; }
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(); ; } 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"; }
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; ] = { 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, ) << ) << endl; cout << p2(av, ) << ) << endl; ])(const double *, int) = { f1, f2, f3 }; auto pb = pa; cout << "\nUsing an array of pointers to functions:\n"; cout << " Address Value\n"; ; i < ; i++) { cout << pa[i](av, ) << ) << endl; } cout << "\nUsing a pointer to a pointer to a function:\n"; cout << " Address Value\n"; ; i < ; i++) { cout << pb[i](av, ) << ) << endl; } cout << "\nUsing pointers to an array of pointers:\n"; cout << " Address Value\n"; auto pc = &pa; cout << (*pc)[](av, ) << ](av, ) << endl; ])(const double *, int) = &pa; ](av, ); cout << pdb << ": " << *pdb << endl; cout << (*(*pd)[])(av, ) << ])(av, ) << endl; cin.get(); cin.get(); ; } const double * f1(const double * ar, int n) { return ar; } const double * f2(const double ar[], int n) { ; } const double * f3(const double ar[], int n) { ; }
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(); ; }
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 & 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(); ; }
引用不同于指针,除了表示法不同外,还有其他的差别.例如,差别之一是,必须在声明引用时将其初始化,而不能像指针那样,先声明,再赋值.
引用更接近于const指针,必须在创建时进行初始化,一旦与某个变量关联起来,就将一直效忠于它.
#define _CRT_SECURE_NO_WARNINGS #include <iostream> int main() { using namespace std; ; int & rodents = rats; cout << "rats = " << rats; cout << ", rodents = " << rodents << endl; cout << "rats address = " << &rats; cout << ", rodents address = " << &rodents << endl; ; 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(); ; }
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; ; ; 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(); ; } 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; }
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(); ; } double cube(double a) { a *= a * a; return a; } double refcube(double &ra) { ra *= ra * ra; return ra; }
左值参数是可被引用的数据对象,例如,变量,数组元素,结构成员,引用和解除引用的指针都是左值.非左值包括字面常量(用引号括起的字符串除外,它们由其地址表示)和包含多项的表达式.
在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 = { , }; free_throws two = { , }; free_throws three = { , }; free_throws four = { , }; free_throws five = { , }; free_throws team = { , }; 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(); ; } 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) { ) { ft.percent = 100.0f * float(ft.made) / float(ft.attempts); } else { ft.percent = ; } } free_throws & accumulate(free_throws & target, const free_throws & source) { target.attempts += source.attempts; target.made += source.made; set_pc(target); return target; }
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(); ; } 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; }
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); ; 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"; ; i < LIMIT; i++) { cout << << ": "; cin >> eps[i]; } file_it(fout, objective, eps, LIMIT); file_it(cout, objective, eps, LIMIT); cout << "Done\n"; cin.get(); cin.get(); ; } void file_it(ostream & os, double fo, const double fe[], int n) { ios_base::fmtflags initial; initial = os.setf(ios_base::fixed); os.precision(); os << "Focal length of objective: " << fo << "mm\n"; os.setf(ios::showpoint); os.precision(); os.width(); os << "f.1. eyeprece"; os.width(); os << "magnification" << endl; ; i < n; i++) { os.width(); os << fe[i]; os.width(); os << int(fo / fe[i] + 0.5) << endl; } os.setf(initial); }
8.2.7 何时使用引用参数
使用引用参数的主要原因有两个
1. 程序员能够修改调用函数中的数据对象
2. 通过传递引用而不是整个数据对象,可以提高程序的运行速度
对于使用传递的值而不作修改的函数
1. 如果数据对象很小,如内置数据类型或小型结构,则按值传递
2. 如果数据对象是数组,则使用指针,因为这是唯一的选择,并将指针声明为指向const的指针
3. 如果数据对象是较大的结构,则使用const指针或const引用
4. 如果数据对象是类对象,则使用const引用.类设计的语义常常要求使用引用,这是C++新增这项特性的主要原因.因此,传递类对象参数的标准方式是按引用传递
对于修改调用函数中数据的函数
1. 如果数据对象是数组,则只能使用指针
2. 如果数据对象是结构,则使用引用或指针
3. 如果数据对象是类对象,则使用引用
当然,这只是一些指导原则,很可能有充分的理由做出其他的选择
8.3 默认参数
#include <iostream> ; ); int main() { using namespace std; char sample[ArSize]; cout << "Enter a string:\n"; cin.get(sample, ArSize); ); cout << ps << endl; delete[] ps; ps = left(sample); cout << ps << endl; delete[] ps; cin.get(); cin.get(); ; } char * left(const char * str, int n) { ) { n = ; } ]; int i; ; i < n && str[i]; i++) { p[i] = str[i]; } while (i <= n) { p[i++] = '\0'; } return p; }
8.4 函数重载
术语"函数重载"指的是可以有多个同名的参数,因此对名称进行了重载.可以通过重载来设计一些列函数----它们完成相同的工作,但使用不同的参数列表
函数重载的关键是函数的参数列表----也称为函数特征标(function signature).如果两个函数的参数数目和类型相同,则它们的特征标相同,而变量名是无关紧要的.C++允许定义名称相同的函数
条件是它们的特征标不同.如果参数数目和/或参数类型不同,则特征标也不同.
编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标
8.4.1 重载示例
#include <iostream> unsigned long left(unsigned long num, unsigned ct); ); int main() { using namespace std; const char * trip = "Hawaii!!"; unsigned ; int i; char * temp; ; i < ; i++) { cout << left(n, i) << endl; temp = left(trip, i); cout << temp << endl; delete[] temp; } cin.get(); cin.get(); ; } unsigned long left(unsigned long num, unsigned ct) { unsigned digits = ; unsigned long n = num; || num == ) { ; } ) { digits++; } if (digits > ct) { ct = digits - ct; while (ct--) { num /= ; } return num; } else { return num; } } char * left(const char * str, int n) { ) { n = ; } ]; int i; ; i < n && str[i]; i++) { p[i] = str[i]; } while (i <= n) { p[i++] = '\0'; } return p; }
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; ; ; 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(); ; } template <typename T> void Swap(T & a, T & b) { T temp; temp = a; a = b; b = temp; }
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[]); ; int main() { using namespace std; , j = ; cout << "i, j = " << i << ", " << j << ".\n"; cout << "Using compiler-generated int swapper:\n"; Swap(i, j); cout << "Now i, j = " << i << ", " << j << ".\n"; , , , , , , , }; , , , , , , , }; cout << "Original arrays:\n"; Show(d1); Show(d2); Swap(d1, d2, Lim); cout << "Swapped arrays:\n"; Show(d1); Show(d2); cin.get(); cin.get(); ; } 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; ; i < n; i++) { temp = a[i]; a[i] = b[i]; b[i] = temp; } } void Show(int a[]) { using namespace std; cout << a[] << a[] << "/"; cout << a[] << a[] << "/"; ; i < Lim; i++) { cout << a[i]; } cout << endl; }
8.5.2 模板的局限性
8.5.3 显示具体化
#include <iostream> template <typename T> void Swap(T & a, T & b); struct job { ]; double salary; int floor; }; template<> void Swap<job>(job & j1, job & j2); void Show(job & j); int main() { using namespace std; cout.precision(); cout.setf(ios::fixed, ios::floatfield); , j = ; 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 = { }; job sidney = { }; 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(); ; } 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; }
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(); ; } #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"; }
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; ; ; 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(); ; } void oil(int x) { using namespace std; ; cout << "In oil(), texas = " << texas << ", &texas = "; cout << &texas << endl; cout << "In oil(), x = " << x << ", &x = "; cout << &x << endl; { ; 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; }
由于自动变量的数目随函数的开始和结束而增减,因此程序必须在运行时对自动变量进行管理.常用的方法是留出一段内存,并将其视为栈,以管理变量的增减
9.2.3 静态持续变量
和C语言一样,C++也为静态存储持续性变量提供了3中链接性:外部链接性(可在其他文件中访问),内部链接性(只能在当前文件中访问)和无链接性(只能在当前函数或代码块中访问).这3种链接性都在整个程序执行期间存在,与自动变量相比,它们的寿命更长.
由于静态变量的数目在程序运行期间是不变的,因此程序不需要使用特殊的装置(如栈)来管理它们.编译器将分配固定的内存块来存储所有的静态变量,这些变量在整个程序执行期间一直存在.另外,如果没有显式地初始化静态变量,编译器将把它设置为0,
在默认情况下,静态数组和结构将每个元素或成员的所有位都设置为0
想要创建链接性为外部的静态持续变量,必须在代码块的外面声明它;要创建链接性为内部的静态持续变量,必须在代码块的外面声明它,并使用static限定符,要创建没有链接性的静态持续变量,必须在代码块内声明它,并使用statci限定符
; // static duration,external linkage ; // static duration,internal linkage int main() { } void funct1(int n) { ; // static duration, on linkage }
表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 ; // definition because of initialization ; // 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;
#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(); ; }
9.2.5 静态持续性,内部链接性
#define _CRT_SECURE_NO_WARNINGS #include <iostream> ; ; ; 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(); ; } #include <iostream> extern int tom; ; ; void remote_access() { using namespace std; cout << "remote_access() reports the following addresses:\n"; cout << &tom << " = &tom, " << &dick << " = &dick, "; cout << &harry << " = &harry\n"; }
9.2.6 静态存储持续性,无链接性
#define _CRT_SECURE_NO_WARNINGS #include <iostream> ; 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(); ; } void strcount(const char * str) { using namespace std; ; ; cout << "\"" << str << "\" contains "; while (*str++) { count++; } total += count; cout << count << " characters\n"; cout << total << " characters total\n"; }
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分配的内存
); // *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}; ] { ,,, }; 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)); ]; * sizeof(int)); delete pi; delete (pi);
通常,new负责在堆(heap)中找到一个足以能够满足要求的内存块.new运算符还有另一种变体,被称为定位(palcement)new运算符,它让您能够指定要使用的位置.程序员可能使用这种特性来设置其内存管理规程,处理需要通过特定地址进行访问的硬件或在特定位置创建对象
#include <new> struct chaff { ]; int slag; }; ]; ]; int main() { chaff * p1, * p2; int * p3, * p4; // first, the regular forms of new p1 = new chaff; // place structure in heap p3 = ]; // place int array in heap // now, the two forms of placement new p2 = new (buffer1) chaff; // place structure in buffer1 p4 = ]; // place int array in buffer2
#define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <new> ; ; 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 ; i < N; i++) { pd2[i] = pd1[i] = + 20.0 * i; } cout << "Memory address:\n" << " heap: " << pd1 << " static: " << (void *)buffer << endl; cout << "Memory contents:\n"; ; 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 ; i < N; i++) { pd4[i] = pd3[i] = + 40.0 * i; } cout << "Memory contents:\n"; ; 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]; ; i < N; i++) { pd2[i] = pd1[i] = + 60.0 * i; } cout << "Memory contents:\n"; ; i < N; i++) { cout << pd1[i] << " at " << &pd1[i] << "; "; cout << pd2[i] << " at " << &pd2[i] << endl; } delete[] pd1; delete[] pd3; cin.get(); cin.get(); ; }
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) { ; ; 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(); ; } void other(void) { using std::cout; using std::endl; using namespace debts; Person dg = { "Doodles", "Glister" }; showPerson(dg); cout << endl; Debt zippy[]; int i; ; i < ; i++) { getDebt(zippy[i]); } ; i < ; i++) { showDebt(zippy[i]); } cout << ) << endl; return; } void another(void) { using pers::Person; Person collector = { "Milo", "Rightshift" }; pers::showPerson(collector); std::cout << std::endl; }
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
类描述看上去很像是包含成员函数以及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; ) { std::cout << "Number of shares can't be negative; " << company << " shares set to 0.\n"; shares = ; } else { shares = n; } share_val = pr; set_tot(); } void Stock::buy(long num, double price) { ) { 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; ) { 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'; }
10.2.4 使用类
#include <iostream> #include "stock00.h" int main() { Stock fluffy_the_cat; fluffy_the_cat.acquire(, 12.50); fluffy_the_cat.show(); fluffy_the_cat.buy(, 18.125); fluffy_the_cat.show(); fluffy_the_cat.sell(, 20.00); fluffy_the_cat.show(); fluffy_the_cat.buy(, 40.125); fluffy_the_cat.show(); fluffy_the_cat.sell(, 0.125); fluffy_the_cat.show(); ; }
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(, 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 = ; 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; ) { std::cout << "Number of shares can't be negative; " << company << " shares set to 0.\n"; shares = ; } else { shares = n; } share_val = pr; set_tot(); } Stock::~Stock() { std::cout << "Bye, " << company << "!\n"; } void Stock::buy(long num, double price) { ) { 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; ) { 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(); cout << "Company: " << company << " Shares: " << shares << '\n'; cout << " Share Price: $" << share_val; cout.precision(); 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(, 20.0); stock1.show(); Stock stock2 = Stock(, 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(, 50.0); cout << "Revised stock1:\n"; stock1.show(); cout << "Done\n"; } std::cin.get(); std::cin.get(); ; }
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(, 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 = ; share_val = 0.0; total_val = 0.0; } Stock::Stock(const std::string & co, long n, double pr) { company = co; ) { std::cout << "Number of shares can't be negative; " << company << " shares set to 0.\n"; shares = ; } else { shares = n; } share_val = pr; set_tot(); } Stock::~Stock() { } void Stock::buy(long num, double price) { ) { 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; ) { 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(); cout << "Company: " << company << " Shares: " << shares << '\n'; cout << " Share Price: $" << share_val; cout.precision(); 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; } }
10.5 对象数组
10.6 类作用域
10.6.1 作用域为类的常量
class Bakery { private: }; double costs[Months]; ... class Bakery { private: ; double costs[Months]; ...
10.6.2 作用域内枚举(C++11)
10.7 抽象数据类型
#ifndef STACK_H_ #define STACK_H_ typedef unsigned long Item; class Stack { private: }; 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 = ; } bool Stack::isempty() const { ; } 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) { ) { 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"; ; }
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(); void AddMin(int m); void AddHr(int h); , ); Time Sum(const Time & t) const; void Show() const; }; #endif // mytime0.cpp #include <iostream> #include "mytime0.h" Time::Time() { hours = minutes = ; } Time::Time(int h, int m) { hours = h; minutes = m; } void Time::AddMin(int m) { minutes += m; hours += minutes / ; minutes %= ; } 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 / ; sum.minutes %= ; return sum; } #include <iostream> #include "mytime0.h" int main() { using std::cout; using std::endl; Time palnning; Time coding(, ); Time fixing(, ); 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; ; } // mytime1.h #ifndef MYTIME0_H_ #define MYTIME0_H_ class Time { private: int hours; int minutes; public: Time(); Time(); void AddMin(int m); void AddHr(int h); , ); Time operator+(const Time & t) const; void Show() const; }; #endif // mytime1.cpp #include <iostream> #include "mytime1.h" Time::Time() { hours = minutes = ; } Time::Time(int h, int m) { hours = h; minutes = m; } void Time::AddMin(int m) { minutes += m; hours += minutes / ; minutes %= ; } 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 / ; sum.minutes %= ; 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(, ); Time fixing(, ); 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(, ); cout << "more fixing time = "; morefixing.Show(); cout << endl; total = morefixing.operator+(total); cout << "morefixing.operator+(total) = "; total.Show(); cout << endl; ; }
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(); void AddMin(int m); void AddHr(int h); , ); 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 = ; } Time::Time(int h, int m) { hours = h; minutes = m; } void Time::AddMin(int m) { minutes += m; hours += minutes / ; minutes %= ; } 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 / ; sum.minutes %= ; return sum; } Time Time::operator-(const Time & t) const { Time diff; int tot1, tot2; tot1 = t.minutes + * t.hours; tot2 = minutes + * hours; diff.minutes = (tot2 - tot1) % ; diff.hours = (tot2 - tot1) / ; return diff; } Time Time::operator*(double mult) const { Time result; + minutes * mult; result.hours = totalminutes / ; result.minutes = totalminutes % ; 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(, ); Time tosca(, ); 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(); ; }
11.4 重载运算符:作为成员函数还是非成员函数
11.5 再谈重载:一个矢量类
11.5.1 使用状态成员
11.5.2 为Vector类重载算术运算符
11.5.3 对实现的说明
11.5.4 使用Vector类来模拟随机漫步
#ifndef VECTOR_H_ #define VECTOR_H_ #include <iostream> namespace VECTOR { class Vector { public: enum Mode { RECT, POL }; // RECT for rectangular, POL for Polar modes private: double x; double y; double mag; double ang; Mode mode; void set_mag(); void set_ang(); void set_x(); void set_y(); public: Vector(); Vector(double n1, double n2, Mode form = RECT); void reset(double n1, double n2, Mode form = RECT); ~Vector(); double xval() const { return x; } double yval() const { return y; } double magval() const { return mag; } double angval() const { return ang; } void polar_mode(); void rect_mode(); Vector operator+(const Vector & b) const; Vector operator-(const Vector & b) const; Vector operator-() const; Vector operator*(double n) const; friend Vector operator*(double n, const Vector & a); friend std::ostream & operator<<(std::ostream & os, const Vector &v); }; } #endif #include <cmath> #include "vector.h" using std::sqrt; using std::sin; using std::cos; using std::atan; using std::atan2; using std::cout; namespace VECTOR { const double Rad_to_deg = 45.0 / atan(1.0); void Vector::set_mag() { mag = sqrt(x * x + y * y); } void Vector::set_ang() { if (x == 0.0 && y == 0.0) { ang = 0.0; } else { ang = atan2(y, x); } } void Vector::set_x() { x = mag * cos(ang); } void Vector::set_y() { y = mag * sin(ang); } Vector::Vector() { x = y = mag = ang = 0.0; mode = RECT; } Vector::Vector(double n1, double n2, Mode form) { mode = form; if (form == RECT) { x = n1; y = n2; set_mag(); set_ang(); } else if (form == POL) { mag = n1; ang = n2 / Rad_to_deg; } else { cout << "Incorrect 3rd argument to Vector() -- "; cout << "vector set to 0\n"; x = y = mag = ang = 0.0; mode = RECT; } } void Vector::reset(double n1, double n2, Mode form) { mode = form; if (form == RECT) { x = n1; y = n2; set_mag(); set_ang(); } else if (form == POL) { mag = n1; ang = n2 / Rad_to_deg; set_x(); set_y(); } else { cout << "Incorrect 3rd argument to Vector() -- "; cout << "vector set to 0\n"; x = y = mag = ang = 0.0; mode = RECT; } } Vector::~Vector() { } void Vector::polar_mode() { mode = POL; } void Vector::rect_mode() { mode = RECT; } Vector Vector::operator+(const Vector & b) const { return Vector(x + b.x, y + b.x); } Vector Vector::operator-(const Vector & b) const { return Vector(x - b.x, y - b.y); } Vector Vector::operator-() const { return Vector(-x, -y); } Vector Vector::operator*(double n) const { return Vector(n * x, n * y); } Vector operator*(double n, const Vector & a) { return a * n; } std::ostream & operator<<(std::ostream & os, const Vector & v) { if (v.mode == Vector::RECT) { os << "(x,y) = (" << v.x << ", " << v.y << ")"; } else if (v.mode == Vector::POL) { os << "(m,a) = (" << v.mag << ", " << v.ang * Rad_to_deg << ")"; } else { os << "Vector object mode is invalid"; } return os; } } #include <iostream> #include <cstdlib> #include <ctime> #include "vector.h" int main() { using namespace std; using VECTOR::Vector; srand(time()); double direction; Vector step; Vector result(0.0, 0.0); unsigned ; double target; double dstep; cout << "Enter target distance (q to quit): "; while (cin >> target) { cout << "Enter step length: "; if (!(cin >> dstep)) { break; } while (result.magval() < target) { direction = rand() % ; step.reset(dstep, direction, Vector::POL); result = result + step; steps++; } cout << "After " << steps << " steps, the subject " "has the following location:\n"; cout << result << endl; result.polar_mode(); cout << " or\n" << result << endl; cout << "Averange outward distance per step = " << result.magval() / steps << endl; steps = ; result.reset(0.0, 0.0); cout << "Enter target distance (q to quit): "; } cout << "Bye\n"; cin.clear(); while (cin.get() != '\n') { continue; } ; }
11.6 类的自动转换和强制类型转换
#ifndef STONEWT_H_ #define STONEWT_H_ class Stonewt { private: }; int stone; double pds_left; double pounds; public: Stonewt(double lbs); Stonewt(int stn, double lbs); Stonewt(); ~Stonewt(); void show_lbs() const; void show_stn() const; }; #endif #include <iostream> #include "stonewt.h" using std::cout; Stonewt::Stonewt(double lbs) { stone = int(lbs) / Lbs_per_stn; pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs); pounds = lbs; } Stonewt::Stonewt(int stn, double lbs) { stone = stn; pds_left = lbs; pounds = stn * Lbs_per_stn + lbs; } Stonewt::Stonewt() { stone = pounds = pds_left = ; } Stonewt::~Stonewt() { } void Stonewt::show_stn() const { cout << stone << " stone, " << pds_left << " pounds\n"; } void Stonewt::show_lbs() const { cout << pounds << " pounds\n"; } #include <iostream> #include "stonewt.h" using std::cout; void display(const Stonewt & st, int n); int main() { Stonewt incognito = ; Stonewt wolfe(285.7); Stonewt taft(, ); cout << "The celebrity weighed "; incognito.show_stn(); cout << "The detective weighed "; wolfe.show_stn(); cout << "The President weighed "; taft.show_lbs(); incognito = 276.8; // use constructor for conversion; taft = ; cout << "After dinner, the celebrity weighed "; incognito.show_stn(); cout << "After dinner, the President weighed "; taft.show_lbs(); display(taft, ); cout << "The wrestler weighed even more.\n"; display(, ); cout << "No stone left unearned\n"; std::cin.get(); ; } void display(const Stonewt & st, int n) { ; i < n; i++) { cout << "Wow!"; st.show_stn(); } }
11.6.1 转换函数
#ifndef STONEWT_H_ #define STONEWT_H_ class Stonewt { private: }; int stone; double pds_left; double pounds; public: Stonewt(double lbs); Stonewt(int stn, double lbs); Stonewt(); ~Stonewt(); void show_lbs() const; void show_stn() const; operator int() const; operator double() const; }; #endif #include <iostream> #include "stonewt.h" using std::cout; Stonewt::Stonewt(double lbs) { stone = int(lbs) / Lbs_per_stn; pds_left = int(lbs) % Lbs_per_stn + lbs - int(lbs); pounds = lbs; } Stonewt::Stonewt(int stn, double lbs) { stone = stn; pds_left = lbs; pounds = stn * Lbs_per_stn + lbs; } Stonewt::Stonewt() { stone = pounds = pds_left = ; } Stonewt::~Stonewt() { } void Stonewt::show_stn() const { cout << stone << " stone, " << pds_left << " pounds\n"; } void Stonewt::show_lbs() const { cout << pounds << " pounds\n"; } Stonewt::operator int() const { return int(pounds + 0.5); } Stonewt::operator double() const { return pounds; } #include <iostream> #include "stonewt.h" int main() { using std::cout; Stonewt popins(, 2.8); double p_wt = popins; cout << "Convert to double => "; cout << "Poppins: " << p_wt << " pounds.\n"; cout << "Convert to int => "; cout << "Poppins: " << int(popins) << " pounds.\n"; std::cin.get(); ; }
11.6.2 转换函数和友元函数
11.7 总结
11.8 复习题
11.9 编程练习
第12章 类和动态内存分配
12.1 动态内存和类
12.1.1 复习示例和静态类成员
#ifndef STRINGBAD_H_ #define STRINGBAD_H_ #include <iostream> class StringBad { private: char * str; int len; static int num_strings; public: StringBad(const char * s); StringBad(); ~StringBad(); friend std::ostream & operator<<(std::ostream & os, const StringBad & st); }; #endif #define _CRT_SECURE_NO_WARNINGS #include <cstring> #include "stringbad.h" using std::cout; ; StringBad::StringBad(const char * s) { len = std::strlen(s); str = ]; std::strcpy(str, s); num_strings++; cout << num_strings << ": \"" << str << "\" object created\n"; } StringBad::StringBad() { len = ; str = ]; std::strcpy(str, "C++"); num_strings++; cout << num_strings << ": \"" << str << "\" default object created\n"; } StringBad::~StringBad() { cout << "\"" << str << "\" object deleted, "; --num_strings; cout << num_strings << " left\n"; delete[] str; } std::ostream & operator<<(std::ostream & os, const StringBad & st) { os << st.str; return os; } #include <iostream> #include "stringbad.h" using std::cout; void callme1(StringBad &); void callme2(StringBad); int main() { using std::endl; { cout << "Starting an inner block.\n"; StringBad headline1("Celery Stalks at Midnight"); StringBad headline2("Lettuce Prey"); StringBad sports("Spinach Leaves Bowl for Dollars"); cout << "headline1: " << headline1 << endl; cout << "headlien2: " << headline2 << endl; cout << "sports: " << sports << endl; callme1(headline1); cout << "headline1: " << headline1 << endl; callme2(headline2); cout << "headline2: " << headline2 << endl; cout << "Initialize one object to another:\n"; StringBad sailor = sports; cout << "sailor: " << sailor << endl; cout << "Assign one object to another:\n"; StringBad knot; knot = headline1; cout << "knot: " << knot << endl; cout << "Exiting the block.\n"; } cout << "End of main()\n"; std::cin.get(); ; } void callme1(StringBad & rsb) { cout << "String passed by reference:\n"; cout << " \"" << rsb << "\"\n"; } void callme2(StringBad sb) { cout << "String passed by value:\n"; cout << " \"" << sb << "\"\n"; }
12.1.2 特殊成员函数
C++自动提供了下面这些成员函数
默认构造函数,如果没有定义构造函数
默认析构函数,如果没有定义
复制构造函数,如果没有定义
赋值运算符,如果没有定义
地址运算符,如果没有定义
更准确地说,编译器将生成上述最后三个函数的定义----如果程序使用对象的方式要求这样做.例如,如果您将一个对象赋给另一个对象,编译器将提供赋值运算符的定义
隐式地址运算符返回调用对象的地址(即this指针的值)
C++11提供了另外两个特殊成员函数:移动构造函数(move constructor)和移动赋值运算符(move assignment operator)
1. 默认构造函数
如果没有提供任何构造函数,C++将创建默认构造函数.不接受任何参数,也不执行任何操作.它的值在初始化时是未知的.带参数的构造函数也可以是默认构造函数,只要所有参数都有默认值.但只能有一个默认构造函数.
2. 复制构造函数
复制构造函数用于将一个对象复制到新创建的对象中.也就是说,它用于初始化过程中(包括按值传递参数),而不是常规的赋值过程中.对于复制构造函数,需要知道两点:何时调用和有何功能
新建一个对象并将其初始化为同类现有对象时,复制构造函数都将被调用.这在很多情况下都可能发生,最常见的情况是将新对象显式地初始化为现有的对象.
StringBad ditto(motto); // calls StringBad(const StringBad &) StringBad metoo = motto); // calls StringBad(const StringBad &) StringBad also = StringBad(motto); // calls StringBad(const StringBad &) StringBad * pStringBad = new StringBad(motto); // calls StringBad(const StringBad &)
其中中间的2种声明可能会使用复制构造函数直接创建metoo和also,也可能使用复制构造函数生成一个临时对象,然后将临时对象的内容赋给metoo和also,这取决于具体的实现.最后一种声明使用motto初始化一个匿名对象,并将新对象的地址赋给pstring指针
每当程序生成了对象副本时,编译器都将使用复制构造函数.具体地说,当函数按值传递对象或函数返回对象时,都将使用复制构造函数.记住,按值传递意味着创建原始变量的一个副本.编译器生成临时对象时,也将使用复制构造函数.
默认的复制构造函数逐个复制非静态成员(成员复制也称为浅复制),复制的是成员的值.
12.1.3 回到Stringbad: 复制构造函数的哪里出了问题
12.1.4 Stringbad的其他问题: 赋值运算符
ANSI C允许结构赋值,而C++允许类对象赋值,这是通过自动为类重载赋值运算符实现的.将已有的对象赋给另一个对象时,将使用重载的赋值运算符
12.2 改进后的新String类
12.2.1 修订后的默认构造函数
C++11空指针
在C++98时,字面值0有两个含义:可以表示数字值零,也可以表示空指针,这使得阅读程序的人和编译器难以区分.有些程序员使用(void *) 0来标识空指针(空指针本身的内部表示可能不是零),还有些程序员使用NULL,这是一个表示空指针的C语言宏.
C++11提供了更好的解决方案:引入新关键字nullptr,用于表示空指针.您仍可像以前一样使用0----否则大量现有的代码将非法,但建议您使用nullptr:
str = nullptr; // C++11 null pointer notation
12.2.2 比较成员函数
12.2.3 使用中括号表示法访问字符
12.2.4 静态类成员和函数
12.2.5 进一步重载赋值运算符
#ifndef STRING1_H_ #define STRING1_H_ #include <iostream> using std::ostream; using std::istream; class String { private: char * str; // pointer to string int len; // length of string static int num_strings; // number of objects ; // cin input limit public: // constructor and other methods String(const char * s); // constructor String(); // default constructor String(const String &); // copy constructor ~String(); // destructor int length() const { return len; } // overloaded operator methods String & operator=(const String &); String & operator=(const char *); char & operator[](int i); const char & operator[](int i) const; // overloaded operator friends friend bool operator<(const String &st, const String &st2); friend bool operator>(const String &st1, const String &st2); friend bool operator==(const String &st1, const String &st2); friend ostream & operator<<(ostream & os, const String &st); friend istream & operator>>(istream & is, String & st); // static function static int HowMany(); }; #endif #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include "string1.h" using std::cin; using std::cout; ; int String::HowMany() { return num_strings; } String::String(const char * s) { len = std::strlen(s); str = ]; std::strcpy(str, s); num_strings++; } String::String() { len = ; str = ]; str[] = '\0'; num_strings++; } String::String(const String & st) { num_strings++; len = st.len; str = ]; std::strcpy(str, st.str); } String::~String() { --num_strings; delete[] str; } String & String::operator=(const String & st) { if (this == &st) { return *this; } delete[] str; len = st.len; str = ]; std::strcpy(str, st.str); return *this; } String & String::operator=(const char * s) { delete[] str; len = std::strlen(s); str = ]; std::strcpy(str, s); return *this; } char & String::operator[](int i) { return str[i]; } const char & String::operator[](int i) const { return str[i]; } bool operator<(const String & st1, const String & st2) { ); } bool operator>(const String & st1, const String & st2) { return st2 < st1; } bool operator==(const String & st1, const String & st2) { ); } ostream & operator<<(ostream & os, const String & st) { os << st.str; return os; } istream & operator>>(istream & is, String & st) { char temp[String::CINLIM]; is.get(temp, String::CINLIM); if (is) { st = temp; } while (is && is.get() != '\n') { continue; } return is; } #include <iostream> #include "string1.h" ; ; int main() { using std::cout; using std::cin; using std::endl; String name; cout << "Hi, what's your name?\n>>"; cin >> name; cout << name << ", please enter up to " << ArSize << " short saying <empty line to quit>:\n"; String sayings[ArSize]; char temp[MaxLen]; int i; ; i < ArSize; i++) { cout << i + << ": "; cin.get(temp, MaxLen); while (cin && cin.get() != '\n') { continue; } ] == '\0') { break; } else { sayings[i] = temp; } } int total = i; ) { cout << "Here are your sayings:\n"; ; i < total; i++) { cout << sayings[i][] << ": " << sayings[i] << endl; } ; ; ; i < total; i++) { if (sayings[i].length() < sayings[shortest].length()) { shortest = i; } if (sayings[i] < sayings[first]) { first = i; } } cout << "Shortest saying:\n" << sayings[shortest] << endl; cout << "First alphabetically:\n" << sayings[first] << endl; cout << "This program used " << String::HowMany() << " String objects. Bye.\n"; } else { cout << "No input! Bye.\n"; } cin.get(); cin.get(); ; }
12.3 在构造函数中使用new时应该注意的事项
NULL,0还是nullptr:以前,空指针可以用0或NULL(在很多头文件中,NULL是一个被定义为0的符号常量)来表示.C程序员通常使用NULL而不是0,以指出这是一个指针,就像使用'\0'而不是0来表示空字符,以指出这是一个字符一样.然而,C++传统上更喜欢简单的0,而不是等价的NULL.但正如前面指出的,C++11提供了关键字nullptr,这是一种更好的选择
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运算符
#include <iostream> #include <string> #include <new> using namespace std; ; class JustTesting { private: string words; int number; public: JustTesting() { words = s; number = n; cout << words << " constructed\n"; } ~JustTesting() { cout << words << " destroyed\n"; } void Show() const { cout << words << ", " << number << endl; } }; int main() { char * buffer = new char[BUF]; // get block of memory JustTesting * pc1, * pc2; pc1 = new (buffer) JustTesting; // place object in buffer pc2 = ); // place object on heap cout << "Memory block address:\n" << "buffer: " << (void *)buffer << " heap:" << pc2 << endl; cout << "Memory contents:\n"; cout << pc1 << ": "; pc1->Show(); cout << pc2 << ": "; pc2->Show(); JustTesting * pc3, * pc4; pc3 = ); pc4 = ); cout << "Memory contents:\n"; cout << pc3 << ": "; pc3->Show(); cout << pc4 << ": "; pc4->Show(); delete pc2; // free Heap1 delete pc4; // free Heap2 pc3->~JustTesting(); // destroy object pointed to by pc3 pc1->~JustTesting(); // destroy object pointed to by pc1 delete[] buffer; // free buffer; cout << "Done\n"; cin.get(); ; }
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模拟
#ifndef QUEUE_H_ #define QUEUE_H_ class Customer { private: long arrive; //arrival time for customer int processtime; // processing time for customer public: Customer() { arrive = processtime = ; } void set(long when); long when() const { return arrive; } int ptime() const { return processtime; } }; typedef Customer Item; class Queue { private: // class scope definitions // Node is a nested structure definition local to this c struct Node { Item item; struct Node * next; }; enum { Q_SIZE = }; // private class members Node * front; // pointer to front of Queue Node * rear; // pointer to rear of Queue int items; // current number of items in Queue const int qsize; // maximum number of items in Queue // preemptive definitions to prevent public copying Queue() {} Queue & operator=(const Queue & q) { return *this; } public: Queue(int qs = Q_SIZE); // create queue with a qs limit ~Queue(); bool isempty() const; bool isfull() const; int queuecount() const; bool enqueue(const Item & item); // add item to end bool dequeue(Item & item); // remove item from front }; #endif #include "queue.h" #include <cstdlib> Queue::Queue(int qs) : qsize(qs) { front = rear = NULL; // or nullptr items = ; } Queue::~Queue() { Node * temp; while (front != NULL) { // while queue is not yet empty temp = front; // save address of front item front = front->next; // reset pointer to next item delete temp; // delete former front } } bool Queue::isempty() const { ; } bool Queue::isfull() const { return items == qsize; } int Queue::queuecount() const { return items; } // Add item to queue bool Queue::enqueue(const Item & item) { if (isfull()) { return false; } Node * add = new Node; // create node // on failure, new throws std::bad_alloc exception add->item = item; // set node pointers add->next = NULL; // or nullptr items++; if (front == NULL) { // if queue is empty, place item at front front = add; } else { rear->next = add; // else place at rear } rear = add; return true; } // Place front item into item variable and remove from queue bool Queue::dequeue(Item & item) { if (front == NULL) { return false; } item = front->item; // set item to first item in queue items--; Node * temp = front; // save location of first item front = front->next; // reset front to next item delete temp; // delete former first item ) { rear = NULL; } return true; } // time set to a random value in the range 1 - 3 void Customer::set(long when) { processtime = std::rand() % + ; arrive = when; } #include <iostream> #include <cstdlib> #include <ctime> #include "queue.h" ; bool newcustomer(double x); // is there a new customer? int main() { using std::cin; using std::cout; using std::endl; using std::ios_base; //setting things up std::srand(std::time()); // random initializing of rand() cout << "Case Study: Bank of Heather Automatic Teller\n"; cout << "Enter maximum size of queue: "; int qs; cin >> qs; Queue line(qs); // line queue holds up to qs people cout << "Enter the number of simulation hours: "; int hours; // hours of simulation cin >> hours; // simulation will run 1 cycle per minute long cyclelimit = MIN_PER_HR * hours; // # of cycles cout << "Enter the average number of custormers per hour: "; double perhour; // average # of arrival per hour cin >> perhour; double min_per_cust; // average time between arrivals min_per_cust = MIN_PER_HR / perhour; Item temp; // new customer data ; // turned away by full queue ; // joined the queue ; // served during the simulation ; // cumulative line length ; // time until autoteller is free ; // cumulative time in line // running the simulation ; cycle < cyclelimit; cycle++) { if (newcustomer(min_per_cust)) { // have newcomer if (line.isfull()) { turnaways++; } else { customers++; temp.set(cycle); // cycle = time of arrival line.enqueue(temp); // add newcomer to line } } && !line.isempty()) { line.dequeue(temp); // attend next customer wait_time = temp.ptime(); // for wait_time minutes line_wait += cycle - temp.when(); served++; } ) { wait_time--; } sum_line += line.queuecount(); } // reporting results ) { cout << "customers accepted: " << customers << endl; cout << " customers served: " << served << endl; cout << " turnaways: " << turnaways << endl; cout << "average queue size: "; cout.precision(); cout.setf(ios_base::fixed, ios_base::floatfield); cout << (double)sum_line / cyclelimit << endl; cout << " average wait time: " << (double)line_wait / served << " minutes\n"; } else { cout << "No customers!\n"; } cout << "Done!\n"; cin.get(); cin.get(); } // x = average time, in minutes, between customers // return value is true if customer shows up this minute bool newcustomer(double x) { ); }
12.8 总结
12.9 复习题
12.10 编程练习
第13章 类继承
13.1 一个简单的基类
#ifndef TABTENN0_H_ #define TABTENN0_H_ #include <string> using std::string; class TableTennisPlayer { private: string firstname; string lastname; bool hasTable; public: TableTennisPlayer(const string & fn = "none", const string & ln = "none", bool ht = false); void Name() const; bool HasTable() const { return hasTable; } void ResetTable(bool v) { hasTable = v; } }; #endif #include "tabtenn0.h" #include <iostream> TableTennisPlayer::TableTennisPlayer(const string & fn, const string & ln, bool ht) : firstname(fn), lastname(ln), hasTable(ht) { } void TableTennisPlayer::Name() const { std::cout << lastname << ", " << firstname; } #include <iostream> #include "tabtenn0.h" int main() { using std::cout; TableTennisPlayer player1("Chuck", "Blizzard", true); TableTennisPlayer player2("Tara", "Boomdea", false); player1.Name(); if (player1.HasTable()) { cout << ": has a table.\n"; } else { cout << ": hasn't a table.\n"; } player2.Name(); if (player2.HasTable()) { cout << ": has a table"; } else { cout << ": hasn't a table.\n"; } std::cin.get(); }
13.1.1 派生一个类
13.1.2 构造函数:访问权限的考虑
13.1.3 使用派生类
#ifndef TABTENN1_H_ #define TABTENN1_H_ #include <string> using std::string; class TableTennisPlayer { private: string firstname; string lastname; bool hasTable; public: TableTennisPlayer(const string & fn = "none", const string & ln = "none", bool ht = false); void Name() const; bool HasTable() const { return hasTable; } void ResetTable(bool v) { hasTable = v; } }; class RatedPlayer : public TableTennisPlayer { private: unsigned int rating; public: RatedPlayer(unsigned , const string & fn = "none", const string & ln = "none", bool ht = false); RatedPlayer(unsigned int r, const TableTennisPlayer & tp); unsigned int Rating() const { return rating; } void ResetRating(unsigned int r) { rating = r; } }; #endif #include "tabtenn1.h" #include <iostream> TableTennisPlayer::TableTennisPlayer(const string & fn, const string & ln, bool ht) : firstname(fn), lastname(ln), hasTable(ht) {} void TableTennisPlayer::Name() const { std::cout << lastname << ", " << firstname; } RatedPlayer::RatedPlayer(unsigned int r, const string & fn, const string & ln, bool ht) : TableTennisPlayer(fn, ln, ht) { rating = r; } RatedPlayer::RatedPlayer(unsigned int r, const TableTennisPlayer & tp) : TableTennisPlayer(tp), rating(r) { } #include <iostream> #include "tabtenn1.h" int main() { using std::cout; using std::endl; TableTennisPlayer player1("Tara", "Boomdea", false); RatedPlayer rplayer1(, "Mallory", "Duck", true); rplayer1.Name(); if (rplayer1.HasTable()) { cout << ": has a table.\n"; } else { cout << ": hasn't a table.\n"; } player1.Name(); if (player1.HasTable()) { cout << ": has a table.\n"; } else { cout << ": hasn't a table.\n"; } cout << "Name: "; rplayer1.Name(); cout << "; Rating: " << rplayer1.Rating() << endl; RatedPlayer rplayer2(, player1); cout << "Name: "; rplayer2.Name(); cout << "; Rating: " << rplayer2.Rating() << endl; std::cin.get(); ; }
13.1.4 派生类和基类之间的特殊关系
13.2 继承: is-a 关系
13.3 多态公有继承
13.3.1 开发Brass类和BrassPlus类
#ifndef BRASS_H_ #define BRASS_H_ #include <string> class Brass { private: std::string fullName; long acctNum; double balance; public: Brass(, double bal = 0.0); void Deposit(double amt); virtual void Widthdraw(double amt); double Balance() const; virtual void ViewAcct() const; virtual ~Brass() {} }; class BrassPlus : public Brass { private: double maxLoan; double rate; double owesBank; public: BrassPlus(, , double r = 0.11125); BrassPlus(, double r = 0.11125); virtual void ViewAcct() const; virtual void Withdraw(double amt); void ResetMax(double m) { maxLoan = m; } void ResetRate(double r) { rate = r; } void ResetOwes() { owesBank = ; } }; #endif #include <iostream> #include "brass.h" using std::cout; using std::endl; using std::string; typedef std::ios_base::fmtflags format; typedef std::streamsize precis; format setFormat(); void restore(format f, precis p); Brass::Brass(const string & s, long an, double bal) { fullName = s; acctNum = an; balance = bal; } void Brass::Deposit(double amt) { ) { cout << "Negative deposit not allowed; " << "deposit is cancelled.\n"; } else { balance += amt; } } void Brass::Widthdraw(double amt) { format initialState = setFormat(); precis prec = cout.precision(); ) { cout << "Withdrawal amount must be positive; " << "withdrawal canceled.\n"; } else if (amt <= balance) { balance -= amt; } else { cout << "Withdrawal amount of $" << amt << " exceeds your balance.\n" << "Withdrawal canceled.\n"; } restore(initialState, prec); } double Brass::Balance() const { return balance; } void Brass::ViewAcct() const { format initialState = setFormat(); precis prec = cout.precision(); cout << "Client: " << fullName << endl; cout << "Account Number: " << acctNum << endl; cout << "Balance: $" << balance << endl; restore(initialState, prec); } BrassPlus::BrassPlus(const string & s, long an, double bal, double ml, double r) : Brass(s, an, bal) { maxLoan = ml; owesBank = 0.0; rate = r; } BrassPlus::BrassPlus(const Brass & ba, double ml, double r) : Brass(ba) { maxLoan = ml; owesBank = 0.0; rate = r; } void BrassPlus::ViewAcct() const { format initialState = setFormat(); precis prec = cout.precision(); Brass::ViewAcct(); cout << "Maximum loan: $" << maxLoan << endl; cout << "Owed to bank: $" << owesBank << endl; cout.precision(); cout << * rate << "%\n"; restore(initialState, prec); } void BrassPlus::Withdraw(double amt) { format initialState = setFormat(); precis prec = cout.precision(); double bal = Balance(); if (amt <= bal) { Brass::Widthdraw(amt); } else if (amt <= bal + maxLoan - owesBank) { double advance = amt - bal; owesBank += advance * (1.0 + rate); cout << "Bank advance: $" << advance << endl; cout << "Finace charge: $" << advance * rate << endl; Deposit(advance); Brass::Widthdraw(amt); } else { cout << "Credit limit exceeded. Transaction cancelled.\n"; } restore(initialState, prec); } format setFormat() { return cout.setf(std::ios_base::fixed, std::ios_base::floatfield); } void restore(format f, precis p) { cout.setf(f, std::ios_base::floatfield); cout.precision(p); } #include <iostream> #include "brass.h" int main() { using std::cout; using std::endl; Brass Piggy(, 4000.00); BrassPlus Hoggy(, 3000.00); Piggy.ViewAcct(); cout << endl; Hoggy.ViewAcct(); cout << endl; cout << "Depositing $1000 into the Hogg Account:\n"; Hoggy.Deposit(1000.00); cout << "New balance: $" << Hoggy.Balance() << endl; cout << "Withdrawing $4200 from the Pigg Account:\n"; Piggy.Widthdraw(4200.00); cout << "Pigg account balance: $" << Piggy.Balance() << endl; cout << "Withdrawing $4200 from the Hogg Account:\n"; Hoggy.Withdraw(4200.00); Hoggy.ViewAcct(); std::cin.get(); std::cin.get(); ; }
13.4 静态联编和动态联编
将源代码种的函数调用解释为执行特定的函数代码块被称为函数名联编(binding).
在编译过程种进行联编被称为静态联编(static binding),又称为早期联编(early binding).然而,虚函数使这项工作变得更困难.
所以,编译器必须生成能够在程序运行时选择正确的虚方法的代码,这被称为动态联编(dynamic binding),又称为晚期联编(late binding)
13.4.1 指针和引用类型的兼容性
将派生类引用或指针转换为基类引用或指针被称为向上强制转换(upcasting),这使公有继承不需要进行显式类型转换.
相反的过程----将基类指针或引用转换为派生类指针或引用----称为向下强制转换(downcasting).如果不使用显式类型转换,则向下强制转换是不允许的.原因是is-a关系通常是不可逆的.
13.4.2 虚成员函数和动态联编
13.4.3 有关虚函数注意事项
13.5 访问控制:protected
13.6 抽象基类
13.6.1 应用ABC概念
#ifndef ACCTABC_H_ #define ACCTABC_H_ #include <iostream> #include <string> class AcctABC { private: std::string fullName; long acctNum; double balance; protected: struct Formatting { std::ios_base::fmtflags flag; std::streamsize pr; }; const std::string & FullName() const { return fullName; } long AcctNum() const { return acctNum; } Formatting SetFormat() const; void Restore(Formatting & f) const; public: AcctABC(, double bal = 0.0); void Deposit(double amt); ; // pure virtual function double Balance() const { return balance; }; ; // pure virtual function virtual ~AcctABC() {} }; class Brass : public AcctABC { public: Brass(, double bal = 0.0) : AcctABC(s, an, bal) {} virtual void Withdraw(double amt); virtual void ViewAcct() const; virtual ~Brass() {} }; class BrassPlus : public AcctABC { private: double maxLoan; double rate; double owesBank; public: BrassPlus(, , double r = 0.10); BrassPlus(, double r = 0.1); virtual void ViewAcct() const; virtual void Withdraw(double amt); void ResetMax(double m) { maxLoan = m; } void ResetRate(double r) { rate = r; } ; } }; #endif #include <iostream> #include "acctabc.h" using std::cout; using std::ios_base; using std::endl; using std::string; AcctABC::AcctABC(const string & s, long an, double bal) { fullName = s; acctNum = an; balance = bal; } void AcctABC::Deposit(double amt) { ) { cout << "Negative deposit not allowed; " << "deposit is cancelled.\n"; } else { balance += amt; } } void AcctABC::Withdraw(double amt) { balance -= amt; } AcctABC::Formatting AcctABC::SetFormat() const { Formatting f; f.flag = cout.setf(ios_base::fixed, ios_base::floatfield); f.pr = cout.precision(); return f; } void AcctABC::Restore(Formatting & f) const { cout.setf(f.flag, ios_base::floatfield); cout.precision(f.pr); } void Brass::Withdraw(double amt) { ) { cout << "Withdrawal amount must be positive: " << "withdrawal canceled.\n"; } else if (amt <= Balance()) { AcctABC::Withdraw(amt); } else { cout << "Withdrawal amount of $" << amt << " exceeds your balance.\n" << "Withdrawal canceld.\n"; } } void Brass::ViewAcct() const { Formatting f = SetFormat(); cout << "Brass Client: " << FullName() << endl; cout << "Account Number: " << AcctNum() << endl; cout << "Balance: $" << Balance() << endl; Restore(f); } BrassPlus::BrassPlus(const string & s, long an, double bal, double ml, double r) : AcctABC(s, an, bal) { maxLoan = ml; owesBank = 0.0; rate = r; } BrassPlus::BrassPlus(const Brass & ba, double ml, double r) : AcctABC(ba) { maxLoan = ml; owesBank = 0.0; rate = r; } void BrassPlus::ViewAcct() const { Formatting f = SetFormat(); cout << "BrassPlus Client: " << FullName() << endl; cout << "Account Number: " << AcctNum() << endl; cout << "Balance: $" << Balance() << endl; cout << "Maximum loan: $" << maxLoan << endl; cout << "Owed to bank: $" << owesBank << endl; cout.precision(); cout << * rate << "%\n"; Restore(f); } void BrassPlus::Withdraw(double amt) { Formatting f = SetFormat(); double bal = Balance(); if (amt <= bal) { AcctABC::Withdraw(amt); } else if (amt <= bal + maxLoan - owesBank) { double advance = amt - bal; owesBank += advance * (1.0 + rate); cout << "Bank advance: $" << advance << endl; cout << "Finance charge: $" << advance * rate << endl; Deposit(advance); AcctABC::Withdraw(amt); } else { cout << "Credit limit exceeded. Transaction cancelled.\n"; } Restore(f); } #include <iostream> #include <string> #include "acctabc.h" ; int main() { using std::cin; using std::cout; using std::endl; AcctABC * p_clients[CLIENTS]; std::string temp; long tempnum; double tempbal; char kind; ; i < CLIENTS; i++) { cout << "Enter client's name: "; std::getline(cin, temp); cout << "Enter client's account number: "; cin >> tempnum; cout << "Enter opening balance: $"; cin >> tempbal; cout << "Enter 1 for Brass Account or " << "2 for BrassPlus Account: "; ')) { cout << "Enter either 1 or 2: "; } ') { p_clients[i] = new Brass(temp, tempnum, tempbal); } else { double tmax, trate; cout << "Enter the overdraft limit: $"; cin >> tmax; cout << "Enter the interest rate " << "as a decimal fraction: "; cin >> trate; p_clients[i] = new BrassPlus(temp, tempnum, tempbal, tmax, trate); } while (cin.get() != '\n') { continue; } } cout << endl; ; i < CLIENTS; i++) { p_clients[i]->ViewAcct(); cout << endl; } ; i < CLIENTS; i++) { delete p_clients[i]; } cout << "Done.\n"; cin.get(); cin.get(); }
13.6.2 ABC概念
13.7 继承和动态内存分配
13.7.1 第一种情况:派生类不使用new
13.7.2 第二种情况:派生类使用new
13.7.3 使用动态内存分配和友元的继承示例
#ifndef DMA_H_ #define DMA_H_ #include <iostream> class baseDMA { private: char * label; int rating; public: baseDMA(); baseDMA(const baseDMA & rs); virtual ~baseDMA(); baseDMA & operator=(const baseDMA & rs); friend std::ostream & operator<<(std::ostream & os, const baseDMA & rs); }; // derived class without DMA // no destructor needed // uses implicit copy constructor // uses implicit assignment operator class lacksDMA : public baseDMA { private: }; char color[COL_LEN]; public: lacksDMA(); lacksDMA(const char * c, const baseDMA & rs); friend std::ostream & operator<<(std::ostream & os, const lacksDMA & rs); }; class hasDMA : public baseDMA { private: char * style; public: hasDMA(); hasDMA(const char * s, const baseDMA & rs); hasDMA(const hasDMA & hs); ~hasDMA(); hasDMA & operator=(const hasDMA & rs); friend std::ostream & operator<<(std::ostream & os, const hasDMA & rs); }; #endif #define _CRT_SECURE_NO_WARNINGS #include <cstring> #include "dma.h" baseDMA::baseDMA(const char * l, int r) { label = ]; std::strcpy(label, l); rating = r; } baseDMA::baseDMA(const baseDMA & rs) { label = ]; std::strcpy(label, rs.label); rating = rs.rating; } baseDMA::~baseDMA() { delete[] label; } baseDMA & baseDMA::operator=(const baseDMA & rs) { if (this == &rs) { return *this; } delete[] label; label = ]; std::strcpy(label, rs.label); rating = rs.rating; return *this; } std::ostream & operator<<(std::ostream & os, const baseDMA & rs) { os << "Label: " << rs.label << std::endl; os << "Rating: " << rs.rating << std::endl; return os; } lacksDMA::lacksDMA(const char * c, const char * l, int r) : baseDMA(l, r) { std::strncpy(color, c, ); color[] = '\0'; } lacksDMA::lacksDMA(const char * c, const baseDMA & rs) : baseDMA(rs) { std::strncpy(color, c, COL_LEN - ); color[COL_LEN - ] = '\0'; } std::ostream & operator<<(std::ostream & os, const lacksDMA & ls) { os << (const baseDMA &)ls; os << "Color: " << ls.color << std::endl; return os; } hasDMA::hasDMA(const char * s, const char * l, int r) : baseDMA(l, r) { style = ]; std::strcpy(style, s); } hasDMA::hasDMA(const char * s, const baseDMA & rs) : baseDMA(rs) { style = ]; std::strcpy(style, s); } hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs) { style = ]; std::strcpy(style, hs.style); } hasDMA::~hasDMA() { delete[] style; } hasDMA & hasDMA::operator=(const hasDMA & hs) { if (this == &hs) { return *this; } baseDMA::operator=(hs); delete[] style; style = ]; std::strcpy(style, hs.style); return *this; } std::ostream & operator<<(std::ostream & os, const hasDMA & hs) { os << (const baseDMA &)hs; os << "Style: " << hs.style << std::endl; return os; } #include <iostream> #include "dma.h" int main() { using std::cout; using std::endl; baseDMA shirt(); lacksDMA balloon(); hasDMA map(); cout << "Displaying baseDMA object:\n"; cout << shirt << endl; cout << "Displaying lacksDMA object:\n"; cout << balloon << endl; cout << "Displaying hasDMA object:\n"; cout << map << endl; lacksDMA balloon2(balloon); cout << "Result of lacksDMA copy:\n"; cout << balloon2 << endl; hasDMA map2; map2 = map; cout << "Resulting of hasDMA assignment:\n"; cout << map2 << endl; std::cin.get(); std::cin.get(); ; }
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类示例
#ifndef STUDENTC_H_ #define STUDENTC_H_ #include <iostream> #include <string> #include <valarray> class Student { private: typedef std::valarray<double> ArrayDb; std::string name; ArrayDb scores; std::ostream & arr_out(std::ostream & os) const; public: Student() : name("Null Student"), scores() {} explicit Student(const std::string & s) : name(s), scores() {} explicit Student(int n) : name("Nullly"), scores(n) {} Student(const std::string & s, int n) : name(s), scores(n) {} Student(const std::string & s, const ArrayDb & a) : name(s), scores(a) {} Student(const char * str, const double * pd, int n) : name(str), scores(pd, n) {} ~Student() {} double Average() const; const std::string & Name() const; double & operator[](int i); double operator[](int i) const; friend std::istream & operator>>(std::istream & is, Student & stu); friend std::istream & getline(std::istream & is, Student & stu); friend std::ostream & operator<<(std::ostream & os, const Student & stu); }; #endif #include "studentc.h" using std::ostream; using std::endl; using std::istream; using std::string; double Student::Average() const { ) { return scores.sum() / scores.size(); } else { ; } } const string & Student::Name() const { return name; } double & Student::operator[](int i) { return scores[i]; } double Student::operator[](int i) const { return scores[i]; } ostream & Student::arr_out(ostream & os) const { int i; int lim = scores.size(); ) { ; i < lim; i++) { os << scores[i] << " "; == ) { os << endl; } } != ) { os << endl; } } else { os << " empty array "; } return os; } istream & operator>>(istream & is, Student & stu) { is >> stu.name; return is; } istream & getline(istream & is, Student & stu) { getline(is, stu.name); return is; } ostream & operator<<(ostream & os, const Student & stu) { os << "Scores for " << stu.name << ":\n"; stu.arr_out(os); return os; } #include <iostream> #include "studentc.h" using std::cin; using std::cout; using std::endl; void set(Student & sa, int n); ; ; int main() { Student ada[pupils] = { Student(quizzes), Student(quizzes), Student(quizzes) }; int i; ; i < pupils; ++i) { set(ada[i], quizzes); } cout << "\nStudent List:\n"; ; i < pupils; ++i) { cout << ada[i].Name() << endl; } cout << "\nResults:"; ; i < pupils; ++i) { cout << endl << ada[i]; cout << "average: " << ada[i].Average() << endl; } cout << "Done.\n"; cin.get(); cin.get(); ; } void set(Student & sa, int n) { cout << "Please enter the student's name: "; getline(cin, sa); cout << "Please enter " << n << " quiz scores:\n"; ; i < n; i++) { cin >> sa[i]; } while (cin.get() != '\n') { continue; } }
14.2 私有继承
14.2.1 Student类示例(新版本)
#ifndef STUDENTC_H_ #define STUDENTC_H_ #include <iostream> #include <valarray> #include <string> class Student : private std::string, private std::valarray<double> { private: typedef std::valarray<double> ArrayDb; std::ostream & arr_out(std::ostream & os) const; public: Student() : std::string("Null Student"), ArrayDb() {} explicit Student(const std::string & s) : std::string(s), ArrayDb() {} explicit Student(int n) : std::string("Nully"), ArrayDb(n) {} Student(const std::string & s, int n) : std::string(s), ArrayDb(n) {} Student(const std::string & s, const ArrayDb & a) : std::string(s), ArrayDb(a) {} Student(const char * str, const double * pd, int n) : std::string(str), ArrayDb(pd, n) {} ~Student() {} double Average() const; double & operator[](int i); double operator[](int i) const; const std::string & Name() const; friend std::istream & operator>>(std::istream & is, Student & stu); friend std::istream & getline(std::istream & is, Student & stu); friend std::ostream & operator<<(std::ostream & os, const Student & stu); }; #endif #include "studenti.h" using std::ostream; using std::endl; using std::istream; using std::string; double Student::Average() const { ) { return ArrayDb::sum() / ArrayDb::size(); } else { ; } } const string & Student::Name() const { return (const string &)*this; } double & Student::operator[](int i) { return ArrayDb::operator[](i); } double Student::operator[](int i) const { return ArrayDb::operator[](i); } ostream & Student::arr_out(ostream & os) const { int i; int lim = ArrayDb::size(); ) { ; i < lim; i++) { os << ArrayDb::operator[](i) << " "; == ) { os << endl; } } != ) { os << endl; } } else { os << " empty array "; } return os; } istream & operator>>(istream & is, Student & stu) { is >> (string &)stu; return is; } istream & getline(istream & is, Student & stu) { getline(is, (string &)stu); return is; } ostream & operator<<(ostream & os, const Student & stu) { os << "Scores for " << (const string &)stu << ":\n"; stu.arr_out(os); return os; } #include <iostream> #include "studenti.h" using std::cin; using std::cout; using std::endl; void set(Student & sa, int n); ; ; int main() { Student ada[pupils] = { Student(quizzes), Student(quizzes), Student(quizzes) }; int i; ; i < pupils; i++) { set(ada[i], quizzes); } cout << "\nStudent List:\n"; ; i < pupils; ++i) { cout << ada[i].Name() << endl; } cout << "\nResults:"; ; i < pupils; i++) { cout << endl << ada[i]; cout << "average: " << ada[i].Average() << endl; } cout << "Done.\n"; cin.get(); cin.get(); ; } void set(Student & sa, int n) { cout << "Please enter the student's name: "; getline(cin, sa); cout << "Please enter " << n << " quiz scores:\n"; ; i < n; i++) { cin >> sa[i]; } while (cin.get() != '\n') { continue; } }
14.2.2 使用包含还是私有继承
14.2.3 保护继承
14.2.4 使用using重新定义访问权限
14.3 多重继承
#ifndef WORKER0_H_ #define WORKER0_H_ #include <string> class Worker { private: std::string fullname; long id; public: Worker() : fullname("no one"), id(0L) {} Worker(const std::string & s, long n) : fullname(s), id(n) {} ; virtual void Set(); virtual void Show() const; }; class Waiter : public Worker { private: int panache; public: Waiter() : Worker(), panache() {} Waiter() : Worker(s, n), panache(p) {} Waiter() : Worker(wk), panache(p) {} void Set(); void Show() const; }; class Singer : public Worker { protected: enum { other, alto, contralto, soprano, bass, baritone, tenor }; }; private: static char * pv[Vtypes]; int voice; public: Singer() : Worker(), voice(other) {} Singer(const std::string & s, long n, int v = other) : Worker(s, n), voice(v) {} Singer(const Worker & wk, int v = other) : Worker(wk), voice(v) {} void Set(); void Show() const; }; #endif #include "worker0.h" #include <iostream> using std::cout; using std::cin; using std::endl; Worker::~Worker() {} void Worker::Set() { cout << "Enter worker's name: "; getline(cin, fullname); cout << "Enter worker's ID: "; cin >> id; while (cin.get() != '\n') { continue; } } void Worker::Show() const { cout << "Name: " << fullname << "\n"; cout << "Employee ID: " << id << "\n"; } void Waiter::Set() { Worker::Set(); cout << "Enter waiter's panache rating: "; cin >> panache; while (cin.get() != '\n') { continue; } } void Waiter::Show() const { cout << "Category: waiter\n"; Worker::Show(); cout << "Panache rating: " << panache << "\n"; } char * Singer::pv[] = { (char *)"other", (char *)"alto", (char *)"contralto", (char *)"soprano", (char *)"bass", (char *)"baritone", (char *)"tenor" }; void Singer::Set() { Worker::Set(); cout << "Enter number of singer's vocal range:\n"; int i; ; i < Vtypes; i++) { cout << i << ": " << pv[i] << " "; == ) { cout << endl; } } != ) { cout << endl; } || voice >= Vtypes)) { cout << "Please enter a value >= 0 and < " << Vtypes << endl; } while (cin.get() != '\n') { continue; } } void Singer::Show() const { cout << "Category: singer\n"; Worker::Show(); cout << "Vocal range: " << pv[voice] << endl; } #include <iostream> #include "worker0.h" ; int main() { Waiter bob(); Singer bev(); Waiter w_temp; Singer s_temp; Worker * pw[LIM] = { &bob, &bev, &w_temp, &s_temp }; int i; ; i < LIM; i++) { pw[i]->Set(); } ; i < LIM; i++) { pw[i]->Show(); std::cout << std::endl; } std::cin.get(); std::cin.get(); ; }
14.3.1 有多少Worker
14.3.2 哪个方法
14.3.3 MI小结
#ifndef WORKERMI_H_ #define WORKERMI_H_ #include <string> class Worker { private: std::string fullname; long id; protected: virtual void Data() const; virtual void Get(); public: Worker() : fullname("no one"), id(0L) {} Worker(const std::string & s, long n) : fullname(s), id(n) {} ; ; ; }; class Waiter : virtual public Worker { private: int panache; protected: void Data() const; void Get(); public: Waiter() : Worker(), panache() {} Waiter() : Worker(s, n), panache(p) { } Waiter() : Worker(wk), panache(p) {} void Set(); void Show() const; }; class Singer : virtual public Worker { protected: enum { other, alto, contralto, soprano, bass, baritone, tenor }; }; void Data() const; void Get(); private: static char *pv[Vtypes]; int voice; public: Singer() : Worker(), voice(other) {} Singer(const std::string & s, long n, int v = other) : Worker(s, n), voice(v) {} void Set(); void Show() const; }; class SingingWaiter : public Singer, public Waiter { protected: void Data() const; void Get(); public: SingingWaiter() {} SingingWaiter(, int v = other) : Worker(s,n), Waiter(s, n, p), Singer(s, n, v) {} SingingWaiter(, int v = other) : Worker(wk), Waiter(wk, p), Singer(wk, v) {} SingingWaiter(const Waiter & wt, int v = other) : Worker(wt), Waiter(wt), Singer(wt, v) {} SingingWaiter() : Worker(wt), Waiter(wt, p), Singer(wt) {} void Set(); void Show() const; }; #endif #include "workermi.h" #include <iostream> using std::cout; using std::cin; using std::endl; Worker::~Worker() {} void Worker::Data() const { cout << "Name: " << fullname << endl; cout << "Employee ID: " << id << endl; } void Worker::Get() { getline(cin, fullname); cout << "Enter worker's ID: "; cin >> id; while (cin.get() != '\n') { continue; } } void Waiter::Set() { cout << "Enter waiter's name: "; Worker::Get(); Get(); } void Waiter::Show() const { cout << "Category: waiter\n"; Worker::Data(); Data(); } void Waiter::Data() const { cout << "Panache rating: " << panache << endl; } void Waiter::Get() { cout << "Enter waiter's panache rating: "; cin >> panache; while (cin.get() != '\n') { continue; } } char * Singer::pv[Singer::Vtypes] = { (char *)"other", (char *)"alto", (char *)"contralto", (char *)"soprano", (char *)"bass", (char *)"baritone", (char *)"tenor" }; void Singer::Set() { cout << "Enter singer's name: "; Worker::Get(); Get(); } void Singer::Show() const { cout << "Category: singer\n"; Worker::Data(); Data(); } void Singer::Data() const { cout << "Vocal range: " << pv[voice] << endl; } void Singer::Get() { cout << "Enter number for singer's vocal range:\n"; int i; ; i < Vtypes; i++) { cout << i << ": " << pv[i] << " "; == ) { cout << endl; } } != ) { cout << '\n'; } cin >> voice; while (cin.get() != '\n') { continue; } } void SingingWaiter::Data() const { Singer::Data(); Waiter::Data(); } void SingingWaiter::Get() { Waiter::Get(); Singer::Get(); } void SingingWaiter::Set() { cout << "Enter singing waiter's name: "; Worker::Get(); Get(); } void SingingWaiter::Show() const { cout << "Category: singing waiter\n"; Worker::Data(); Data(); } #include <iostream> #include <cstring> #include "workermi.h" ; int main() { using std::cin; using std::cout; using std::endl; using std::strchr; Worker * lolas[SIZE]; int ct; ; ct < SIZE; ct++) { char choice; cout << "Enter the employee category:\n" << "w: waiter s:singer " << "t: singing waiter q: quit\n"; cin >> choice; while (strchr("wstq", choice) == NULL) { cout << "Please enter a w, s, t, or q: "; cin >> choice; } if (choice == 'q') { break; } switch (choice) { case 'w': lolas[ct] = new Waiter; break; case 's': lolas[ct] = new Singer; break; case 't': lolas[ct] = new SingingWaiter; break; } cin.get(); lolas[ct]->Set(); } cout << "\nHere is your staff:\n"; int i; ; i < ct; i++) { cout << endl; lolas[i]->Show(); } ; i < ct; i++) { delete lolas[i]; } cout << "Bye.\n"; cin.get(); cin.get(); ; }
14.4 类模板
14.4.1 定义类模板
#ifndef STACKTP_H_ #define STACKTP_H_ template <class Type> class Stack { private: }; Type items[MAX]; int top; public: Stack(); bool isempty(); bool isfull(); bool push(const Type & item); bool pop(Type & item); }; template <class Type> Stack<Type>::Stack() { top = ; } template <class Type> bool Stack<Type>::isempty() { ; } template <class Type> bool Stack<Type>::isfull() { return top == MAX; } template <class Type> bool Stack<Type>::push(const Type & item) { if (top < MAX) { items[top++] = item; return true; } else { return false; } } template <class Type> bool Stack<Type>::pop(Type & item) { ) { item = items[--top]; return true; } else { return false; } } #endif
14.4.2 使用模板类
#include <iostream> #include <string> #include <cctype> #include "stacktp.h" using std::cin; using std::cout; int main() { Stack<std::string> st; char ch; std::string po; cout << "Please enter A to add a purchase order,\n" << "P to process a PO, or Q to quit.\n"; while (cin >> ch && std::toupper(ch) != 'Q') { while (cin.get() != '\n') { continue; } if (!std::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"; ; }
14.4.3 深入探讨模板类
#ifndef STACKTP1_H_ #define STACKTP1_H_ template <class Type> class Stack { private: }; int stacksize; Type * items; int top; public: explicit Stack(int ss = SIZE); Stack(const Stack & st); ~Stack() { delete[] items; } bool isempty() { ; } bool isfull() { return top == stacksize; } bool push(const Type & item); bool pop(Type & item); Stack & operator=(const Stack & st); }; template <class Type> Stack<Type>::Stack() { items = new Type[stacksize]; } template <class Type> Stack<Type>::Stack(const Stack & st) { stacksize = st.stacksize; top = st.top; items = new Type[stacksize]; ; i < top; i++) { items[i] = st.items[i]; } } template <class Type> bool Stack<Type>::push(const Type & item) { if (top < stacksize) { items[top++] = item; return true; } else { return false; } } template <class Type> bool Stack<Type>::pop(Type & item) { ) { item = items[--top]; return true; } else { return false; } } template <class Type> Stack<Type> & Stack<Type>::operator=(const Stack<Type> & st) { if (this == &st) { return *this; } delete[] items; stacksize = st.stacksize; top = st.top; items = new Type[stacksize]; ; i < top; i++) { items[i] = st.items[i]; } return *this; } #endif #include <iostream> #include <cstdlib> #include <ctime> #include "stcktp1.h" ; int main() { std::srand(std::time()); std::cout << "Please enter stack size: "; int stacksize; std::cin >> stacksize; Stack<const char *> st(stacksize); const char * in[Num] = { " 1: Hank Gilgamesh", " 2: Kiki Ishtar", " 3: Betty Rocket", " 4: Ian Flagranti", " 5: Wolfgang Kibble", " 6: Portia Koop", " 7: Joy ALmondo", " 8: Xaverie Paprika", " 9: Juan Moore", "10: Misha Mache" }; const char * out[Num]; ; ; while (processed < Num) { if (st.isempty()) { st.push(in[nextin++]); } else if (st.isfull()) { st.pop(out[processed++]); } && nextin < Num) { st.push(in[nextin++]); } else { st.pop(out[processed++]); } } ; i < Num; i++) { std::cout << out[i] << std::endl; } std::cout << "Bye\n"; std::cin.get(); std::cin.get(); ; }
14.4.4 数组模板示例和非类型参数
#ifndef ARRAYTP_H_ #define ARRAYTP_H_ #include <iostream> #include <cstdlib> template <class T, int n> class ArrayTP { private: T ar[n]; public: ArrayTP() {}; explicit ArrayTP(const T & v); virtual T & operator[](int i); virtual T operator[](int i) const; }; template <class T, int n> ArrayTP<T, n>::ArrayTP(const T & v) { ; i < n; i++) { ar[i] = v; } } template <class T, int n> T & ArrayTP<T, n>::operator[](int i) { || i >= n) { std::cerr << "Error in array limits: " << i << " is out of range\n"; std::exit(EXIT_FAILURE); } return ar[i]; } template <class T, int n> T ArrayTP<T, n>::operator[](int i) const { || i >= n) { std::cerr << "Error in array limits: " << i << " is out of range\n"; std::exit(EXIT_FAILURE); } return ar[i]; } #endif
14.4.5 模板多功能性
#include <iostream> #include "arraytp.h" int main() { using std::cout; using std::endl; ArrayTP<> sums; ArrayTP<> aves; ArrayTP< ArrayTP<>, > twodee; int i, j; ; i < ; i++) { sums[i] = ; ; j < ; j++) { twodee[i][j] = (i + ) * (j + ); sums[i] += twodee[i][j]; } aves[i] = (; } ; i < ; i++) { ; j < ; j++) { cout.width(); cout << twodee[i][j] << ' '; } cout << ": sum = "; cout.width(); cout << sums[i] << ", average = " << aves[i] << endl; } cout << "Done.\n"; std::cin.get(); std::cin.get(); ; }
#include <iostream> #include <string> template <class T1, class T2> class Pair { private: T1 a; T2 b; public: T1 & first(); T2 & second(); T1 first() const { return a; } T2 second() const { return b; } Pair(const T1 & aval, const T2 & bval) : a(aval), b(bval) { } Pair() {} }; template<class T1, class T2> T1 & Pair<T1, T2>::first() { return a; } template<class T1, class T2> T2 & Pair<T1, T2>::second() { return b; } int main() { using std::cout; using std::endl; using std::string; Pair<] = { Pair<), Pair<), Pair<), Pair<) }; int joints = sizeof(ratings) / sizeof(Pair<string, int>); cout << "Rating:\t Eatery\n"; ; i < joints; i++) { cout << ratings[i].second() << ":\t" << ratings[i].first() << endl; } cout << "Oops! Revised rating:\n"; ratings[].first() = "Bertie's Fab Eats"; ratings[].second() = ; cout << ratings[].second() << ":\t " << ratings[].first() << endl; std::cin.get(); std::cin.get(); ; }
14.4.6 模板的具体化
14.4.7 成员模板
#include <iostream> using std::cout; using std::endl; template <typename T> class beta { private: template <typename V> class hold { private: V val; public: hold(V v = ) : val(v) {} void show() const { cout << val << endl; } V Value() const { return val; } }; hold<T> q; hold<int> n; public: beta(T t, int i) : q(t), n(i) {} template<typename U> U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; } void Show() const { q.show(); n.show(); } }; int main() { beta<); cout << "T was set to double\n"; guy.Show(); cout << "V was set to T, which is double, then V was set to int\n"; cout << guy.blab(, 2.3) << endl; cout << "U was set to int\n"; cout << guy.blab(10.0, 2.3) << endl; cout << "U was set to double\n"; cout << "Done\n"; std::cin.get(); ; }
14.4.8 将模板用作参数
#include <iostream> #include "stacktp.h" template <template <typename T> class Thing> class Crab { private: Thing<int> s1; Thing<double> s2; public: Crab() {}; bool push(int a, double x) { return s1.push(a) && s2.push(x); } bool pop(int & a, double & x) { return s1.pop(a) && s2.pop(x); } }; int main() { using std::cout; using std::cin; using std::endl; int ni; double nb; cout << "Enter int double pairs, such as 4 3.5 (0 0 to end):\n"; && nb > ) { if (!nebula.push(ni, nb)) { break; } } while (nebula.pop(ni, nb)) { cout << ni << ", " << nb << endl; } cout << "Done.\n"; ; }
14.4.9 模板类和友元
模板类声明也可以有友元.模板的友元分3类:
非模板友元
约束(bound)模板友元,即友元的类型取决于类被实例化时的类型
非约束(unbound)模板友元,即友元的所有具体化都是类的每一个具体化的友元
#include <iostream> using std::cout; using std::endl; template <typename T> class HasFriend { private: T item; public: static int ct; public: HasFriend(const T & i) : item(i) { ct++; } ~HasFriend() { ct--; } friend void counts(); friend void reports(HasFriend<T> &); }; template <typename T> ; void counts() { cout << "int count: " << HasFriend<int>::ct << "; "; cout << "double count: " << HasFriend<double>::ct << endl; } void reports(HasFriend<int> & hf) { cout << "HasFriend<int>: " << hf.item << endl; } void reports(HasFriend<double> & hf) { cout << "HasFriend<double>: " << hf.item << endl; } int main() { cout << "No objects declared: "; counts(); HasFriend<); cout << "After hfi1 declared: "; counts(); HasFriend<); cout << "After hfi2 declared: "; counts(); HasFriend<double> hfdb(10.5); cout << "After hfdb declared: "; counts(); reports(hfi1); reports(hfi2); reports(hfdb); std::cin.get(); }
#include <iostream> using std::cout; using std::endl; template <typename T> void counts(); template <typename T> void report(T &); template <typename TT> class HasFriendT { private: TT item; public: static int ct; public: HasFriendT(const TT & i) : item(i) { ct++; } ~HasFriendT() { ct--; } friend void counts<TT>(); friend void report<>(HasFriendT<TT> &); }; template <typename T> ; template <typename T> void counts() { cout << "template size: " << sizeof(HasFriendT<T>) << "; "; cout << "template counts(): " << HasFriendT<T>::ct << endl; } template <typename T> void report(T & hf) { cout << hf.item << endl; } int main() { counts<int>(); HasFriendT<); HasFriendT<); HasFriendT<double> hfdb(10.5); report(hfi1); report(hfi2); report(hfdb); cout << "counts<int>() output:\n"; counts<int>(); cout << "coiunts<double>() output:\n"; counts<double>(); std::cin.get(); ; }
#include <iostream> using std::cout; using std::endl; template <typename T> class ManyFriend { private: T item; public: ManyFriend(const T & i) : item(i) {} template <typename C, typename D> friend void show2(C &, D &); }; template <typename C, typename D> void show2(C & c, D & d) { cout << c.item << ", " << d.item << endl; } int main() { ManyFriend<); ManyFriend<); ManyFriend<double> hfdb(10.5); cout << "hfi1, hfi2: "; show2(hfi1, hfi2); cout << "hfdb, hfi2: "; show2(hfdb, hfi2); std::cin.get(); ; }
14.4.10 模板别名(C++11)
14.5 总结
14.6 复习题
14.7 编程练习
第15章 友元,异常和其他
15.1 友元
15.1.1 友元类
#ifndef TV_H_ #define TV_H_ class Tv { public: friend class Remote; enum { Off, On }; }; enum { Antenna, Cable }; enum { TV, DVD }; Tv() : state(s), volume(), maxchannel(mc), channel(), mode(Cable), input(TV) {} void onoff() { state = (state == On) ? Off : On; } bool ison() const { return state == On; } bool volup(); bool voldown(); void chanup(); void chandown(); void set_mode() { mode = (mode == Antenna) ? Cable : Antenna; } void set_input() { input = (input == TV) ? DVD : TV; } void settings() const; private: int state; int volume; int maxchannel; int channel; int mode; int input; }; class Remote { private: int mode; public: Remote(int m = Tv::TV) : mode(m) {} bool volup(Tv & t) { return t.volup(); } bool volddown(Tv & t) { return t.voldown(); } void onoff(Tv & t) { t.onoff(); } void chanup(Tv & t) { t.chanup(); } void chandown(Tv & t) { t.chandown(); } void set_chan(Tv & t, int c) { t.channel = c; } void set_mode(Tv & t) { t.set_mode(); } void set_input(Tv & t) { t.set_input(); } }; #endif
#include <iostream> #include "tv.h" bool Tv::volup() { if (volume < MaxVal) { volume++; return true; } else { return false; } } bool Tv::voldown() { if (volume > MinVal) { volume--; return true; } else { return false; } } void Tv::chanup() { if (channel < maxchannel) { channel++; } else { channel = ; } } void Tv::chandown() { ) { channel--; } else { channel = maxchannel; } } void Tv::settings() const { using std::cout; using std::endl; cout << "TV is " << (state == Off ? "Off" : "On") << endl; if (state == On) { cout << "Volume setting = " << volume << endl; cout << "Channel setting = " << channel << endl; cout << "Mode = " << (mode == Antenna ? "antenna" : "cable") << endl; cout << "Input = " << (input == TV ? "TV" : "DVD") << endl; } }
#include <iostream> #include "tv.h" int main() { using std::cout; Tv s42; cout << "Initial settings for 42\" TV:\n"; s42.settings(); s42.onoff(); s42.chanup(); cout << "\nAdjusted settings for 42\" TV:\n"; s42.chanup(); cout << "\nAdjusted settings for 42\" TV:\n"; s42.settings(); Remote grey; grey.set_chan(s42, ); grey.volup(s42); grey.volup(s42); cout << "\n42\" settings after using remote:\n"; s42.settings(); Tv s58(Tv::On); s58.set_mode(); grey.set_chan(s58, ); cout << "\n58\" settings:\n"; s58.settings(); std::cin.get(); ; }
15.1.2 友元成员函数
#ifndef TVFM_H_ #define TVFM_H_ class Tv; // forward declaration class Remote { public: enum State { Off, On }; }; enum { Antenna, Cable }; enum { TV, DVD }; private: int mode; public: Remote(int m = TV) : mode(m) {} bool volup(Tv & t); bool voldown(Tv & t); void onoff(Tv & t); void chanup(Tv & t); void chandown(Tv & t); void set_mode(Tv & t); void set_input(Tv & t); void set_chan(Tv & t, int c); }; class Tv { public: friend void Remote::set_chan(Tv & t, int c); enum State { Off, On }; }; enum { Antenna, Cable }; enum { TV, DVD }; Tv() : state(s), volume(), maxchannel(mc), channel(), mode(Cable), input(TV) {} void onoff() { state = (state == On) ? Off : On; } bool ison() const { return state == On; } bool volup(); bool voldown(); void chanup(); void chandown(); void set_mode() { mode = (mode == Antenna) ? Cable : Antenna; } void set_input() { input = (input == TV) ? DVD : TV; } void settings() const; private: int state; int volume; int maxchannel; int channel; int mode; int input; }; inline bool Remote::volup(Tv & t) { return t.volup(); } inline bool Remote::voldown(Tv & t) { return t.voldown(); } inline void Remote::onoff(Tv & t) { t.onoff(); } inline void Remote::chanup(Tv & t) { t.chanup(); } inline void Remote::chandown(Tv & t) { t.chandown(); } inline void Remote::set_mode(Tv & t) { t.set_mode(); } inline void Remote::set_input(Tv & t) { t.set_input(); } inline void Remote::set_chan(Tv & t, int c) { t.channel = c; } #endif
#include <iostream> #include "tvfm.h" bool Tv::volup() { if (volume < MaxVal) { volume++; return true; } else { return false; } } bool Tv::voldown() { if (volume > MinVal) { volume--; return true; } else { return false; } } void Tv::chanup() { if (channel < maxchannel) { channel++; } else { channel = ; } } void Tv::chandown() { ) { channel--; } else { channel = maxchannel; } } void Tv::settings() const { using std::cout; using std::endl; cout << "TV is " << (state == Off ? "Off" : "On") << endl; if (state == On) { cout << "Volume setting = " << volume << endl; cout << "Channel setting = " << channel << endl; cout << "Mode = " << (mode == Antenna ? "antenna" : "cable") << endl; cout << "Input = " << (input == TV ? "TV" : "DVD") << endl; } }
#include <iostream> #include "tvfm.h" int main() { using std::cout; Tv s42; cout << "Initial settings for 42\" TV:\n"; s42.settings(); s42.onoff(); s42.chanup(); cout << "\nAdjusted settings for 42\" TV:\n"; s42.chanup(); cout << "\nAdjusted settings for 42\" TV:\n"; s42.settings(); Remote grey; grey.set_chan(s42, ); grey.volup(s42); grey.volup(s42); cout << "\n42\" settings after using remote:\n"; s42.settings(); Tv s58(Tv::On); s58.set_mode(); grey.set_chan(s58, ); cout << "\n58\" settings:\n"; s58.settings(); std::cin.get(); ; }
15.1.3 其他友元关系
15.1.4 共同的友元
15.2 嵌套类
15.2.1 嵌套类和访问权限
15.2.2 模板中的嵌套
#ifndef QUEUETP_H_ #define QUEUETP_H_ template <class Item> class QueueTP { private: }; class Node { public: Item item; Node * next; Node() { } }; Node * front; Node * rear; int items; const int qsize; QueueTP() { } QueueTP & operator=(const QueueTP & q) { return *this; } public: QueueTP(int qs = Q_SIZE); ~QueueTP(); bool isempty() const { ; } bool isfull() const { return items == qsize; } int queuecount() const { return items; } bool enqueue(const Item & item); bool dequeue(Item & item); }; template <class Item> QueueTP<Item>::QueueTP(int qs) : qsize(qs) { front = rear = ; items = ; } template <class Item> QueueTP<Item>::~QueueTP() { Node * temp; ) { temp = front; front = front->next; delete temp; } } template <class Item> bool QueueTP<Item>::enqueue(const Item & item) { if (isfull()) { return false; } Node * add = new Node(item); items++; ) { front = add; } else { rear->next = add; } rear = add; return true; } template <class Item> bool QueueTP<Item>::dequeue(Item & item) { ) { return false; } item = front->item; items--; Node * temp = front; front = front->next; delete temp; ) { rear = ; } return true; } #endif
#include <iostream> #include <string> #include "queutp.h" int main() { using std::string; using std::cin; using std::cout; QueueTP<); string temp; while (!cs.isfull()) { cout << "Please enter your name. You will be " "served in the order of arrival.\n" "name: "; std::getline(cin, temp); cs.enqueue(temp); } cout << "The queue is full. Processing begins!\n"; while (!cs.isempty()) { cs.dequeue(temp); cout << "Now processing " << temp << "...\n"; } cin.get(); ; }
15.3 异常
15.3.1 调用abort()
#include <iostream> #include <cstdlib> double hmean(double a, double b); int main() { double x, y, z; std::cout << "Enter two numbers: "; while (std::cin >> x >> y) { z = hmean(x, y); std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl; std::cout << "Enter next set of numbers <q to quit>: "; } std::cout << "Bye!\n"; ; } double hmean(double a, double b) { if (a == -b) { std::cout << "untenable arguments to hmean()\n"; std::abort(); } return 2.0 * a * b / (a + b); }
15.3.2 返回错误码
#include <iostream> #include <cfloat> bool hmean(double a, double b, double * ans); int main() { double x, y, z; std::cout << "Enter two numbers: "; while (std::cin >> x >> y) { if (hmean(x, y, &z)) { std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl; } else { std::cout << "One value should not be the negative " << "of the other - try again.\n"; } std::cout << "Enter next set of numbers <q to quit>: "; } std::cout << "Bye!\n"; ; } bool hmean(double a, double b, double * ans) { if (a == -b) { *ans = DBL_MAX; return false; } else { *ans = 2.0 * a * b / (a + b); return true; } }
15.3.3 异常机制
#include <iostream> double hmean(double a, double b); int main() { double x, y, z; std::cout << "Enter two numbers: "; while (std::cin >> x >> y) { try { // start of try block z = hmean(x, y); } // end of try block catch (const char * s) { // start of exception handler std::cout << s << std::endl; std::cout << "Enter a new pair of numbers: "; continue; } // end of handler std::cout << "Harmonic mean of " << x << " and " << y << " is " << z << std::endl; std::cout << "Enter next set of numbers <q to quit>: "; } std::cout << "Bye!\n"; std::cin.get(); std::cin.get(); ; } double hmean(double a, double b) { if (a == b) { throw "bad hmean() arguments: a = -b not allowed"; } return 2.0 * a * b / (a + b); }
15.3.4 将对象用作异常类型
#include <iostream> #include <cmath> #include "exc_mean.h" double hmean(double a, double b); double gmean(double a, double b); int main() { using std::cout; using std::cin; using std::endl; double x, y, z; cout << "Enter two numbers: "; while (cin >> x >> y) { try { z = hmean(x, y); cout << "Harmonic mean of " << x << " and " << y << " is " << z << endl; cout << "Geometric mean of " << x << " and " << y << " is " << gmean(x, y) << endl; cout << "Enter next set of numbers <q to quit>: "; } catch (bad_hmean & bg) { bg.mesg(); cout << "Try agian.\n"; continue; } catch (bad_gmean & hg) { cout << hg.mesg(); cout << "Values used: " << hg.v1 << ", " << hg.v2 << endl; cout << "Sorry, you don't get to play any more.\n"; break; } } cout << "Bye!\n"; cin.get(); cin.get(); ; } double hmean(double a, double b) { if (a == -b) { throw bad_hmean(a, b); } return 2.0 * a * b / (a + b); } double gmean(double a, double b) { || b < ) { throw bad_gmean(a, b); } return std::sqrt(a * b); }
15.3.5 异常规范和C++11
15.3.6 栈解退
15.3.7 其他异常特性
15.3.8 exception类
#include <iostream> #include <new> #include <cstdlib> using namespace std; struct Big { ]; }; int main() { Big * pb; try { cout << "Trying to get a big block of memory:\n"; pb = ]; cout << "Got past the new request:\n"; } catch (bad_alloc & ba) { cout << "Caught the exception!\n"; cout << ba.what() << endl; exit(EXIT_FAILURE); } cout << "Memory successfully allocated\n"; pb[].stuff[] = ; cout << pb[].stuff[] << endl; delete[] pb; std::cin.get(); std::cin.get(); ; }
15.3.9 异常,类和继承
#include <stdexcept> #include <string> class Sales { public: }; class bad_index : public std::logic_error { private: int bi; public: explicit bad_index(int ix, const std::string & s = "Index error in Sales object\n"); int bi_val() const { return bi; } virtual ~bad_index() throw() {} }; ); Sales(int yy, const double * gr, int n); virtual ~Sales() {} int Year() const { return year; } virtual double operator[](int i) const; virtual double & operator[](int i); private: double gross[MONTHS]; int year; }; class LabeledSales : public Sales { public: class nbad_index : public Sales::bad_index { private: std::string lbl; public: nbad_index(const std::string & lb, int ix, const std::string & s = "Index error in LabeledSales object\n"); const std::string & label_val() const { return lbl; } virtual ~nbad_index() throw() {} }; ); LabeledSales(const std::string & lb, int yy, const double * gr, int n); virtual ~LabeledSales() {} const std::string & Label() const { return label; } virtual double operator[](int i) const; virtual double & operator[](int i); private: std::string label; }; #include "sales.h" using std::string; Sales::bad_index::bad_index(int ix, const string & s) : std::logic_error(s), bi(ix) { } Sales::Sales(int yy) { year = yy; ; i < MONTHS; ++i) { gross[i] = ; } } Sales::Sales(int yy, const double * gr, int n) { year = yy; int lim = (n < MONTHS) ? n : MONTHS; int i; ; i < lim; ++i) { gross[i] = gr[i]; } for (; i < MONTHS; ++i) { gross[i] = ; } } double Sales::operator[](int i) const { || i >= MONTHS) { throw bad_index(i); } return gross[i]; } double & Sales::operator[](int i) { || i >= MONTHS) { throw bad_index(i); } return gross[i]; } LabeledSales::nbad_index::nbad_index(const string & lb, int ix, const string & s) : Sales::bad_index(ix, s) { lbl = lb; } LabeledSales::LabeledSales(const string & lb, int yy) : Sales(yy) { label = lb; } LabeledSales::LabeledSales(const string & lb, int yy, const double * gr, int n) : Sales(yy, gr, n) { label = lb; } double LabeledSales::operator[](int i) const { || i >= MONTHS) { throw nbad_index(Label(), i); } return Sales::operator[](i); } double & LabeledSales::operator[](int i) { || i >= MONTHS) { throw nbad_index(Label(), i); } return Sales::operator[](i); } #include <iostream> #include "sales.h" int main() { using std::cout; using std::cin; using std::endl; ] = { , , , , , , , , , , , }; ] = { , , , , , , , , , , , }; Sales sales1(, vals1, ); LabeledSales sales2(, vals2, ); cout << "First try block:\n"; try { int i; cout << "Year = " << sales1.Year() << endl; ; i < ; ++i) { cout << sales1[i] << ' '; == ) { cout << endl; } } cout << "Year = " << sales1.Year() << endl; cout << "Label = " << sales2.Label() << endl; ; i <= ; ++i) { cout << sales2[i] << ' '; == ) { cout << endl; } } cout << "End of try block 1.\n"; } catch (LabeledSales::nbad_index & bad) { cout << bad.what(); cout << "Company: " << bad.label_val() << endl; cout << "bad index: " << bad.bi_val() << endl; } catch (Sales::bad_index & bad) { cout << bad.what(); cout << "bad index: " << bad.bi_val() << endl; } cout << "\nNext try block:\n"; try { sales2[] = 37.5; sales1[] = ; cout << "End of try block 2.\n"; } catch (LabeledSales::nbad_index & bad) { cout << bad.what(); cout << "Company: " << bad.label_val() << endl; cout << "bad index: " << bad.bi_val() << endl; } catch (Sales::bad_index & bad) { cout << bad.what(); cout << "bad index: " << bad.bi_val() << endl; } cout << "done\n"; cin.get(); cin.get(); ; }
15.3.10 异常何时会迷失方向
15.3.11 有关异常的注意事项
15.4 RTTI
RTTI是运行阶段类型识别(Runtime Type Identification)的简称
15.4.1 RTTI的用途
15.4.2 RTTI的工作原理
C++有3个支持RTTI的元素
如果可能的话,dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针;否则,该运算符返回0----空指针
typeid运算符返回一个指出对象的类型的值
type_info结构存储了有关特定类型的信息
只能将RTTI用于包含虚函数的类层次结构,原因在于只有对于这种类层次结构,才应该将派生对象的地址赋给基类指针
#include <iostream> #include <cstdlib> #include <ctime> using std::cout; class Grand { private: int hold; public: Grand() : hold(h) {} virtual void Speak() const { cout << "I am a grand class!\n"; } virtual int Value() const { return hold; } }; class Superb : public Grand { public: Superb() : Grand(h) {} void Speak() const { cout << "I am a superb class!!\n"; } virtual void Say() const { cout << "I hold the superb value of " << Value() << "!\n"; } }; class Magnificent : public Superb { private: char ch; public: Magnificent(, char c = 'A') : Superb(h), ch(c) {} void Speak() const { cout << "I am a magnificent class!!\n"; } void Say() const { cout << "I hold the characters " << ch << " and the integer " << Value() << "!\n"; } }; Grand * GetOne(); int main() { std::srand(std::time()); Grand * pg; Superb * ps; ; i < ; i++) { pg = GetOne(); pg->Speak(); if (ps = dynamic_cast<Superb *>(pg)) { ps->Say(); } } std::cin.get(); ; } Grand * GetOne() { Grand * p = nullptr; ) { : p = ); break; : p = ); break; : p = , ); break; } return p; }
#include <iostream> #include <cstdlib> #include <ctime> #include <typeinfo> using namespace std; class Grand { private: int hold; public: Grand() : hold(h) {} virtual void Speak() const { cout << "I am a grand class!\n"; } virtual int Value() const { return hold; } }; class Superb : public Grand { public: Superb() : Grand(h) {} void Speak() const { cout << "I am a superb class!!\n"; } virtual void Say() const { cout << "I hold the superb value of " << Value() << "!\n"; } }; class Magnificent : public Superb { private: char ch; public: Magnificent(, char c = 'A') : Superb(h), ch(c) {} void Speak() const { cout << "I am a magnificent class!!\n"; } void Say() const { cout << "I hold the characters " << ch << " and the integer " << Value() << "!\n"; } }; Grand * GetOne(); int main() { srand(time()); Grand * pg; Superb * ps; ; i < ; i++) { pg = GetOne(); cout << "Now processing type " << typeid(*pg).name() << ".\n"; pg->Speak(); if (ps = dynamic_cast<Superb *>(pg)) { ps->Say(); } if (typeid(Magnificent) == typeid(*pg)) { cout << "Yes, you're really magnificent.\n"; } } std::cin.get(); ; } Grand * GetOne() { Grand * p = nullptr; ) { : p = ); break; : p = ); break; : p = , ); break; } return p; }
15.5 类型转换运算符
#include <iostream> using std::cout; using std::endl; void change(const int * pt, int n); int main() { ; ; cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl; change(&pop1, -); change(&pop2, -); cout << "pop1, pop2: " << pop1 << ", " << pop2 << endl; std::cin.get(); ; } void change(const int * pt, int n) { int * pc; pc = const_cast<int *>(pt); *pc += n; }
15.6 总结
15.7 复习题
15.8 编程练习
第16章 string类和标准模板库
16.1 string类
16.1.1 构造字符串
#include <iostream> #include <string> int main() { using namespace std; string one("Lottery Winner!"); cout << one << endl; , '$'); cout << two << endl; string three(one); cout << three << endl; one += " Oops!"; cout << one << endl; two = "Sorry! That was "; three[] = 'P'; string four; four = two + three; char alls[] = "All's well that ends well"; ); cout << five << "!\n"; , alls + ); cout << six << ", "; ], &five[]); cout << seven << "...\n"; , ); cout << eight << " in motion!" << endl; std::cin.get(); ; }
16.1.2 string类输入
#include <iostream> #include <fstream> #include <string> #include <cstdlib> int main() { using namespace std; ifstream fin; fin.open("tobuy.txt"); if (fin.is_open() == false) { cerr << "Can't open file. Bye.\n"; exit(EXIT_FAILURE); } string item; ; getline(fin, item, ':'); while (fin) { ++count; cout << count << ": " << item << endl; getline(fin, item, ':'); } cout << "Done\n"; fin.close(); ; }
16.1.3 使用字符串
#include <iostream> #include <string> #include <cstdlib> #include <ctime> #include <cctype> using std::string; ; const string wordlist[NUM] = { "apiary", "beetle", "cereal", "danger", "ensign", "florid", "garage", "health", "insult", "jackal", "keeper", "loaner", "manage", "nonce", "onset", "plaid", "quilt", "remote", "stolid", "train", "useful", "valid", "whence", "xenon", "yearn", "zippy" }; int main() { using std::cout; using std::cin; using std::tolower; using std::endl; std::srand(std::time()); char play; cout << "Will you play a word game? <y/n> "; cin >> play; play = tolower(play); while (play == 'y') { string target = wordlist[std::rand() % NUM]; int length = target.length(); string attempt(length, '-'); string badchars; ; cout << "Guess my secret word. It has " << length << " letters, and you guess\n" << "one letter at a time. You get " << guesses << " wrong guesses.\n"; cout << "Your word: " << attempt << endl; && attempt != target) { char letter; cout << "Guess a letter: "; cin >> letter; if (badchars.find(letter) != string::npos || attempt.find(letter) != string::npos) { cout << "You already guessed that. Try again.\n"; continue; } int loc = target.find(letter); if (loc == string::npos) { cout << "Oh, bad guess!\n"; --guesses; badchars += letter; } else { cout << "Good guess!\n"; attempt[loc] = letter; loc = target.find(letter, loc + ); while (loc != string::npos) { attempt[loc] = letter; loc = target.find(letter, loc + ); } } cout << "Your word: " << attempt << endl; if (attempt != target) { ) { cout << "Bad choices: " << badchars << endl; } cout << guesses << " bad guesses left\n"; } } ) { cout << "That's right!\n"; } else { cout << "Sorry, the word is " << target << ".\n"; cout << "Will you play another? <y/n> "; cin >> play; play = tolower(play); } cout << "Bye\n"; cin.get(); cin.get(); ; } }
16.1.4 string还提供了哪些功能
#include <iostream> #include <string> int main() { using namespace std; string empty; string small = "bit"; string larger = "Elephants are a girl's best friend"; cout << "Sizes:\n"; cout << "\tempty: " << empty.size() << endl; cout << "\tsmall: " << small.size() << endl; cout << "\tlarger: " << larger.size() << endl; cout << "Capacities:\n"; cout << "\tempty: " << empty.capacity() << endl; cout << "\tsmall: " << small.capacity() << endl; cout << "\tlarger: " << larger.capacity() << endl; empty.reserve(); cout << "Capacity after empty.reserve(50): " << empty.capacity() << endl; cin.get(); ; }
16.1.5 字符串种类
16.2 智能指针模板类
16.2.1 使用智能指针
#include <iostream> #include <string> #include <memory> class Report { private: std::string str; public: Report(const std::string s) : str(s) { std::cout << "Object created!\n"; } ~Report() { std::cout << "Object deleted!\n"; } void comment() const { std::cout << str << "\n"; } }; int main() { { std::auto_ptr<Report> ps(new Report("using auto_ptr")); ps->comment(); } { std::shared_ptr<Report> ps(new Report("using shared_ptr")); ps->comment(); } { std::unique_ptr<Report> ps(new Report("using unique_ptr")); ps->comment(); } std::cin.get(); std::cin.get(); ; }
16.2.2 有关智能指针的注意事项
#include <iostream> #include <string> #include <memory> int main() { using namespace std; auto_ptr<] = { auto_ptr<string>(new string("Fowl Balls")), auto_ptr<string>(new string("Duck Walks")), auto_ptr<string>(new string("Chicken Runs")), auto_ptr<string>(new string("Turkey Errors")), auto_ptr<string>(new string("Goose Eggs")) }; auto_ptr<string> pwin; pwin = films[]; cout << "The nominees for best avian baseball film are\n"; ; i < ; i++) { cout << *films[i] << endl; } cout << "The winner is " << *pwin << "!\n"; cin.get(); ; }
#include <iostream> #include <string> #include <memory> int main() { using namespace std; shared_ptr<] = { shared_ptr<string>(new string("Fowl Balls")), shared_ptr<string>(new string("Duck Walks")), shared_ptr<string>(new string("Chicken Runs")), shared_ptr<string>(new string("Turkey Errors")), shared_ptr<string>(new string("Goose Eggs")) }; shared_ptr<string> pwin; pwin = films[]; cout << "The nominees for best avian baseball film are\n"; ; i < ; i++) { cout << *films[i] << endl; } cout << "The winner is " << *pwin << "!\n"; cin.get(); ; }
#include <iostream> #include <string> #include <memory> int main() { using namespace std; unique_ptr<] = { unique_ptr<string>(new string("Fowl Balls")), unique_ptr<string>(new string("Duck Walks")), unique_ptr<string>(new string("Chicken Runs")), unique_ptr<string>(new string("Turkey Errors")), unique_ptr<string>(new string("Goose Eggs")) }; unique_ptr<string> pwin; pwin = films[]; cout << "The nominees for best avian baseball film are\n"; ; i < ; i++) { cout << *films[i] << endl; } cout << "The winner is " << *pwin << "!\n"; cin.get(); ; }
16.2.3 unique_ptr为何优于auto_ptr
16.2.4 选择智能指针
16.3 标准模板库
STL提供了一组表示容器,迭代器,函数对象和算法的模板.容器是一个与数组类似的单元.可以存储若干个值.STL容器是同质的,即存储的值的类型相同;算法是完成特定任务(如对数组进行排序或在链表中查找特定值)的处方;迭代器能够用来遍历容器的对象,与能够遍历数组的指针类似,是广义指针;函数对象是类似于函数的对象,可以是类对象或函数指针(包括函数名,因为函数名被用作指针).STL使得能够构造各种容器(包括数组,队列和链表)和执行各种操作(包括搜索,排序和随机排列)
16.3.1 模板类vector
#include <iostream> #include <string> #include <vector> ; int main() { using std::vector; using std::string; using std::cin; using std::cout; using std::endl; vector<int> ratings(NUM); vector<string> titles(NUM); cout << "You will do exactly as told. You will enter\n" << NUM << " book titles and your ratings (0-10).\n"; int i; ; i < NUM; i++) { cout << << ": "; getline(cin, titles[i]); cout << "Enter your rating (0-10): "; cin >> ratings[i]; cin.get(); } cout << "Thank you. You entered the following:\n" << "Rating\tBook\n"; ; i < NUM; i++) { cout << ratings[i] << "\t" << titles[i] << endl; } cin.get(); ; }
16.3.2 可对矢量执行的操作
16.3.3 对矢量可执行的其他操作
#include <iostream> #include <string> #include <vector> #include <algorithm> struct Review { std::string title; int rating; }; bool operator<(const Review & r1, const Review & r2); bool worseThan(const Review & r1, const Review & r2); bool FillReview(Review & rr); void ShowReview(const Review & rr); int main() { using namespace std; vector<Review> books; Review temp; while (FillReview(temp)) { books.push_back(temp); } ) { cout << "Thank you. You entered the following " << books.size() << " ratings:\n" << "Rating\tBook\n"; for_each(books.begin(), books.end(), ShowReview); sort(books.begin(), books.end()); cout << "Sorted by title:\nRating\tBook\n"; for_each(books.begin(), books.end(), ShowReview); sort(books.begin(), books.end(), worseThan); cout << "Sorted by rating:\nRating\tBook\n"; for_each(books.begin(), books.end(), ShowReview); random_shuffle(books.begin(), books.end()); cout << "After shuffling:\nRating\tBook\n"; for_each(books.begin(), books.end(), ShowReview); } else { cout << "No entries. "; cout << "Bye.\n"; } cin.get(); cin.get(); ; } bool operator<(const Review & r1, const Review & r2) { if (r1.title < r2.title) { return true; } else if (r1.title == r2.title && r1.rating < r2.rating) { return true; } else { return false; } } bool worseThan(const Review & r1, const Review & r2) { if (r1.rating < r2.rating) { return true; } else { return false; } } bool FillReview(Review & rr) { std::cout << "Enter book title (quit to quit): "; std::getline(std::cin, rr.title); if (rr.title == "quit") { return false; } std::cout << "Enter book rating: "; std::cin >> rr.rating; if (!std::cin) { return false; } while (std::cin.get() != '\n') { continue; } return true; } void ShowReview(const Review & rr) { std::cout << rr.rating << "\t" << rr.title << std::endl; }
16.3.4 基于范围的for循环(C++11)
16.4 泛型编程
16.4.1 为何使用迭代器
16.4.2 迭代器类型
16.4.3 迭代器层次结构
16.4.4 概念,改进和模型
#include <iostream> #include <iterator> #include <vector> int main() { using namespace std; ] = { , , , , , , , , , }; vector<); copy(casts, casts + , dice.begin()); cout << "Let the dice be cast!\n"; ostream_iterator<int, char> out_iter(cout, " "); copy(dice.begin(), dice.end(), out_iter); cout << endl; cout << "Implicit use of reverse iterator.\n"; copy(dice.rbegin(), dice.rend(), out_iter); cout << endl; cout << "Explicit use of reverse iterator.\n"; vector<int>::reverse_iterator ri; for (ri = dice.rbegin(); ri != dice.rend(); ++ri) { cout << *ri << ' '; } cout << endl; cin.get(); ; }
#include <iostream> #include <string> #include <iterator> #include <vector> #include <algorithm> void output(const std::string & s) { std::cout << s << " "; } int main() { using namespace std; ] = { "fine", "fish", "fashion", "fate" }; ] = { "busy", "bats" }; ] = { "silly", "singers" }; vector<); copy(s1, s1 + , words.begin()); for_each(words.begin(), words.end(), output); cout << endl; copy(s2, s2 + , back_insert_iterator<vector<string> >(words)); for_each(words.begin(), words.end(), output); cout << endl; copy(s3, s3 + , insert_iterator<vector<string> >(words, words.begin())); for_each(words.begin(), words.end(), output); cout << endl; cin.get(); ; }
16.4.5 容器种类
#include <iostream> #include <list> #include <iterator> #include <algorithm> void outint(int n) { std::cout << n << " "; } int main() { using namespace std; list<, ); ] = { , , , , }; list<int> two; two.insert(two.begin(), stuff, stuff + ); ] = { , , , , , }; list<int> three(two); three.insert(three.end(), more, more + ); cout << "List one: "; for_each(one.begin(), one.end(), outint); cout << endl << "List two: "; for_each(two.begin(), two.end(), outint); cout << endl << "List three: "; for_each(three.begin(), three.end(), outint); three.remove(); cout << endl << "List three minus 2s: "; for_each(three.begin(), three.end(), outint); three.splice(three.begin(), one); cout << endl << "List three after splice: "; for_each(three.begin(), three.end(), outint); cout << endl << "List one: "; for_each(one.begin(), one.end(), outint); three.unique(); cout << endl << "List three after unique: "; for_each(three.begin(), three.end(), outint); three.sort(); three.unique(); cout << endl << "List three after sort & unique: "; for_each(three.begin(), three.end(), outint); two.sort(); cout << endl << "Sorted two merged into three: "; for_each(three.begin(), three.end(), outint); cout << endl; cin.get(); cin.get(); ; }
16.4.6 关联容器
#include <iostream> #include <string> #include <set> #include <algorithm> #include <iterator> int main() { using namespace std; ; string s1[N] = { "buffoon", "thinkers", "for", "heavy", "can", "for" }; string s2[N] = { "metal", "any", "food", "elegant", "deliver", "for" }; set<string> A(s1, s1 + N); set<string> B(s2, s2 + N); ostream_iterator<string, char> out(cout, " "); cout << "Set A: "; copy(A.begin(), A.end(), out); cout << endl; cout << "Set B: "; copy(B.begin(), B.end(), out); cout << endl; cout << "Union of A and B:\n"; set_union(A.begin(), A.end(), B.begin(), B.end(), out); cout << endl; cout << "Intersection of A and B:\n"; set_intersection(A.begin(), A.end(), B.begin(), B.end(), out); cout << endl; cout << "Difference of A and B:\n"; set_difference(A.begin(), A.end(), B.begin(), B.end(), out); cout << endl; set<string> C; cout << "Set C:\n"; set_union(A.begin(), A.end(), B.begin(), B.end(), insert_iterator<set<string> >(C, C.begin())); copy(C.begin(), C.end(), out); cout << endl; string s3("grungy"); C.insert(s3); cout << "Set C after insertion:\n"; copy(C.begin(), C.end(), out); cout << endl; cout << "Showing a range:\n"; copy(C.lower_bound("ghost"), C.upper_bound("spook"), out); cout << endl; cin.get(); ; }
#include <iostream> #include <string> #include <map> #include <algorithm> typedef int KeyType; typedef std::pair<const KeyType, std::string> Pair; typedef std::multimap<KeyType, std::string> MapCode; int main() { using namespace std; MapCode codes; codes.insert(Pair(, "San Francisco")); codes.insert(Pair(, "Oakland")); codes.insert(Pair(, "*lyn")); codes.insert(Pair(, "Staten Island")); codes.insert(Pair(, "San Rafael")); codes.insert(Pair(, "Berkeley")); cout << "Number of cities with area code 415: " << codes.count() << endl; cout << "Number of cities with area code 718: " << codes.count() << endl; cout << "Number of cities with area code 510: " << codes.count() << endl; cout << "Area Code City\n"; MapCode::iterator it; for (it = codes.begin(); it != codes.end(); ++it) { cout << " " << (*it).first << " " << (*it).second << endl; } pair<MapCode::iterator, MapCode::iterator> range = codes.equal_range(); cout << "Cities with area code 718:\n"; for (it = range.first; it != range.second; ++it) { cout << (*it).second << endl; } cin.get(); ; }
16.4.7 无序关联容器(C++11)
16.5 函数对象
很多STL算法都使用函数对象----也叫函数符(functor).函数符是可以以函数方式与()结合使用的任意对象.这包括函数名,指向函数的指针和重载了()运算符的类对象(即定义了函数operator()()的类).
16.5.1 函数符概念
正如STL定义了容器和迭代器的概念一样,它也定义了函数符概念
生成器(generator)是不用参数就可以调用的函数符
一元函数(unary function)是用一个参数可以调用的函数符
二元函数(binary function)是用两个参数可以调用的函数符
返回bool值的一元函数是谓词(predicate)
返回bool值的二元函数是二元谓词(binary predicate)
#include <iostream> #include <list> #include <iterator> #include <algorithm> template <class T> class TooBig { private: T cutoff; public: TooBig(const T & t) : cutoff(t) { } bool operator()(const T & v) { return v > cutoff; } }; void outint(int n) { std::cout << n << " "; } int main() { using std::list; using std::cout; using std::endl; TooBig<); ] = { , , , , , , , , , }; list<); list<); cout << "Original lists:\n"; std::for_each(yadayada.begin(), yadayada.end(), outint); cout << endl; std::for_each(etcetera.begin(), etcetera.end(), outint); cout << endl; yadayada.remove_if(f100); etcetera.remove_if(TooBig<)); cout << "Trimmed lists:\n"; std::for_each(yadayada.begin(), yadayada.end(), outint); cout << endl; std::for_each(etcetera.begin(), etcetera.end(), outint); cout << endl; std::cin.get(); ; }
16.5.2 预定义的函数符
16.5.3 自适应函数符和函数适配器
#include <iostream> #include <vector> #include <iterator> #include <algorithm> #include <functional> void Show(double); ; int main() { using namespace std; , , , , , }; , , , , , }; vector<double> gr8(arr1, arr1 + LIM); vector<double> m8(arr2, arr2 + LIM); cout.setf(ios_base::fixed); cout.precision(); cout << "gr8:\t"; for_each(gr8.begin(), gr8.end(), Show); cout << endl; cout << "m8: \t"; for_each(m8.begin(), m8.end(), Show); cout << endl; vector<double> sum(LIM); transform(gr8.begin(), gr8.end(), m8.begin(), sum.begin(), plus<double>()); cout << "sum:\t"; for_each(sum.begin(), sum.end(), Show); cout << endl; vector<double> prod(LIM); transform(gr8.begin(), gr8.end(), prod.begin(), bind1st(multiplies<double>(), 2.5)); cout << "prod:\t"; for_each(prod.begin(), prod.end(), Show); cout << endl; cin.get(); ; } void Show(double v) { std::cout.width(); std::cout << v << ' '; }
16.6 算法
16.6.1 算法组
STL将算法库分成4组:
非修改式序列操作
修改式序列操作
排序和相关操作
通用数字运算
前3组在头文件algorithm(以前为algo.h)中描述,第4组是专用于数值数据的,有自己的头文件,称为numeric(以前它们也位于algo.h中)
非修改式序列操作对区间中的每个元素进行操作.这些操作不修改容器的内容.例如,find()和for_each()就属于这一类
修改式序列操作也对区间中的每个元素进行操作.然而,顾名思义,它们可以修改容器的内容.可以修改值,也可以修改值的排列顺序.transform(),random_shuffle()和copy()属于这一类
排序和相关操作包括多个排序函数(包括sort())和其他各种函数,包括集合操作
数字操作包括将区间的内容累积,计算两个容器的内部乘积,计算小计,计算相邻对象差的函数.通常,这些都是数组的操作特性,因此vector是最有可能使用这些操作的容器
16.6.2 算法的通用特征
16.6.3 STL和string类
#include <iostream> #include <string> #include <algorithm> int main() { using namespace std; string letters; cout << "Enter the letter grouping (quit to quit): "; while (cin >> letters && letters != "quit") { cout << "Permutations of " << letters << endl; sort(letters.begin(), letters.end()); cout << letters << endl; while (next_permutation(letters.begin(), letters.end())) { cout << letters << endl; } cout << "Enter next sequence (quit to quit): "; } cout << "Done.\n"; cin.get(); ; }
16.6.4 函数和容器方法
#include <iostream> #include <list> #include <algorithm> void Show(int); ; int main() { using namespace std; , , , , , , , , , }; list<int> la(ar, ar + LIM); list<int> lb(la); cout << "Original list contents:\n\t"; for_each(la.begin(), la.end(), Show); cout << endl; la.remove(); cout << "After using the remove() method:\n"; cout << "la:\t"; for_each(la.begin(), la.end(), Show); cout << endl; list<int>::iterator last; last = remove(lb.begin(), lb.end(), ); cout << "After using the remove() function:\n"; cout << "lb:\t"; for_each(lb.begin(), lb.end(), Show); cout << endl; lb.erase(last, lb.end()); cout << "After using the erase() method:\n"; cout << "lb:\t"; for_each(lb.begin(), lb.end(), Show); cout << endl; cin.get(); ; } void Show(int v) { std::cout << v << ' '; }
16.6.5 使用STL
#include <iostream> #include <string> #include <vector> #include <set> #include <map> #include <iterator> #include <algorithm> #include <cctype> using namespace std; char toLower(char ch) { return tolower(ch); } string & ToLower(string & st); void display(const string & s); int main() { vector<string> words; cout << "Enter words (enter quit to quit):\n"; string input; while (cin >> input && input != "quit") { words.push_back(input); } cout << "You entered the following words:\n"; for_each(words.begin(), words.end(), display); cout << endl; set<string> wordset; transform(words.begin(), words.end(), insert_iterator<set<string> >(wordset, wordset.begin()), ToLower); cout << "\nAlphabetic list of words:\n"; for_each(wordset.begin(), wordset.end(), display); cout << endl; map<string, int> wordmap; set<string>::iterator si; for (si = wordset.begin(); si != wordset.end(); si++) { wordmap[*si] = count(words.begin(), words.end(), *si); } cout << "\nWord frequency:\n"; for (si = wordset.begin(); si != wordset.end(); si++) { cout << *si << ": " << wordmap[*si] << endl; } cin.get(); cin.get(); ; } string & ToLower(string & st) { transform(st.begin(), st.end(), st.begin(), toLower); return st; } void display(const string & s) { cout << s << " "; }
16.7 其他库
16.7.1 vector,valarray和array
#include <iostream> #include <valarray> #include <vector> #include <algorithm> int main() { using namespace std; vector<double> data; double temp; cout << "Enter numbers (<=0 to quit):\n"; ) { data.push_back(temp); } sort(data.begin(), data.end()); int size = data.size(); valarray<double> numbers(size); int i; ; i < size; i++) { numbers[i] = data[i]; } valarray<double> sq_rts(size); sq_rts = sqrt(numbers); valarray<double> results(size); results = numbers + 2.0 * sq_rts; cout.setf(ios_base::fixed); cout.precision(); ; i < size; i++) { cout.width(); cout << numbers[i] << ": "; cout.width(); cout << results[i] << endl; } cout << "done\n"; cin.get(); cin.get(); ; }
#include <iostream> #include <valarray> #include <cstdlib> ; typedef std::valarray<int> vint; void show(const vint & v, int clos); int main() { using std::slice; using std::cout; vint valint(SIZE); int i; ; i < SIZE; ++i) { valint[i] = std::rand() % ; } cout << "Original array:\n"; show(valint, ); vint vcol(valint[slice(, , )]); cout << "Second column:\n"; show(vcol, ); vint vrow(valint[slice(, , )]); cout << "Second row:\n"; show(vrow, ); valint[slice(, , )] = ; cout << "Set last column to 10:\n"; show(valint, ); cout << "Set first column to sum of next two:\n"; valint[slice(, , )] = vint(valint[slice(, , )]) + vint(valint[slice(, , )]); show(valint, ); std::cin.get(); std::cin.get(); ; } void show(const vint & v, int cols) { using std::cout; using std::endl; int lim = v.size(); ; i < lim; ++i) { cout.width(); cout << v[i]; ) { cout << endl; } else { cout << ' '; } ) { cout << endl; } } }
16.7.2 模板initializer_list(C++11)
#include <iostream> #include <initializer_list> double sum(std::initializer_list<double> il); double average(const std::initializer_list<double> & ril); int main() { using std::cout; cout << , , }) << , , }) << '\n'; std::initializer_list<double> dl = { 1.1, 2.2, 3.3, 4.4, 5.5 }; cout << "List 2: sum = " << sum(dl) << ", ave = " << average(dl) << '\n'; dl = { 16.0, 25.0, 36.0, 40.0, 64.0 }; cout << "List 3: sum = " << sum(dl) << ", ave = " << average(dl) << '\n'; std::cin.get(); ; } double sum(std::initializer_list<double> il) { ; for (auto p = il.begin(); p != il.end(); p++) { tot += *p; } return tot; } double average(const std::initializer_list<double> & ril) { ; int n = ril.size(); double ave = 0.0; ) { for (auto p = ril.begin(); p != ril.end(); p++) { tot += *p; } ave = tot / n; } return ave; }
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方法
#include <iostream> #include <cstring> int main() { using std::cout; using std::endl; const char * state1 = "Florida"; const char * state2 = "Kansas"; const char * state3 = "Euphoria"; int len = std::strlen(state2); cout << "Increasing loop index:\n"; int i; ; i <= len; i++) { cout.write(state2, i); cout << endl; } cout << "Decreasing loop index:\n"; ; i--) { cout.write(state2, i) << endl; } cout << "Exceeding string lengt:\n"; cout.write(state2, len + ) << endl; std::cin.get(); ; }
17.2.3 刷新输出缓冲区
17.2.4 用cout进行格式化
#include <iostream> int main() { using std::cout; cout << "12345678901234567890\n"; char ch = 'K'; ; cout << ch << ":\n"; cout << t << ":\n"; cout << -t << ":\n"; double f1 = 1.200; cout << f1 << ":\n"; cout << (f1 + 1.0 / 9.0) << ":\n"; double f2 = 1.67E2; cout << f2 << ":\n"; f2 += 1.0 / 9.0; cout << f2 << ":\n"; cout << (f2 * 1.0e4) << ":\n"; double f3 = 2.3e-4; cout << f3 << ":\n"; cout << f3 / << ":\n"; std::cin.get(); ; }
#include <iostream> int main() { using namespace std; cout << "Enter an integer: "; int n; cin >> n; cout << "n n * n\n"; cout << n << " " << n * n << " (decimal)\n"; cout << hex; cout << n << " "; cout << n * n << " (hexadecimal)\n"; cout << oct << n << " " << n * n << " (octal)\n"; dec(cout); cout << n << " " << n * n << " (decimal)\n"; std::cin.get(); std::cin.get(); ; }
#include <iostream> int main() { using std::cout; ); cout << "default field width = " << w << ":\n"; cout.width(); cout << "N" << ':'; cout.width(); cout << "N * N" << ":\n"; ; i <= ; i *= ) { cout.width(); cout << i << ':'; cout.width(); cout << i * i << ":\n"; } std::cin.get(); ; }
#include <iostream> int main() { using std::cout; cout.fill('*'); ] = { "Waldo Whipsnade", "Wilmarie Wooper" }; ] = { , }; ; i < ; i++) { cout << staff[i] << ": $"; cout.width(); cout << bonus[i] << "\n"; } std::cin.get(); ; }
#include <iostream> int main() { using std::cout; float price1 = 20.40; float price2 = 1.9 + 8.0 / 9.0; cout << "\"Furry Friends\" is $" << price1 << "!\n"; cout << "\"Fiery Friends\" is $" << price2 << "!\n"; cout.precision(); cout << "\"Furry Friends\" is $" << price1 << "!\n"; cout << "\"Fiery Friends\" is $" << price2 << "!\n"; std::cin.get(); ; }
#include <iostream> int main() { using std::cout; using std::ios_base; float price1 = 20.40; float price2 = 1.9 + 8.0 / 9.0; cout.setf(ios_base::showpoint); cout << "\"Furry Friends\" is $" << price1 << "!\n"; cout << "\"Fiery Friends\" is $" << price2 << "!\n"; cout.precision(); cout << "\"Furry Friends\" is $" << price1 << "!\n"; cout << "\"Fiery Friends\" is $" << price2 << "!\n"; std::cin.get(); ; }
#include <iostream> int main() { using std::cout; using std::endl; using std::ios_base; ; cout << "Today's water temperature: "; cout.setf(ios_base::showpos); cout << temperature << endl; cout << "For our programming friends, that's\n"; cout << std::hex << temperature << endl; cout.setf(ios_base::uppercase); cout.setf(ios_base::showbase); cout << "or\n"; cout << temperature << endl; cout << "How " << true << "! oops -- How "; cout.setf(ios_base::boolalpha); cout << true << "!\n"; std::cin.get(); ; }
#include <iostream> #include <cmath> int main() { using namespace std; cout.setf(ios_base::left, ios_base::adjustfield); cout.setf(ios_base::showpos); cout.setf(ios_base::showpoint); cout.precision(); ios::fmtflags old = cout.setf(ios_base::scientific, ios_base::floatfield); cout << "Left Justification:\n"; long n; ; n <= ; n += ) { cout.width(); cout << n << "|"; cout.width(); cout << sqrt(double(n)) << "|\n"; } cout.setf(ios_base::internal, ios_base::adjustfield); cout.setf(old, ios_base::floatfield); cout << "Internal Justification:\n"; ; n <= ; n += ) { cout.width(); cout << n << "|"; cout.width(); cout << sqrt(double(n)) << "|\n"; } cout.setf(ios_base::right, ios_base::adjustfield); cout.setf(ios_base::fixed, ios_base::floatfield); cout << "Right Justification:\n"; ; n <= ; n += ) { cout.width(); cout << n << "|"; cout.width(); cout << sqrt(double(n)) << "|\n"; } std::cin.get(); ; }
#include <iostream> #include <iomanip> #include <cmath> int main() { using namespace std; cout << fixed << right; cout << setw() << ) << "square root" << setw() << "fourth root\n"; double root; ; n <= ; n += ) { root = sqrt(double(n)); cout << setw() << setfill('.') << n << setfill(' ') << setw() << setprecision() << root << setw() << setprecision() << sqrt(root) << endl; } cin.get(); ; }
17.3 使用cin进行输入
17.3.1 cin>>如何检查输入
#include <iostream> int main() { using namespace std; cout << "Enter numbers: "; ; int input; while (cin >> input) { sum += input; } cout << "Last value entered = " << input << endl; cout << "Sum = " << sum << endl; cin.get(); cin.get(); ; }
17.3.2 流状态
17.3.3 其他istream类方法
17.3.4 其他istream方法
#include <iostream> int main() { using std::cout; using std::cin; using std::endl; char ch; while (cin.get(ch)) { if (ch != '#') { cout << ch; } else { cin.putback(ch); break; } } if (!cin.eof()) { cin.get(ch); cout << endl << ch << " is next input character.\n"; } else { cout << "End of file reached.\n"; std::exit(); } while (cin.peek() != '#') { cin.get(ch); cout << ch; } if (!cin.eof()) { cin.get(ch); cout << endl << ch << " is next input character.\n"; } else { cout << "End of file reached.\n"; } ; }
#include <iostream> ; inline void eatline() { while (std::cin.get() != '\n') { continue; } } int main() { using std::cin; using std::cout; using std::endl; char name[SLEN]; char title[SLEN]; cout << "Enter your name: "; cin.get(name, SLEN); if (cin.peek() != '\n') { cout << "Sorry, we only have enough room for " << name << endl; } eatline(); cout << "Dear " << name << ", enter your title: \n"; cin.get(title, SLEN); if (cin.peek() != '\n') { cout << "We were forced to turncate your title.\n"; } eatline(); cout << " Name: " << name << "\nTitle: " << title << endl; cin.get(); cin.get(); ; }
17.4 文件输入和输出
17.4.1 简单的文件I/O
#include <iostream> #include <fstream> #include <string> int main() { using namespace std; string filename; cout << "Enter name for new file: "; cin >> filename; ofstream fout(filename.c_str()); fout << "For your eyes only!\n"; cout << "Enter your secret number: "; float secret; cin >> secret; fout << "Your secret number is " << secret << endl; fout.close(); ifstream fin(filename.c_str()); cout << "Here are the contents of " << filename << ":\n"; char ch; while (fin.get(ch)) { cout << ch; } cout << "Done\n"; cin.get(); cin.get(); ; }
17.4.2 流状态检查和is_open()
17.4.3 打开多个文件
#include <iostream> #include <fstream> #include <cstdlib> int main(int argc, char * argv[]) { using namespace std; ) { cerr << ] << " filename[s]\n"; exit(EXIT_FAILURE); } ifstream fin; long count; ; char ch; ; file < argc; file++) { fin.open(argv[file]); if (!fin.is_open()) { cerr << "Could not open " << argv[file] << endl; fin.clear(); continue; } count = ; while (fin.get(ch)) { count++; } cout << count << " characters in " << argv[file] << endl; total += count; fin.clear(); fin.close(); } cout << total << " characters in all files\n"; cin.get(); cin.get(); ; }
17.4.5 文件模式
#include <iostream> #include <fstream> #include <string> #include <cstdlib> const char * file = "guests.txt"; int main() { using namespace std; char ch; ifstream fin; fin.open(file); if (fin.is_open()) { cout << "Here are the current contents of the " << file << " file:\n"; while (fin.get(ch)) { cout << ch; } fin.close(); } ofstream fout(file, ios::out | ios::app); if (!fout.is_open()) { cerr << "Can't open " << file << " file for output.\n"; exit(EXIT_FAILURE); } cout << "Enter guest names (enter a blank line to quit):\n"; string name; ) { fout << name << endl; } fout.close(); fin.clear(); fin.open(file); if (fin.is_open()) { cout << "Here are the new contents of the " << file << " file:\n"; while (fin.get(ch)) { cout << ch; } fin.close(); } cout << "Done.\n"; cin.get(); cin.get(); ; }
#include <iostream> #include <fstream> #include <iomanip> #include <cstdlib> inline void eatline() { while (std::cin.get() != '\n') { continue; } } struct planet { ]; double population; double g; }; const char * file = "planets.dat"; int main() { using namespace std; planet pl; cout << fixed << right; ifstream fin; fin.open(file, ios_base::in | ios_base::binary); if (fin.is_open()) { cout << "Here are the current contents of the " << file << " file:\n"; while (fin.read((char *)&pl, sizeof pl)) { cout << setw() << pl.name << ": " << setprecision() << setw() << pl.population << setprecision() << setw() << pl.g << endl; } fin.close(); } ofstream fout(file, ios_base::out | ios_base::app | ios_base::binary); if (!fout.is_open()) { cerr << "Can't open " << file << " file for output:\n"; exit(EXIT_FAILURE); } cout << "Enter planet name (enter a blank line to quit):\n"; cin.); ] != '\0') { eatline(); cout << "Enter planetary population: "; cin >> pl.population; cout << "Enter planet's acceleration of gravity: "; cin >> pl.g; eatline(); fout.write((char *)&pl, sizeof pl); cout << "Enter planet name (enter a blank line " "to quit):\n"; cin.); } fout.close(); fin.clear(); fin.open(file, ios_base::in | ios_base::binary); if (fin.is_open()) { cout << "Here are the new contents of the " << file << " file:\n"; while (fin.read((char *)&pl, sizeof pl)) { cout << setw() << pl.name << ": " << setprecision() << setw() << pl.population << setprecision() << setw() << pl.g << endl; } fin.close(); } cout << "Done.\n"; cin.get(); ; }
17.4.6 随机存取
#include <iostream> #include <fstream> #include <iomanip> #include <cstdlib> ; struct planet { char name[LIM]; double population; double g; }; const char * file = "planets.dat"; inline void eatline() { while (std::cin.get() != '\n') { continue; } } int main() { using namespace std; planet pl; cout << fixed; fstream finout; finout.open(file, ios_base::in | ios_base::out | ios_base::binary); ; if (finout.is_open()) { finout.seekg(); cout << "Here are the current contents of the " << file << " file:\n"; while (finout.read((char *)&pl, sizeof pl)) { cout << ct++ << ": " << setw(LIM) << pl.name << ": " << setprecision() << setw() << pl.population << setprecision() << setw() << pl.g << endl; } if (finout.eof()) { finout.clear(); } else { cerr << "Error in reading " << file << ".\n"; exit(EXIT_FAILURE); } } else { cerr << file << " could not be opened -- bye.\n"; exit(EXIT_FAILURE); } cout << "Enter the record number you wish to change: "; long rec; cin >> rec; eatline(); || rec >= ct) { cerr << "Invalid record number -- bye\n"; exit(EXIT_FAILURE); } streampos place = rec * sizeof pl; finout.seekg(place); if (finout.fail()) { cerr << "Error on attempted seek\n"; exit(EXIT_FAILURE); } finout.read((char *)&pl, sizeof pl); cout << "Your selection:\n"; cout << rec << ": " << setw(LIM) << pl.name << ": " << setprecision() << setw() << pl.population << setprecision() << setw() << pl.g << endl; if (finout.eof()) { finout.clear(); } cout << "Enter planet name: "; cin.get(pl.name, LIM); eatline(); cout << "Enter planetary population: "; cin >> pl.population; cout << "Enter planet's acceleration of gravity: "; cin >> pl.g; finout.seekp(place); finout.write((char *)&pl, sizeof pl) << flush; if (finout.fill()) { cerr << "Error on attempted write\n"; exit(EXIT_FAILURE); } ct = ; finout.seekg(); cout << "Here are the new contents of the " << file << " file:\n"; while (finout.read((char *)&pl, sizeof pl)) { cout << ct++ << ": " << setw(LIM) << pl.name << ": " << setprecision() << setw() << pl.population << setprecision() << setw() << pl.g << endl; } finout.close(); cout << "Done.\n"; cin.get(); ; }
17.5 内核格式化
#include <iostream> #include <sstream> #include <string> int main() { using namespace std; ostringstream outstr; string hdisk; cout << "What's the name of your hard disk? "; getline(cin, hdisk); int cap; cout << "What's its capacity in GB? "; cin >> cap; outstr << "The hard disk " << hdisk << " has a capacity of " << cap << " gigabytes.\n"; string result = outstr.str(); cout << result; cin.get(); cin.get(); ; }
#include <iostream> #include <sstream> #include <string> int main() { using namespace std; string lit = "It was a dark and stormy day, and " " the full moon glowed brilliantly. "; istringstream instr(lit); string word; while (instr >> word) { cout << word << endl; } cin.get(); ; }
17.6 总结
17.7 复习题
17.8 编程练习
第18章 探讨C++新标准
18.1 复习前面介绍过的C++11功能
18.1.1 新类型
C++11新增了类型long long和unsigned long long,以支持64位(或更宽)的整型;新增了类型char16_t和char32_t,以支持16位和32位的字符表示;还新增了"原始"字符串
18.1.2 统一的初始化
C++11扩大了用大括号括起的列表(初始化列表)的使用范围,使其可用于所有内置类型和用户定义的类型(即类对象).
18.1.3 声明
以前,关键字auto是一个存储类型说明符,C++11将其用于实现自动类型推断.这要求进行显式初始化,让编译器能够将变量的类型设置位初始值的类型.
关键字decltype将变量的类型声明为表达式指定的类型
C++11新增了一种函数声明语法:在函数名和参数列表后面(而不是前面)指定返回类型
模板别名: using=
空指针是不会指向有效数据的指针.以前,C++在源代码中使用0表示这种指针,但内部表示可能不同.这带来了一些问题,因为这使得0即可表示指针常量,又可表示整型常量,C++11新增了关键字nullptr,用于表示空指针;它是指针类型,不能转换为整型类型.为向后兼容,C++11仍允许使用0来表示空指针,因此表达式nullptr==0为true,但使用nullptr而不是0提供了更高的类型安全.
18.1.4 智能指针
C++11摒弃了auto_ptr,并新增了三种智能指针: unique_ptr,shared_ptr,weak_ptr
18.1.5 异常规范方面的修改
18.1.6 作用域内枚举
18.1.7 对类的修改
18.1.8 模板和STL方面的修改
18.1.9 右值引用
#include <iostream> inline double f(double tf) { return 5.0 * (tf - 32.0) / 9.0; } int main() { using namespace std; double tc = 21.5; double && rd1 = 7.07; double && rd2 = 1.8 * tc + 32.0; double && rd3 = f(rd2); cout << " tc value and address: " << tc << ", " << &tc << endl; cout << "rd1 value and address: " << rd1 << ", " << &rd1 << endl; cout << "rd2 value and address: " << rd2 << ", " << &rd2 << endl; cout << "rd3 value and address: " << rd3 << ", " << &rd3 << endl; cin.get(); ; }
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函数
#include <iostream> #include <vector> #include <algorithm> #include <cmath> #include <ctime> const long Size1 = 39L; * Size1; * Size2; == ; } == ; } int main() { using std::cout; std::vector<int> numbers(Size1); std::srand(std::time()); std::generate(numbers.begin(), numbers.end(), std::rand); cout << "Samples size = " << Size1 << '\n'; int count3 = std::count_if(numbers.begin(), numbers.end(), f3); cout << "Count of numbers divisible by 3: " << count3 << '\n'; int count13 = std::count_if(numbers.begin(), numbers.end(), f13); cout << "Count of numbers divisible by 13: " << count13 << "\n\n"; numbers.resize(Size2); std::generate(numbers.begin(), numbers.end(), std::rand); cout << "Sample size = " << Size2 << '\n'; class f_mod { private: int dv; public: f_mod() : dv(d) {} ; } }; count3 = std::count_if(numbers.begin(), numbers.end(), f_mod()); cout << "Count of numbers divisible by 3: " << count3 << '\n'; count13 = std::count_if(numbers.begin(), numbers.end(), f_mod()); cout << "Count of numbers divisible by 13: " << count13 << "\n\n"; numbers.resize(Size3); std::generate(numbers.begin(), numbers.end(), std::rand); cout << "Sample size = " << Size3 << '\n'; count3 = std::count_if(numbers.begin(), numbers.end(), []( == ;}); cout << "Count of numbers divisible by 3: " << count3 << '\n'; count13 = std::count_if(numbers.begin(), numbers.end(), []( == ; }); cout << "Count of numbers divisible by 13: " << count13 << '\n'; std::cin.get(); std::cin.get(); ; }
#include <iostream> #include <vector> #include <algorithm> #include <cmath> #include <ctime> const long Size = 390000L; int main() { using std::cout; std::vector<int> numbers(Size); std::srand(std::time()); std::generate(numbers.begin(), numbers.end(), std::rand); cout << "Sample size = " << Size << '\n'; == ; }); cout << "Count of numbers divisible by 3: " << count3 << '\n'; ; std::for_each(numbers.begin(), numbers.end(), [&count13]( == ; }); cout << "Count of numbers divisible by 13: " << count13 << '\n'; count3 = count13 = ; std::for_each(numbers.begin(), numbers.end(), [&]( == ; count13 += x % == ; }); cout << "Count of numbers divisible by 3: " << count3 << '\n'; cout << "Count of numbers divisible by 13: " << count13 << '\n'; std::cin.get(); ; }
18.4.2 为何使用lambda
18.5 包装器
18.5.1 包装器function及模板的低效性
#include <iostream> template<typename T, typename F> T use_f(T v, F f) { ; count++; std::cout << " use_f count = " << count << ", &count = " << &count << std::endl; return f(v); } class Fp { private: double z_; public: Fp(double z = 1.0) : z_(z) { } double operator()(double p) { return z_ * p; } }; class Fq { private: double z_; public: Fq(double z = 1.0) : z_(z) { } double operator()(double q) { return z_ + q; } };
#include <iostream> #include "somedefs.h" double dub(double x) { return 2.0 * x; } double square(double x) { return x * x; } int main() { using std::cout; using std::endl; double y = 1.21; cout << "Function pointer dub:\n"; cout << " " << use_f(y, dub) << endl; cout << "Function pointer square:\n"; cout << " " << use_f(y, square) << endl; cout << "Function object Fp:\n"; cout << " " << use_f(y, Fp(5.0)) << endl; cout << "Function object Fq:\n"; cout << " " << use_f(y, Fq(5.0)) << endl; cout << "Lambda expression 1:\n"; cout << " " << use_f(y, [](double u) { return u * u; }) << endl; cout << "Lambda expression 2:\n"; cout << " " << use_f(y, [](double u) { return u + u / 2.0; }) << endl; std::cin.get(); ; }
18.5.2 修复问题
#include <iostream> #include <functional> #include "somedefs.h" double dub(double x) { return 2.0 * x; } double square(double x) { return x * x; } int main() { using std::cout; using std::endl; using std::function; double y = 1.21; function<double(double)> ef1 = dub; function<double(double)> ef2 = square; function<double(double)> ef3 = Fq(10.0); function<double(double)> ef4 = Fp(10.0); function<double(double)> ef5 = [](double u) { return u * u; }; function<double(double)> ef6 = [](double u) { return u + u / 2.0; }; cout << "Function pointer dub:\n"; cout << " " << use_f(y, ef1) << endl; cout << "Function pointer square:\n"; cout << " " << use_f(y, ef2) << endl; cout << "Function object Fp:\n"; cout << " " << use_f(y, ef3) << endl; cout << "Function object Fq:\n"; cout << " " << use_f(y, ef4) << endl; cout << "Lambda expression 1:\n"; cout << " " << use_f(y, ef5) << endl; cout << "Lambda expression 2:\n"; cout << " " << use_f(y, ef6) << endl; std::cin.get(); ; }
18.5.3 其他方式
18.6 可变参数模板
18.6.1 模板和函数参数包
18.6.2 展开参数包
18.6.3 在可变参数模板函数中使用递归
#include <iostream> #include <string> void show_list3() {} template<typename T, typename... Args> void show_list3(T value, Args... args) { std::cout << value << ", "; show_list3(args...); } int main() { ; double x = 2.71828; std::string mr = "Mr. String objects!"; show_list3(n, x); show_list3(x * x, , mr); std::cin.get(); ; }
#include <iostream> #include <string> void show_list() {} template<typename T> void show_list(const T& value) { std::cout << value << '\n'; } template<typename T, typename... Args> void show_list(const T& value, const Args&... args) { std::cout << value << ", "; show_list(args...); } int main() { ; double x = 2.71828; std::string mr = "Mr. String objects!"; show_list(n, x); show_list(x * x, , mr); std::cin.get(); ; }
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