C++ Primer
读书笔记
注:本文
转
自
www.Eachfun.com
(整理说明:本资料是我在网上无意间找到的,读起来感觉不错,但由于原文是每章一个网页的格式,读起来不是很习惯,而且也不方便保存,所以我花了
2
个多小时的时间将所有网页的内容综合整理了一下,但最后才发现,文章的顺序颠倒了,所以各位如果愿意阅读本文的话,请从后面向前读,每个红色“标题”代表一章,如有不便还请各位见谅,或到原文网站阅览)
标题:
:
类
型
转换
之
隐
式
转换
对
于我
们
来
说
,
3+1.5=4.5
。但是
对
于
计
算机来
说
,
这
两个数的相加可不
这么简单
。因
为
3
与
3.0
是不同的数据
类
型,
3.0
与
1.5
是可以相加的,
3
却不能与
1.5
相加。于是,
C++
在
对
上面的表达式
进
行
处
理
时
,有必要
对
其中一个(或两者)
进
行
转换
。
因
为这
个
转换
是
“
隐
式
”
的,也就是
说这
个
转换
不
让
程序
员
知道,那
么
,系
统
就不能必
须
保
证
不
产
生
损
失,
这
个
损
失指的是精度。
为
了不
损
失精度,数据
总
是向精度高的
类
型
转换
。惟一的例外是当某个
变
量用作条件
时
,它被
转换为
bool
型。
对
算
术类
型的
转换
是
这样
的:所有比
int
小的都
转为
int
或
unsigned int
,即使没有必要也
这么转
。原因很
简单
,因
为
int
的
长
度正好等于字
长
。
对
CPU
来
说
,一次
处
理一个字是最快的。如果
int
或
unsigned int
无法达到要求,
则
往
long
、
double
转
化。
如果
每
一个
转换
都
能不造成
损
失,那自然是好事。可是世
间
的事
总
有不随人愿的
时
候。
对
于同一
种
算
术类
型,其
signed
和
unsigned
所能表达的范
围
是一
样
大,但却是互不重叠的两个范
围
。就像
“
妇联
”
和
“
工会
”
一
样
,往哪
边转换
都可能会
产
生
损
失。
这
是无法解决的
问题
,所以,在
VC
中,
试图
比
较
int
和
unsigned int
变
量
时
会
显
示警告。
奇怪的是,据我
测试
,在
VC++.net
中,只有
进
行
“<”
和
“>”
比
较
的
时
候才会
显
示警告,而
进
行
“==”
和
“!=”
比
较
的
时
候却不
显
示警告。
实际
上
这
两个比
较
也会有同
样
的
问题产
生,比如以下代
码
:
int a = -3;
unsigned b = 4294967293;
if (a == b) cout << "yes" << endl;
测试
运行以上代
码
会
发现
表达式
a==b
的
值为
true
。
这
是从
int
转为
unsigned int
过
程中的副作用,
这
个副作用我
们应该
知道,但是
VC++.net
不
进
行任何警告似乎也有些与理不通。
——
难
道我
关闭
了某些警告?
其它
隐
式
转换还
包括数
组
名
转换为
指
针
、算
术值
用作条件
时转换为
bool
,枚
举
被
转换为
整数,非
const
对
象
转换为
const
对
象等。其中枚
举转换为
整数没什
么
要提的,枚
举值
本来就是整数的
别
名,非
const
对
象
转为
const
对
象只是
临时
声明它的保
护级别
,通常用于作
为
参数
传递时
。
标题:
:内存管理之
new
和
delete
林
锐
博士曾将内存管理比
喻为
“
雷区
”
(《高
质
量
C++/C
编
程指南》第
44
页
),内存管理
这块难
不
难
?恐怕不好
说
。
“
会者不
难难
者不会
”
嘛。但是
说
内存管理
这块难
以成
为
“
会者
”
,
应该
是没有
错
的。
程序
时时
刻刻与内存打交道,只不
过
以往我
们
不用考
虑
,甚至不用知道。所以,所
谓
“
内存管理
”
,是特指堆内存。
如果把堆内存和
栈
内存的使用放在一起考
虑
,可以降低
对
内存管理恐惧。
一、内存的分配:
int i(100);//
栈
上分配内存
int *pi = new int(100);//
堆上分配内存
以上两
种
分配,都使用了
100
作
为
初始
值进
行初始化,如果是
进
行
类对
象的分配,它
们还
可以指定使用哪个构造函数,比如:
CString s(s1);//
栈
上分配内存
CString *ps = new CString(s1)//
堆上分配内存
这
里的
s
可以是
char*
指
针
,也可以是另一个
CString
对
象,它的
类
型将决定上面
这
两行
语
句
调
用哪一个构造函数。
在
这
里,有一点要特
别说
明,如果要使用默
认
构
造函数,
则
new
语
句后面可以用空括号
对
,而
栈
内分配的
语
句切不可用空括号
对
。如果写成
“CString s();”
,
则
并不是定
义
一个
CString
对
象
s
,而是定
义
一个返回
值为
CString
的函数
s
。
上面两
种
分配,也都可以分配
对
象数
组
,不同的是,用
new
操作符在堆内存分配数
组时
,只能
调
用默
认
构造函数。而在
栈
上分配却可以指定
对
象成
员
的初始
值
。如:
int a[3] = {1,2,3};//
栈
上分配内存,
int
可以
换
成其它
类
型名,后面的初始
值
可作相
应调
整。
int *p = new int[3];//
不能指定
这
三个
对
象的初始
值
二、内存的
访问
:
栈
内存可以通
过对
象
访问
,也可以通
过
指
针访问
,堆内存通
过
指
针访问
。方法完全相同。
三、内存的
释
放:
栈
内存在
对
象作用域
结
束后自
动释
放,堆内存要用
delete
。
delete pi;//
释
放内存
delete []p;//
释
放
对
象数
组
对
于
释
放
对
象数
组
,那个空的
[]
对
不可以
丢
,否
则
将只
释
放数
组
的第一个元素。
导
致内存泄露。
有了以上
对
比,堆内存似乎没有了任何
难
度。那
么
内存管理的玄机究竟在哪儿呢?在
进
行内存分配与
释
放的
时
候,有几个注意点要
记
住:
1
、
new
操作有可能失
败
,当系
统
无法分配需要的内存
块时
,将返回
NULL
值
,所以在
new
操作之后,要立即判断
pi
的
值
是否
为
NULL
。
int *pi = new int(100);
if (pi = NULL) {...}
2
、堆上分配的内存必
须
delete
,而且只可以
delete
一次。
为
了保
证
内存只被
delete
一次,
请务
必
记
住
delete
以后立即将指
针设为
NULL
:
delete pi;
pi = NULL;
虽
然
“pi=NULL;”
不是必
须
的,但
这
是个好
习惯
。将指
针设为
NULL
既可以防止
继续读
写
该
内存,也可以防止再次
释
放
该
内存。
老的
C
程序
员
可能忘不了
malloc
和
free
函数,它
们
也可以
进
行内存的分配与
释
放。但是
C++
时
代它
们
已
经
落伍了。它
们
只是按
请
求的字
节
数
进
行分配,而不管你用
这块
内存来干什
么
。
这样
做,就等于放弃了
类对
象的构造与析构。
对
于很多
类
来
说
,
这样
做是很危
险
的
。
标题:
:
优
先
级
、
结
合性和求
值顺
序
说
到
优
先
级
,我能熟
练
背出
“
先乘除,后加减
”
,之于
C++
列出的整整
19
个
优
先
级
,
每
个
优
先
级
又包含若干个操作符,我
总
是看了就
头
皮
发
麻。以我的
记
性,
连军
旗里哪个大哪个小都背不出来,
这
几十个操作符
——
还
是
饶
了我吧。
记
住林
锐
博士的
话
:
“
如果代
码
行中的运算符比
较
多,用括号确定表达式的操作
顺
序,避免使用默
认
的
优
先
级
。
”
(《高
质
量
C++/C
编
程指南》第
26
页
)
这样
做最直接的作用是不用
记忆
了
复杂
的
优
先
级
了,不用
记忆
并不是因
为懒
,而是
为
了更清晰。
毕
竟程序不只是
编给计
算机运行的,当我
们处
在一个多人
协
作的
团
体中
时
,程序的清晰度和精确性比性能要高得多。再
说
,多加几
对
括号是不影响运行效率的。
结
合性和求
值顺
序是容易混淆的两个概念。
每
一个操作符都
规
定了
结
合性,但是只有极少数操作符
规
定求
值顺
序。
结
合性是
说
如果有多个同
级别
的操作符,
这
些操作数
该
如何分
组
。比如
“1+2+3”
究竟分成
“(1+2)+3”
还
是
“1+(2+3)”
,
虽
然
这
两
种
分
组
最
终
没有区
别
,但不等于所有操作符都不
产
生区
别
。即使不
产
生区
别
,
计
算机
毕
竟是
计
算机,它只能按死的
规
范做事,于其
给
它灵
活机制,
还
不如
规
定了
结
合性
让
它遵守。
C++
只有四个操作符
规
定了求
值顺
序,它
们
是
“&&”
、
“||”
、
“?:”
和
“,”
,
记
住
这
四个操作符并不
难
。反
过
来
记
住其它操作符也不
难
,
难
的是在写程序中是否有
这
个意
识
。那
么
多网友
讨论
“j = i++ + i++ + i++;”
的
结
果,正
说
明了
还
有好多人不了解
“
未定
义
”
的威力。如果不小心使用了依
赖
于未定
义
求
值
程序的
语
句,将是一个不容易
发现
并改正的
问题
。
比如
“if (a[index++] < a[index]);”
标题:
:
sizeof
和逗号操作符
把
sizeof
说
成操作符可能有些不合
习惯
,因
为
sizeof
的用法与函数没区
别
。但是
sizeof
与函数有着本
质
的区
别
:它是
编译时
常量。也就是
说
,在程序
编译时
,就会求出它的
值
,并且成
为
程序中的常量。
sizeof
本身比
较简单
,惟一要提的就是它
对
数
组
名和指
针进
行操作的
结
果。
int a[10];
sizeof(a);
该
操作返回的是数
组
所有元素在内存中的
总长
度。但是如果
对
指
针进
行操作,返回的
则
是指
针
本身的
长
度,与指
针
所指
类
型无
关
。
正因
为
数
组
名与指
针
有着千
丝
万
缕
的
关
系,所以有
时
候
这
个特性会
让
人摸不着
头脑
:
int function(int a[10])
{
sizeof(a);
...
}
以上
sizeof
返回的不是数
组
所有成
员
的大小,而是指
针
的大小,因
为
数
组
在参数
传递
中弱化
为
指
针
。
逗号操作符除了在
for
语
句中
应
用以外,我没
发现
在哪儿
还
有用
处
。因
为
在一般情况下,逗号改成分号肯定是可以的,在
for
语
句中因
为
分号的作用另有定
义
,所以不能随便改。
这
才有了逗号的用武之地
。
标题:
:条件操作符
我
觉
得条件操作符的存在就是
为
了
简
化
if-else
语
句。第一,它与
if-else
语
句的功能完全一致;第二,它
虽
然是一行
语
句,但是它
规
定了求解
顺
序,
这
个
顺
序保
证
了有些表达式不被求
值
。
条件操作符是有一定的危
险
性的,危
险
的原因在于它的
优
先
级
特
别
底,
还
容易漏掉括号。它的
优
先
级仅仅
高于
赋值
和逗号运算符,也就是
说
,只有在与
赋值
或逗号共存
时
,才可以免去括号,其它情况下都得加上括号。漏加括号的
BUG
是很
难发现
的。
比如
“cout << (i < j) ? i : j;”
这
句的
实际
作用是将表达式
“(i
的
值输
出,然后
测试
一下
cout
的状
态
(
<<
操作符的返回
值
是
cout
),整个表达式的
值
不管是
i
还
是
j
,都被
丢
弃
。
标题:
:箭
头
操作符(
->
)
箭
头
操作符是
C++
发
明的全新操作符,但却不是
C++
才用到的功能。早期的
C
语
言
虽
然没有
类
,却有
结
构体,也允
许
有指向
结
构体
对
象的指
针
。不同的只是没有
发
明
“->”
这
个符号来
进
行
简
化操作。
说
到底,
“->”
的出
现
只是代替原来就可以
实现
的功能。
引用:
C++
语
言
为
包含点操作符和解引用操作符的表达式提供了一个同
义词
:箭
头
操作符(
->
)。
笔
记
:
这
一同
义词
的出
现
,不
仅仅
使程序
简
化而且更易于理解,更重要的是,它降低了出
错
的可能性。出什
么错
呢?
这
就跟操作符的
优
先
级
有
关
了:
p->a();
(*p).a();
以上两行等价,但是第二行却很容易写成
“*p.a();”
,由于点操作符的
优
先
级
高,就成了
“*(p.a());”
,
这
里至少包含了两个
错误
:一是
p
不是
对
象,点操作无效;二是
试图对类
成
员
解引用(只有当
该
成
员
返回指
针
才有效)。
也
许
有人要
说
了,第一个
错误
已
经导
致了
编译
不通
过
,
还
要
说
第二个
错误
干什
么
?
这样
理解就
错
了。
VC++
为
程序
员
提供了一个
十分
强
大的
库
,其中有些
类
的
对
象,既可以
进
行点操作也可以
进
行解引用操作的,如果上例中的
p
是那
种类
的
对
象,而且
p.a()
刚
好又返回指
针
,那
么
上面
这
句将可以通
过编译
,最
终换
来
难
以
查
找的
BUG
。
记
住,尽量多用箭
头
操作符
。
标题:
:
++
的陷阱
自增和自减符作符是如此常用,以至于没有必要提了。但是任何一本
书
都会下重手来提它,原因是它
虽
然
简单
,却含有玄机。
讲
到前自增和后自增
时
,几乎所有的
书
都是
这样讲
的:用
“j = i++”
和
“j = ++i”
对
比,告
诉读
者
虽
然
i
都增了
1
,但是
j
却不一
样
。
这
也没
办
法,因
为绝
大多数
书
在
讲
到
++
时还
没有提到
“
表达式的
值
”
这
个概念,有些
书
本可能从
头
到尾都不提。
对
于
“j = i++;”
来
说
,它是一个表达式没
错
,但是等号右
侧
也是一个表达式,
对
于表达式
“i++”
来
说
,它是有
值
的,
该
表达式的
值赋
予了
j
,而整个表达式也有一个
值
,只是
这
个
值
被
丢
弃了。
类
推:
“i = j = k = 1;”
语
句中
单
独的
“1”
就是一个表达式,叫常量表达式,它的
值
就是
1
,它
给
了
k
,
“k=1”
这
个表达式也有
值
,它的
值
就是
k
的
值
,它
给
了
j
,
……
最后
给
i
赋值
的表达式也有
值
,它的
值
就是
i
的
值
,
这
个
值
被
丢
弃。
弄明白了
“
表达式的
值
”
后,就可以科学地
讲
解
“++i”
和
“i++”
了,因
为这
两个表达式的
值
是
这样
定
义
的:前置自增
/
减表达式的
值
是修改后的
变
量
值
,后置
则为
修改前的
值
。
那
么
,在不
关
心表达式的
值
的
时
候
——
即只是
给
某
变
量自增或自减一下
——
究竟用哪个好呢?当然是前置好,因
为
前置只要用一个指令
进
行一下自增自减运算,然后直接返回即可。而后置却要先保存好
这
个初始
值
,再自增减,然后再返回以前保存的
值
。写
过
操作符重
载
的程序
员应该
更能体会
这
里面的区
别
。
“++i”
或
“i++”
是
这样简洁
,所以它
们
比
“i = i + 1;”
要美得多,所以
书
上
说
“
简洁
就是美
”
。但是,在美的同
时
,一个美
丽
的陷阱正在招手,程序
员们
必
须
理解并小心。
“if (ia[index++] < ia[index])”
这样
的表达式是危
险
的。前文中已
经
提到了,
C++
中只有极少的几个操作符是
规
定了
求
值顺
序的,
“<”
号没有
规
定,那
么
,
这
两个数要比
较
大小,系
统
究竟先求前者
还
是后者?如果先求前者,那
么
求后者的
时
候
index
是
值
有没有增一?
经
常在
论坛
上看到有人
讨论
“j = i++ + i++ + i++;”
的
结
果,
还
有人把自己的
实
践
结
果
贴
出来
证实
自己的理
论
。
这
正是初学者令人悲哀与同情的地方:
对
于
应该
好好掌握的知
识
没有足
够
的
热
情,却把一腔
热
血放在
这
些无
须讨论
的
话题
上,
C++
初学者要走的路不
仅长
,而且充
满
了
荆
棘
。
标题:
:回
忆
十几年前的
编
程入
门
本来不想
为这
段写
读书
笔
记
,不
过
突然想起十几年前的一件趣事来,
还
是
记
下来吧。
1993
年的
时
候,学校
开设
了
“
劳
技
”
课
,
讲
的是
BASIC
语
言。
对
于当
时连电脑
都没看
过
一眼的我
们
来
说
,学校
开设这样
的
课
,真是
让
我
们
无比感
动
。我至今仍然感
谢
我的母校,在片面追求升学率、大量
缩
减副
课
的全局下,我的母校居然
开设
了音
乐
、美
术
、
劳
技等一系列副之又副的
课
。
这让
我至今
难
忘。而令一方面,我今天能
够
在程序界打拼,完全是从那
时
候
开
始陪
养
的
兴
趣。如果我的高中没有
开设这门课
,我未必就不
进
入程序界,但是入
门
至少要
晚
三年。
这
三年,
我学的
编
程
东
西少之又少,
对
于整个
BASIC
来
说
,
简
直
连
皮毛都不如,而且学校只提供了一次上机机会,
练
的是
开
机与
关
机。我所
谓
的
“
调试
程序
”
是在自己的小霸王学
习
机上
进
行的。但是,在
电脑
没有普及的年份,懂得一点点就是很先
进
的了。
进
入大学后,堂堂一个大学的班
级
,居然只有两个人碰
过电脑
,大家的基
础
可想而知。在同学
们
拼命学
习
DOS
命令的
时
候,我已
经
遥遥地走在了前面。之后学
习
True Basic
自然一日千里,之后自学
VB
、自学
C
也就
顺
理成章。如果我的高中没有
开设这门课
,我未必就不
进
入程序界,但是我在
进
大学的
头
一段
时间
肯定会
与其他同学一起拼命
记
DOS
命令。要知道,其他同学至所以学得比我慢,并不是比我笨,而是没有
习惯电脑
的思
维
模式。
该说
“
赋值
操作符
”
了。高中
开设
的
BASIC
语
言
课
,其
课
本是不到半厘米厚的小
书
。但是我
爱
不
释
手地提前
阅读
了。
读
得一知半解就去做后面的
习题
,
发现
一句
“P=P+1”
,心想:
这
怎
么
可能嘛?
P
怎
么
可能等于
P
加一呢?移
项
一减,不就相当于
0=1
了
吗
?一定是
书
上印
错
了。于是,我将其中一个
P
改成了
R
,而且是用黑
钢
笔描的。我描得是如此
细
致,以至于根本看不出那个
R
是
P
改的。等到老
师讲
到
这
一
节
的
时
候,我
虽
然已
经
懂了
这
里
的
“=”
与数学上的
“=”
不一
样
,但是由于把
P
看成了
R
,
这题还
是做
错
了。
当
时
的情况是
这样
的:老
师
喊了几个同学上去做
题
,没有一个会。老
师说
“
有没有
谁
会做的?主
动
上来?
”
好几个同学立即喊我的名字,把我逼上去了。
结
果我
这么
一做就做
错
了。老
师
表
扬
了我的勇气,但是同学却
说
我
虽
然平
时
捧着
这
本
书
不放,原来也是个
“
菜
鸟
”
。
赋值
操作符就是在那个
时
候
给
我留下深刻印像的,那天我知道了,
“=”
号不表示左右相等。
复
合
赋值
操作符也比
较简单
,理解了它的用法就好了。
C++
至所以有了
赋值
操作符以后
还
要
复
合
赋值
操作符,不
仅仅
是
为
了
简
化代
码
,
还
可以加快
处
理速度。
“i=i+j”
和
“i+=j”
相比,前者的
i
求
值
了两次。不
过
,
这
点性能差
别对
整个程序性能来
说
不大。
还
有一个区
别
就是
优
先
级
方面的了。
“i*=j+1”
如果不写成
复
合
赋值
,就要加上括号:
i=i*(j+1)
。
标题:
:
关
系、
逻辑
和位操作符
关
系操作符本身没什
么
好提的,它
们
与我
们
平
时
身
边
的
逻辑
一
样
,所以不
难
理解。有两点可以略提一下:
一、因
为
ASC
字符中没有
“≥”
这
些符号,所以只好用
“>=”
代替。于是
产
生了
BASIC
和
C++
的两
种
不同符号集:
BASIC
用
“<>”
表示不等于,
C++
则
用
“!=”
。
二、程序
设计时
不能用
“if (i < j < k)”
这样
的写法。原因很
简单
,因
为这种
写法另有含
义
,或者
说
正因
为
不能
这样
写,才
给这种
写法另外
赋
了一
种
含
义
。
BASIC
和
C++
的
逻辑
操作符也有完全不同的写法,
BASIC
用比
较
直
观
的
关键
字
“And”
、
“Or”
之
类
,
C++
则
用
“&&”
和
“||”
。
这
也没什
么
,
记
住就行了。
逻辑
操作符中的
“&&”
和
“||”
是
C++
标
准中
为
数不多的指定了求
值顺
序的操作符(除此以外
还
有条件操作符
“?:”
和逗号操作符
“,”
,
关
于求
值顺
序,后面
还
将提及)。
这样
的
规
定惟一的缺点是需要
额
外的
记忆
,
优
点
则
是很明
显
的:它可以
让
“
危
险
”
的操作
变
得不危
险
。如
“while (i0)”
,
对
于
“Array[i]”
来
说
,指
针
越界是很可怕的,但是
“&&”
操作符的求
值顺
序保
证
了指
针
不越界。从另一方面
说
,要保
证
指
针
不越界,就必
须记
住
该
操作符的求
值顺
序(不然就只能分成两个
语
句写
喽
)。
又要提到
bool
值
的比
较
了,
“if (i < j < k)”
的
实际
就是
进
行了
bool
值
的比
较
。本
书
在
讲
解的
时
候,
虽
然提到了
“
将
k
与整数
0
或
1
做比
较
”
,但是我宁可提醒大家不去
记
住
这
个。
记
住
bool
值
只有
true
和
false
两个
值
,比
记
住
1
和
0
要好得多,因
为虽
然
false
就是
0
,但是
true
却不
仅
是
1
。
C++
中如果只有
逻辑
操作符也就算了,它偏
偏
还
有
“&”
和
“|”
这样
的位操作符。而位操作正是
BASIC
不提供的功能。
这
就
给
初学
C/C++
的人
带
来了
难
度。
难
点不在于理解,而在于
记忆
。位操作符的作用是
进
行某一
Bit
位的
设
定,在一个字
节掰
成八份用的年代,它非常常用,
—
比如
Turbo C
中的屏幕
设
置,就是
3
位表示背景色、
4
位表示前景色、一位表示
闪烁
,用一个字
节
完全存放了屏幕字体的信息。
现
在的海量存
储
与高速
处
理中,大可不必
这么节约
了(从
处
理速上看,非但没有
节约
,反而浪
费
了),所以,不会位操作也没什
么
大不了的。
不
过
,不熟悉位操作的用
户
却都知道
“<<”
和
“>>”
的另一用
处
。
这
事不能怪程序
员
,几乎所有的
C++
书
本都会从
“cin >> i;”
和
“cout << i;”
入手。不用知道
这
两个操作符原来是干什
么
的,甚至不用知道
“
重
载
”
是怎
么
回事。
C++
来到
这
个世界,
发
展了
C
语
言,使得
“
知其然不知道其所以然
”
的程序
员
也能好好工作。
也
许这
是它的一个
进步
吧
。
标题:
:可
爱
的算
术
操作符
算
术
操作符是最容易理解的符号了,因
这
它与平
时
做的数学是完全相同的
规则
。就
连
小学的知
识
“
先乘除后加减
”
都完全适用。
不
过
,就像因
为
与生活的
逻辑
完全一
样导
致易于理解一般,与生活
逻辑
不一致的
问题
就比
较难
以理解了。比如
“
有
9
个苹果,
3
个小朋友分,平均
每
个小朋友可以分到几个苹果?
”
,
现实
中既不可能是
-9
个苹果,也不可能
给
-3
个小朋友分。而是
C/C++
中的除法和求余却不得不面
对这种
情况。它
们
非但
难
于理解,而且
还
有不确定因素。
引用:如果两个操作数都
为
正,除法和求模操作的
结
果也是正数(或零);如果两个操作数
都是
负
数,除法操作的
结
果
为
正数(或零),而求模操作的
结
果
则为负
数(或零);如果只有一个操作数是
负
数,
这
两
种
操作的
结
果取决于机器;求模
结
果的符号也取决于机器,而除法操作的
值则
是
负
数(或零)。
笔
记
:以上
这
一大堆似乎有些
费
口舌。
这么说
吧,如果两个数同号,
则
一切都那
么简单
。即使是两个
负
数相除,只要把它
们
当成两个正数就可以了
——
商肯定是正的,余数只要
对应调
整一下符号即可;如果是一正一
负
两个数相除,那
么
商肯定是
负
的,但是究竟是
负
多少可不一定,余数就更
难说
了,
连
是正是
负
都不能确定。
21 % 6 = 3;
21 % 7 = 0;
-21 % -8 = -5;
21 % -5 = ?;//
与机器相
关
,可能是
1
可能是
-4
21 / 6 = 3;
21 / 3 = 3;
-21 / -28 = 2;
21 / -5 = ?;//
与机器相
关
,可能是
-4
或
-5
引用:当只有一个操作数
为负
数
时
,求模操作
结
果
值
的符号可依据分子(被除数)或分母(除数)的符号而定。如果求模的
结
果随分子的符号,
则
除出来的
值
向零一
侧
取整;如果求模与分母的符号匹配,
则
除出来的
值
向
负
无
穷
一
侧
取整。
10
个苹果分
给
-3
个朋友,平均
每
个小朋友可以分到几个
?
还
剩下几个
?
标题:
:操作符
第五章
开
始了,看得出来,从
这
章才真正
开
始
讲
解
C++
的基本内容。没有
这
里的内容,前四章都是狗屎。
操作符就是我
们
平
时
理解的
“
运算符
”
了,不
过
因
为
C++
是
计
算机
语
言,它与我
们
平
时
生活有着不一
样
的
逻辑
,所以,在我
们
平
时
看来
简单
的
“3+4”
,到了
C++
里,就得分成一个操作符和两个操作数了。
操作符的含
义
以及它能得到的
结
果,不
仅仅
取决于操作符本身,
还
同
时
取决于操作数。当初学
C
语
言的
时
候,
发现
做除法要用
“10/3.0”
而不用
“10/3”
着
实
惊
讶
了一回。特
别
是从
BASIC
走
过
来的人,
BASIC
里没有
这么强
的
类
型,所以
10/3
就是浮点数,要整除
还
得用
“Int()”
函数或改用
“/”
运算符(不是
每
个
VB
程序
员
都知道
这
个运算符的哦。)
从
C/C++
中弄明白了
“10/3.0”
和
“10/3”
,反
过
来再去理解
C
与
BASIC
的区
别
,不
难发现
C/C++
这样
做的确比
BASIC
高明得多。而
进
一
步
了解了硬件的运算机制后,
则
可以理解
这样
做不
仅仅
是高明,而且是必
须
。
引用:有些符号既可以表示一元操作也可以表示二元操作。例如
*……
,
这种
两用法相互独立
、各不相
关
,如果将其
视为
两个不同的符号可能会更容易理解些。
……
需要根据
该
符号所
处
的上下文来确定它代表一元操作
还
是二元操作。
笔
记
:
这
是一个大家都明白,但是大家都不会去想的
问题
。
细
想起来,我
们
不去想,正是因
为
我
们
早已熟知。然而我
们读
程序可以上下
关联
,
计
算机要做到
这
点就不容易
——
比如拼音
输
入法的自
动选词
。由此看来,
C++
编译
器是十分
优
秀的人工智能
软
件
。
标题:
:多
维
数
组
引用:
严
格地
说
,
C++
中没有多
维
数
组
。
笔
记
:不只是
C++
啦,
C
中就是
这样
。不
过
,正因
为
C++
中没有多
维
数
组
,而提供了
“
数
组
的数
组
”
,所以
C/C++
在数
组
使用上更灵活。
多
维
数
组
的定
义
和使用没什
么
要多提的,用
过
就懂了。无非是多一
对
括号而已。不
过
,如果把它跟指
针
一起用,倒是要注意的:二
维
数
组
名
对应
的是
“
指向指
针
的指
针
”
,所以,如果要在函数
间传递
多
维
数
组
,指
针类
型一定要正确:
int a[3][4];
int *p1 = &a[0][0];//a[][]
是一个
int
,
对
其取地址就是
int*
int *p2 = a[0];//a[0]
虽
然是
a
有一个元素,但它也是另一个数
组
的数
组
名
int **p3 = a;//a
是一个二
维
数
组
的数
组
名
int **p4 = &a[0];//a[0]
是一个数
组
名,它是
a
数
组
的一个成
员
另外,有一个比
较难记
、容易混淆的用法:
int (*p5)[4] = a;
说
它容易混淆,是因
为
它与
“int *p5[4];”
有着截然不同的意
义
。前者是指定
义
一个指向数
组
的指
针
,后者
则
是定
义
一个指
针
数
组
。
——
头
昏
ing...
本人在
实际
使用中,
经
常避
开
多
维
数
组
,而用其它途径来使用一大堆数
值
。比如可以
这样
用:
int a, b;//
两
维
的元素个数
int *p = new int[a*b];
for (int i=0; i
for (int j=0; j
p[i*a+j].....;
delete []p;
用
这种
方法,就是三
维
、四
维
也不用考
虑
“
指向指
针
的指
针
”
这么复杂
的
东
西。
不是我不会,而是不高
兴
去想
。
标题:
:
动态
数
组
我
晕
,本
书
才
讲
了个
开头
,居然
讲
到
new
和
delete
了。我
“
偷窥
”
了一下:下一章
开
始才
讲
到操作符,而且下章将有
专门
的一
节讲
new
和
delete
。看来
这
里提到它
们
的目的只是
为
了
说
明
string
这样
的
类为
什
么
可以自
动
适
应
大小。
new
的返回
值
是一个指
针
,不
过
本
书暂时
没有提到
new
也会返回
NULL
的。是的,
暂时还
不用提内容不
够这么复杂
的情况。
引用:在自由存
储
区中
创
建的数
组对
象数
组
是没有名字的,程序
员
只能通
过
其地址
间
接地
访问
堆中的
对
象。
笔
记
:
这
里有两个
问题
,一是
“
数
组
的名字
”
其
实
也是个指
针
,指
针
当然也可以看成
“
数
组
的名字
”
,
这
本来就可以互
换
的,正如我前面
说
的一
样
:
“5[a]”
完全等价于
“*(5+a)”
。至于
a
是静
态
的数
组
名
还
是
动态
的指
针
,没有区
别
。第二个
问题
是
这
里提到了
“
堆
”
,在没有
讲
解内存之前,
这样说毕
竟理解上有
难
度。
还
是那句
话
,
这
本
书
是
给
有一定基
础
的人看的。
令我耳目一新的是:
new
还
能
创
建
const
数
组
。
——
不是我不懂,而是
实
在没想到。再
说
了,
创
建一大堆
const
的内容,而且只能初始化
为
同一个
值
。那
这
有什
么
用?正如本
书
提到的一
样
:
“
这样
的数
组实际
上用
处
不大
”
。依我看,不是用
处
不大,而是根本没用。
动态
空
间
的
释
放
应该
用
“delete []p;”
而不是
“delete p;”
,
这
是一个只要
记
住就可以的
问题
,我之所以提上一句,是因
为
有人没有注意
过这
个
细节
,包括很多自以
为
很了不起的程序
员
。
引用:如果
遗
漏了方括号
对
,
这
是一个
编译
器无法
发现
的
错误
,将
导
致程序在运行
时
出
错
。
标题:
:
C
风
格字符
串
给这
篇文章定下
这
个
标题:
,是因
为书
中就是
这样说
的。本
书
是
讲
解
C++
的,所以它推荐
读
者尽量使用
C++
的内容,而
实际
上像我
这样
从
C
过
来的人,
还
是
习惯
于使用
C
风
格的字符串。
——
我又想起了那句
话
:
“
原来我只是一个
‘
古代
’
的
C++
程序
员
。
”(
见
《数
组
》一文
)
C
语
言是用字符数
组
来做字符串的(当然
这
个字符数
组
必需要有一个
NULL
结
尾),因
为
字符串是如此常用,
C
语
言
还专门开发
了一套
库
函数来
处
理
这
个特殊的数
组
。于是,我
们进
行字符串操作
时
,可以忘
记
指
针
、忘
记
循
环
、
还
可以忘
记
char
这
个内置
类
型。
正是因
为
如此,林
锐
博士的《高
质
量
C++/C
编
程指南》中
还
特
别强调
,不可以用指
针
的
赋值
和比
较
来
进
行字符串的
赋值
和比
较
。
这
个警告
对
于从
VB
转过
来的人尤其重要。
使用
C
风
格的字符串有两点是必
须
保
证
的:一是要
给这
个数
组开
劈足
够长
度的空
间
;二是一定不要忘了
NULL
;其中第二点一般程序
员
不会犯
错
,因
为毕
竟没几个人用
“chat s[3] = {'a', 'b', '/0'}”
这种
方式来定
义
字符串。第一点就成了重中之重。我
们
在
strcpy
之前,有没有考
虑过
目
标
字符串可能的空
间
不足?
“strn”
风
格的函数既救了大家也可能害了大家,
说
它救了大家,因
为
大家在
strncpy
和
strncat
时
可以控制字符个数,即使源字符串太
长
,也可以避免内存溢出。但是它存在的危
险
性是它不会
为
目
标
字符串添加
NULL
。
所以,
书
写到
这
里再次做了一个提醒:
“
尽可能使用
标
准
库类
型
string”——
我都忘了
这
是第几次提醒了,本
书
一而再再而三地提醒
读
者不要做
“
古代
”
的
C++
程序
员
。
标题:
:指
针
(三)指
针
与数
组
指
针
和数
组
之
间
是什
么关
系呢?
书
中曰
“
密切相
关
”
。其
实
,那
简
真就是同一回事嘛。用到指
针
的
时
候,你未必会用到数
组
;但是只要你用到数
组
,你就必要然用到指
针
(即使你不知道)。
正是因
为
指
针
可以用加或减运算来移
动
它所指的位置,而且
每
加一或减一正好移
动
到相
邻
一个同
类
型的
变
量(不管
这
个
变
量占内存是多少),那
么
我有意将一堆同
类
型的
变
量放在一起,拿一个指
针
指向它
们
中的第一个,再
记
住它
们
的个数,
这
就成了数
组
。
数
组
用一
组
方括号来解引用其中的某个成
员
,
这
也只是指
针
运算的
简
化。比如:
int a[10];
a[5] = 5;
以上
这种
代
码谁
都用
过
,
谁
都能理解。那
么
下面
这
行代
码
呢?
5[a] = 5;
这种
用法恐怕很少有人知道,即使
现
在知道了,恐怕也很
难
理解。
实际
上知道了数
组
运算的
实质
,
这
行代
码
的迷
雾
就会立即消失:
C/C++
语
言
处
理括号的方法很
简单
,将方括号前面的
值
和方括号内的
值
相加,得到一个新的指
针
,再取指
针
所指的
对
象
值
。
“a[5]”
就完全等价于
“*(a+5)”
,
“5[a]”
就完全等价于
“*(5+a)”
。
那
么
“*(a+5)”
是什
么
运算呢?指
针
运算。因
为
在
编译
器
处
理
“int a[10];”
的
时
候,就等于定
义
了一个
“int * const a;”
同
时
将它初始化
为
指向
栈
内存的某
处
。
实际
上,正是因
为
“*(a+5)”
这种
用法
实
在太常用了,
C
才
规
定了它的替代用法,后来
这
个替代用法被广
为
接受,而它的
实际
却被人
遗
忘。
以上内容本
书
未有提及,
这
是我看
书
看到
这
里的一点心得,作
为读书
笔
记
写下来。不是
为
了炫耀。本
书虽
然是
给
有一定基
础
的人
读
的,但是
毕
竟它只是按
步
就章地写下
C++
的
语
法
规则
,没有必要提及
这
些
技巧性高而又
实
用性少的内容。我之所以要写下来,目的是
为
了便于理解指
针
运算
。
标题:
:指
针
(二)
指
针
的初始化与
赋值
:指
针
是一个
变
量,它可以被
赋值
,也可以被求
值
。指
针
可以接受的
值
只有以下几
种
:
1
、
编译时
可求
值
的
0
值
常量。(必
须
是
0
,其
实
就是
NULL
啦)
2
、
类
型匹配的
对
象的地址。(也就是用
&
运算符取一个
变
量的地址)
3
、另一
对
象末的下一地址。(
这种
用法主要用在循
环
里,其
实
当指
针
取
这
个
值时
,
对
其所指的内存
进
行存取往往会
导
致灾
难
)
4
、同
类
型的另一个有效指
针
(如
“p=q;”
)。
其中第
1
点,将
0
值赋给
指
针
,主要是
为
了有一个状
态
表示
这
个指
针
是
“
空的
”
。
C/C++
通常
约
定
0
为
NULL
,
虽
然的确存在地址
为
0
的内存,但是
别
指望用指
针
来
访问这
个内存。
初学者怎
样
才能消除
对
指
针
的恐惧?我
觉
得首要的一点是清醒地
认识
并且
时
刻提醒自己
“
指
针
也是一个
变
量
”
。比如以下两行程序:
int i;
int *p = &i;
看到
这
儿的人几乎无一例外把
p
和
i
联
系起来(
这
当然不是坏事),但是,我
觉
得更重要的是将
p
和
i
分离,心里
记
住,
p
是一个
变
量,
该变
量是有它的
值
的,
这
个
值
与
i
的唯一
关
系是:目前
该值
正好等于
变
量
i
在内存中的位置。两
种
情况下
p
与
i
将毫无
关
系:
1
、
p
值
被改
变
,如
“p = &j;”
或
“p++;”
2
、
i
变
量被
释
放,如离
开
了
i
的作用域。
标题:
:指
针
指
针
是
C/C++
的精
华
,也是最
难
的部分。
——
所有学
习
C/C++
的人都明白
这
点,当年我初学的
时
候也是
这样
。但是,
现
在再回想指
针
,我却很
难
回
忆
它究竟
难
在哪儿。
应该说这
就叫
“
难
者不会,会者不
难
”
吧。
“
饱汉
不知
饿汉饥
”
是有一定的道理的,即使
饱汉
曾
经饿过
。
本
书
中
规
中矩地
讲
解了指
针
的概念、定
义
与初始化、操作等。正如上面提到的
“
饱汉
不知
饿汉饥
”
,我似乎很健忘,以至于不
记
得指
针
的
难
点在哪儿了。
指
针
的灵活性可以把大量的工作化繁
为
易,前提是必
须
首很把足
够
繁的指
针
弄懂。听起来有点像
绕
口令,事
实
就是
这样
,你
现
在把
难
懂的
东
西弄懂了,日后可以把
难
事化
简
,大事化小。
从
VB
过
来的人一定会熟悉
“
值传递
”
和
“
地址
传递
”
这
两个概念,
实际
上,
“
地址
传递
”
这种说
法正是
为
了弥
补
VB
没有指
针
却有
类
似的需要才
发
明的。我
认为
C/C++
程序
员
要想深入理解指
针
,首先要抛弃
这
个概念。在
C/C++
程序中,即使在函数
调
用中
传递
指
针
,也不能
说
“
地址
传递
”
,
还应该说
是
值传递
,只不
过这
次
传递
的
值
有点特殊,特殊在于借用
这
个
值
,可以找到其
它
值
。就好像我
给
你一把
钥
匙一
样
,你通
过钥
匙可以
间
接
获
得更多,但是我
给
你的只不
过
是
钥
匙。
我前
阵
子曾写
过
一篇
关
于指
针
的文章,之所以写那篇文章,是因
为
看到一大堆初学者在
论坛
上提
问
。通
过对
他
们
提的
问题
的分析,我
总结
了几点。下面,首先就先引用我自己写的《
关
于指
针
》中的片段吧(完整的文章
请
到我的个人主
页查
找):
一、指
针
就是
变
量:
虽
然申明指
针
的
时
候也提
类
型,如:
char *p1;
int *p2;
float *p3;
double *p4;
.....
但是,
这
只表示
该
指
针
指
向某
类
型的数据,而不表示
该
指
针
的
类
型。
说
白了,指
针
都是一个
类
型:四字
节
无符号整数(将来的
64
位系
统
中可能有
变
化)。
二、指
针
的加减运算很特殊:
p++
、
p--
之
类
的运算并不是
让
p
这
个
“
四字
节
无符号整数
”
加一或减一,而是
让
它指向下一个或上一个存
储单
元,它
实际
加减的
值
就是它所指
类
型的
值
的
size
。
比如:
char *
型指
针
,
每
次加减的改
变
量都是
1
;
float *
型的指
针
,
每
次加减的改
变
量都是
4
;
void *
型指
针
无法加减。
还
要注意的是:指
针
不能相加,指
针
相减的差
为
int
型。
正是因
为
指
针
有着不同于其它
变
量的运算方式,所以,在任何
时
候用到指
针
都必
须
明确
“
指
针
的
类
型
”
(即指
针
所指的
变
量的
类
型)。
这
就不
难
理解
为
什
么
函数声明
时
必
须
用
“int abc(char *p)”
而
调
用的
时
候却成了
“a = abc(p);”
这样
的形式了。
三、用指
针
做参数
传递
的是指
针值
,不是指
针
本身:
要理解参数
传递
,首先必
须
把
“
形参
”
与
“
实
参
”
弄明白。
函数
A
在
调
用函数
B
时
,如果要
传递
一个参数
C
,
实际
是在函数
B
中重新建立一个
变
量
C
,并将函数
A
中的
C
值传
入其中,于是函数
B
就可以使用
这
个
值
了,在函数
B
中,无
论
有没有修改
这
个
C
值
,
对
于函数
A
中的
C
都没有影响。函数
B
结
束
时
,会将所有内存收回,局部
变
量
C
被
销毁
,函数
B
对变
量
C
所做的一切修改都将被抛弃。
以上示例中,函数
A
中的
变
量
C
称
为
“
实
参
”
,函数
B
中的
变
量
C
被称
为
“
形参
”
,
调
用函数
时
,会在
B
函数体内建立一个形参,
该
形参的
值
与
实
参的
值
是相同的,但是形参的改
变
不影响
实
参,函数
结
束
时
,形参被
销毁
,
实
参依然没有
发
生
变
化。
指
针
也是一个
变
量,所以它也符合以上的
规
定,但是,指
针
存放的不
仅仅
是一个
值
,而是一个内存地址。
B
函数
对这
个地址
进
行了改
动
,改
动
的并不是形参,
而是形参所指的内存。由于形参的
值
与
实
参的
值
完全相同,所以,
实
参所指的内存也被修改。函数
结
束
时
,
虽
然
这
个形参会被
销毁
,指
针
的
变
化无法影响
实
参,但此前
对
它所指的内存的修改会持
续
有效。所以,把指
针
作
为
参数可以在被
调
函数(
B
)中改
变
主
调
函数(
A
)中的
变
量,好像形参影响了
实
参一
样
。
注意:是
“
好像
”
。在
这过
程中,函数
B
影响的不是参数,而是内存。
下面再来看
刚
才的例子:
“int abc(char *p)”
和
“a = abc(p);”
。
为
什
么
申
请
中要用
*
号,因
为
函数必
须
知道
这
是指
针
;
为
什
么调
用
时
不加
*
号,因
为传递
的是
“
指
针值
”
,而不是
“
指
针
所指内存的
值
”
。
四、指向指
针
的指
针
:
正因
为
指
针
也是一个
变
量,它一
样
要尊守形参与
实
参的
规
定。所以,
虽
然指
针
做参数可以将函数内
对变
量的修改
带
到函数外,但是,函数体内
对
指
针
本身作任何修都将被
丢
弃。如果要
让
指
针
本身被修改而且要影响函数外,那
么
,被
调
函数就
应该
知道
“
该
指
针
所在的内存地址
”
。
这时
,指
针
不再是指
针
,而是
“
普通
变
量
”
。作
为
参数
传递
的不是
这
个
“
普通
变
量
”
,而是指向
这
个
“
普通
变
量
”
的指
针
。即
“
指向指
针
的指
针
”
。
如果
p
是一个指向指
针
的指
针
,那
么
*p
就是一个指
针
,我
们
不
妨就把它看成
q
。要
访问
q
指
针
所指的内存,只要
*q
就是了。用初中数学的
“
等量代
换
”
一
换
就知道,
*q
就是
**p
。
五、指
针
数
组
。
之所以要把
“
指
针
数
组
”
单
独提出来,是因
为
数
组
本身就与指
针
有着千
丝
万
缕
的
关
系。即使你不想用指
针
,只要你使用了数
组
,
实际
就在与指
针
打交道了。
只要理解了指
针
本身就是
变
量,就不
难
理解
“
指
针
数
组
”
,我
们
可以
暂
且把它当成普通数
组
来
处
理,
a[0]
、
a[1]
、
a[2]……
就是数
组
的元素,只是,
a[0]
是一个指
针
,
a[1]
、
a[2]
也是一个指
针
。那
a
呢?当然也是指
针
,但
这
是两
码
事。你可以
完全无
视
a
的存在,只去管
a[0]
等元素。
*a[0]
与
*p
没有什
么
本
质
的区
别
。
还
有一个
东
西不得不提一下,它比
较
重要:
指
针
的定
义
有两个可取的方式,它
们
各有
优
缺点:
“int *p;”
和
“int* p;”
是完全等价的,后者的好
处
是
让
人体会到
p
是一个
“
指向
int
的
”
指
针
,前者会
让
人
误
解
为
*p
是一个
int
型
变
量(
这
里没有定
义
int
型
变
量);但是前者的好
处
是不会
产
生混淆,如
“int *p, *q;”
让
人一眼就看出定
义
了两个指
针
,而
“int* p,q;”
会
让
人
误
解成定
义
了两个指
针
(
实际
上
q
不是指
针
)
。
标题:
:数
组
进
入本
书
第四章,
开
始
讲
“
数
组
”
了。数
组难
不
难
?
这
不好
说
。但是数
组
非常重要
这
是肯定的,有
许
多基本的算法就是与数
组
一起出
现
的
——
比如冒泡排序法。而离
开
了那些算法,数
组
本身也失去了价
值
。
注意:
阅读
本章
时
要
对
“
维
数
”
概念加以小心,按平
时
的理解,
“
维
数
”
是多
维
数
组
中的概念,但是本
书
中的
“
维
数
”
指的是元素个数。
为
了避免干
扰
,我在
阅读
笔
记
中用
“
个数
”
来取代
“
维
数
”
。
引用:在出
现标
准
库
之前,
C++
程序大量使用数
组
保存一
组对
象
。而
现
代的
C++
程序
则
更多地使用
vector
来取代数
组
,数
组
被
严
格限制于程序内部使用,只有当性能
测试
表明使用
vector
无法达到必要的速度要求
时
,才使用数
组
。
笔
记
:我汗一个先!原来我只是一个
“
古代
”
的
C++
程序
员
。
数
组
的定
义
必需指明元素的个数,而且必
须
是
“
常量表达式
”
。
这
一点想必理解数
组
的人都会操作(即使弄
错
了,
编译
器也会立即
报错
),但是真正去思考它的人也
许
不多。
这
里的
“
常量
”
概念是指程序在
编译阶
段就能求
值
的量。
在
C
时
代,我
们
并不会
过
多地理会
“
常量
”
这
个概念,那是因
为
C
时
代没有
const
,而
define
这
个
东
西,
谁
都知道它是在
编译
前就直接替
换
的。但是到了
C++
时
代,由于
const
的存在,使
“
常量
”
这
个概念
变
得更普
杂
了。
const size1 = 5;//
这
是个
编译时
就可以求
值
的常量,它可以在数据的定
义
中使用。它的作用
仅仅
相当于
“define”
。
const size = getsize();//
这
个常量是运行
时
才能求
值
的。
除此之外,
“
常量求达式
”
的概念
还
指明
这
可是以一个算
术
式,只要
编译时
能求
值
。如
“char username[max_name_size + 1];”
这种
用法非
常常用,因
为
它可以避免在定
义
数
组时
忘掉
NULL
。
数
组
成
员
的初始化可以
显
式指定,也可以不指定。
显
式指定
时
指定成
员
的个数可以小于等于
实际
个数,但不可以比
实际
个数大,如果小于,
则
其它元素
视为
未指定。
对
于未指定的元素的初始
值
,它
们
遵循
变
量的初始化原
则
,
请
看《
变
量初始化》一文。
绝
大多数
书
本在介
绍
“
字符串
”
时
都会提到,字符串其
实
是字符数
组
,只是因
为
它
们
太常用,才会有
专门
的使用方法。本
书
也不例外。
本
书
没有提数
组
的
实质
,因
为
“
指
针
”
还
没有
进
入
读
者的眼睛。
引用:数
组
的
显
著缺点在于:数
组
的
长
度是固定的,
而且程序
员
无法知道一个
给
定数
组
的
长
度。
笔
记
:其
实这
正在程序
员
心中的一个痛。当我把数
组
作
为
参数
传
送
给
函数
时
,函数
该
怎
么处
理
这
个
东
西?要
么
是
对
大小有个事先的
约
定
——
这样
的程序将失去很多通用性,要
么连
大小一起
传递给
人家
。
标题:
:
C++
标
准
库
,想
说爱
你不容
易
第三章就
这样结
束了,本章介
绍
了三个
标
准
库类
型:
string
、
vector
和
bitset
。
可惜的是,整个第三章我都是草草
读过
的。一方面因
为
它
们
不属于
严
格意
义
上的
C++
内容,另一方面
C
时
代的
东
西在不
经
意
间
抵触着它
们
。
确切地
说
,它
们
C
时
代的那些
东
西的替代品。它
们
存在的理由就是它
们
更
优
秀。然而
优
秀是一回事,
动
不
动
心又是一回事。
C
语
言在
类
与
对
象方面的缺失,使
C
程序
员
更多地掌握了底
层
的操作。面
对
C++
标
准
库
中的
string
和
bitset
这
些
东
西,
C
程序
员们
都知道,在
C++
出来以前自己是怎
样
想方法
解决
过问题
的。刻苦才有刻骨
铭
心,
转
到
C++
以后,是使用更
简单
安全的
标
准
库
,
还
是使用自己曾熟悉的老
办
法,
这
是一个取舍的
问题
。
引用:程序
员应优
先使用
标
准
库类类
型。
笔
记
:本
书
在
这
里放置了
这样
一个提示,大概是想告
诫
我
这样
的
顽
固派:
“
别
再死
纠
着
陈
腐旧套不放了,回
头
吧。
”
其
实
,早在本
书
的前言就有了
类
似的内容。可是,我依然不能
让
自己静下心来
细读
并熟
记
第三章。
也
许
有一天我会回
头
重
读
第三章,当然,也
许
永
远
不会。
因
为
像
string
和
vector
这样
的
东
西,在
MFC
中
还
有更好的替代品
。
标题:
:
标
准
库
bitset
类
型
每
当使用到布
尔变
量数
组时
,
总
是有点心疼。因
为
布
尔变
量只需要
0
和
1
两
种值
,然而
编译
器
动辄
使用一个字
节
——
甚至四个字
节
来存放一个布
尔变
量。面
对
1/32
的使用效率,叫人怎能不心疼?
要想
节约
空
间
也不是没有
办
法,代价是写更多的代
码
:用
“&”
操作将
变
量的某一个
bit
取出来,一个字
节
就可以存放
8
个布
尔变
量。但是,
这
个代价是比
较
重的,重到足以
让
程序
员
望而生畏的地
步
。
bitset
应
运而生,它可以方便地管理一系列的
bit
位而不用程序
员
自己来写代
码
。
更重要的是,
bitset
除了可以
访问
指定下
标
的
bit
位以外,
还
可以把它
们
作
为
一个整数来
进
行某些
统计
,如:
b.any();//b
中是否存在置
为
1
的二
进
制位?
b.count();//b
中置
为
1
的二
进
制位的个数
……
不
过
,
话说
回来,我
还
是不
习惯
用
bitset
,原因在于我是从
C
语
言
转
到
C++
的。
详细问题
留到后一篇文章中
讨论
。
标题:
:迭代器:指
针
与数据
库杂
交的后代
迭代器与数据
库
的相似之
处
在于
end()
函数返回
值为
“
指向末元素的下一个
”
。跟数据
记录
集的
eof
这么
相似。
话说
回来,熟
练
于
C/C++
的程序
员
一定不会忘了,利用下
标访问
数
组时
用的
总
是用
“
半
开
半
闭
区
间
”
。就拿
“int a[10]; for(int i=0; i!=10; i++)”
来
说
,下
标为
10
就可以
视为
“
最后一个元素的下一个
”
。只是以前不会有
这么
明
显
的思考。
迭代器与指
针
的相似之
处
在于它的
“
解引操作符
”
,居然就是一个
“*”
号。而且存在着
“ivec[n]”
和
“*ivec”
这
两个完全等价的操作。而且
还
支
持算
术
运算来移
动
要
访问
的元素。
迭代的
const
用法与指
针
有个区
别
:
“vector::const_iterator ivec = vec.begin();”
定
义
的
ivec
并不是常量,只是它指向的元素会得到保
护
。如果要定
义
本身
为
const
的迭代器,要用
“const vector::ivec = vec.begin();”
。
指
针
是
这样处
理的:
const int *p = &i;//p
指向的
变
量是
const
int* const p = &i;//p
是
const
地雷:任何改
变
vector
长
度的操作都会使已存在的迭代器失
败
。例如,在
调
用
push_back
之后,就不能再信
赖
指向
vector
的迭代器的
值
了。
笔
记
:我不得不相信所
谓
的迭代器其本
质
就是一个指
针
了。因
为
vector
支持
动态
增
长
,而且保
证
内存的
连续
。所以,
每
次改
变
它的
长
度都会
导
致
释
放已有内存、重新申
请
内存。
指
针
当然得失效
。
标题:
:
for
语
句的条件思
考
对
于
for
语
句,我几乎
总
是
这样
写的:
“for(int i=0; i
。但是,按本
书
的
说
法,我
这样
写似乎同
时
犯了两个
错误
。
1
、本
书
中写
for
语
句中的第二个表达式(条件表达式)
时总
是用
!=
,而不用
<
。
这
几天来
虽
然心里
觉
得奇怪,但是一直没去思考
这
里面的含
义
。本
书
没有急着告
诉
我
“
所以然
”
,只是提醒我
读
完本
书
的第二部分后就会明白。我等着吧
:)
2
、我
总
是
觉
得用
Max
这样
一个
变
量去代替某个需要用函数才
能返回的
值
可以增加运行效率,但是本
书
的建
议
与我的理解相反。它建
议
用
“i
,
“
因
为
数据
结
构可以
动态
增
长
,
……
如果确
实
增加了新元素的
话
,那
么测试
已保存的
size
值
作
为
循
环结
束条件就会有
问题
,因
为
没有将新加入的元素
计
算在内。
”
同
时
,本
书为
了打消运行效率的
顾虑
,
还
提到了
“
内
联
函数
”
这
个概念。
现
在
还
没有到介
绍
内
联
的
时
候,但是
C++
必
须
是一个有机体,在
讲
述某一个知
识
点的
时
候,不可能完全避免另一个知
识
点。
所以,我
还
是建
议
初学者不要急着
阅读
本
书
。
标题:
:
标
准
库
vector
类
型
终
于
让
“
类
模板
”
上
场
了,可惜的是,在本
书
的第三章,
还
不能
彻
底
让类
模板浮出水面,只能将就着提一下。
引用:使用
类
模板可以
编
写一个
类
定
义
或函数定
义
,而用于多个不同的数据
类
型。
……vector
并不是一
种
数据
类
型,而只是一个
类
模板,可用来定
义
任意多
种
数据
类
型。
笔
记
:
“vector ivec;”
中,
“vector”
是一个数据
类
型,
C++
支持
类
模板,以后大量接触
类
模板
时
,
这
个一定要
时
刻注意。
引用:在元素
值
已知的情况下,最好是
动态
地增加元素。
……
虽
然可以
对给
定元素个数的
vector
对
象
预
先分配内存,但是更有效的方法是先初始化一个空的
vector
对
象,然后再
动态
地增加元素。
将
vector
与
string
对
比,可以
轻
松地
记
住
empty()
、
size()
函数和
[]
、
=
、
==
、
!=
等运算符。不
过这
似乎有个前提:
对
C
语
言比
较
熟并且
摈
弃
VB
的
String
变
量
类
型。因
为
,
string
虽
然是一个
变
量,但是如果缺少
对
“char []”
的理解,将注定不能理解它。
——
所以,我很反
对
某些人
说
的
“
可以不学
C
语
言直
接学
C++”
论调
。
标题:
:
标
准
库
string
类
型
习惯
了
VC++
的
CString
类
,而此前用的又是
C
语
言,所以,
压
根没有看一眼
string
。
现
在既然
书
中
专门讲
它,我就看一看吧。
定
义
与初始化:
string s1;
string s2(s1);
string s3("value");
string s4(n, 'c');//
由
n
个
c
组
成的一串
string
对
象的
输
入除了可以
“cin >> s1;”
以外,
还
可以将
cin
和
string
对
象一起作
为
getline()
函数的参数
“getline(cin, s1);”
,而且
这
个函数的返回
值还
是
cin
。
除此之外,
string
类还
有
empty()
、
size()
等函数和
[]
、
+
、
=
、
==
等运算符。
地雷:
empty()
函数的功能并不是将
对
象置空,而是
测试
是否
为
空。
这
可是与
CString
类
的
Empty()
函数不一
样
的哦!
本
书还详细
介
绍
了
empty::size_type
类
型存在的原因。在使用
VC++.NET
的
时
候,我已
经见过
了
size_t
类
型
——strlen()
函数的返回
值
就是
size_t
类
型
对
象。当
时
我也没有
细
究
这
个
东
西,想来
应该
是
为
了支持
64
位机而
设
的吧。不
过现
在我知道了,原来我想得
还
不
够
。
引用:通
过这
些配套
类
型,
库类
型的使用就能与机器无
关
。
关
于
“+
运算必
须
至少包含一个
string
类
型
”
,
这
可能
让
初学者摸不着
头脑
。比如
“s1 + s2”
、
“s1 + "hello"”
、
“"hello" + s1”
是合法的,唯独
“"hello" + "world"”
不合法。我在看懂
“
运算符重
载
”
之前也没有明白
这
个。本
书
由于
刚开头
,离
“
运算符重
载
”
还远
着
呢,所以只告
诉读
者
“
然
”
、没有告
诉读
者
“
所以然
”
。我也不写了。
引用:下
标
操作可作左
值
。
笔
记
:人
类
一思考,上帝就
发
笑。
这
就是一个例
证
。下
标
操作可作左
值
有什
么
好
说
的?早就用
过
啊?比如
“char s[]="abcde"; s[2]='t';”
。可是
现
在的不是指
针变
量,而是
类
成
员
,
对
于
“string s("abcde");”
来
说
,
“s[n]”
就不是
简单
操作一个内存了,而是从
“[]
重
载
函数中返回了一个
值
”
。
这
个
值
可以做左
值
,并不是想当然的事
。
标题:
:命名空
间
的
using
声明
虽
然
实际
工作中
绝
少用到
cin
和
cout
,但是我
还
是
记
得要用它
们
必先
“using namespace std;”
,至于
为
什
么这样
做,三个字:不知道。
本
书
从一
开头
就用到了
cin
和
cout
,但是它没有
using
,而是
每
一次用到它
们
都写成
“std::cin”
和
“std::cout”
,同
时
提醒
说这
两个字名是来自
std
命名空
间
的。于是,我似乎明白了
“using namespace std;”
的作用。
也
许
是
C++
实
在太
难
,
作者
迟迟
不介
绍
更深入的内容,到了第一部分、第三章,
还
没有打算深入
using
,但又不得不
讲
一点,于是提了
这样
两行:
using std::cin;
using std::cout;
至此,究竟什
么
是命名空
间
,命名空
间
是干什
么
的,我
还
是不懂。也
许书
后面会提吧
。
标题:
:
头
文件
头
文件的用
处
主要是代
码
重用
——
重用不
仅仅
是
为
了减少工作量,
还
可以保
证每
一次重用都是完全相同的内容。
正因
为头
文件可以多次重用,所以要防止有些只能出
现
一次的代
码
放
进头
文件中。比如
变
量的定
义
只能有一次,声明(含
extern
且不含初始化)却可以有多次。函数也是。
引用:一些
const
对
象定
义
在
头
文件中。
笔
记
:看到
这
里,
总
算想通了前面的困惑:
为
什
么
const
常量的作用域
仅为
一个文件。正如我前面的估
计
,
const
常量是不
开
劈内存空
间
的,代
码
在
编译
的
时
候就被直接替
换
成常量
值
。而且,
编译
器
对
多个
CPP
是分
开编译
的。所以,它必
须
要
编译
的
时
候知道
该
常量的
值
。如果常量的作用域也是全局的,那
么
我告
诉编译
器
“
该
常量的
值
在另一个
CPP
中
”
将使其无可适从。
避免
头
文件被重
复
包含是个比
较
有岐
义
的
说
法,
头
文件既然可以被多次包含,
为
什
么
又要避免重
复
包含?
还
是因
为
CPP
文件的
单
独
编译
。在
编译
任一个
CPP
文件
时
,都要知道文件的内
容,所以,如果两个
CPP
都包含了同一个
头
文件,那
么头
文件就要被
编译
两次。但是同一个
CPP
如果重
复调
用
——
甚至可能循
环调
用
——
了某一个
头
文件,
则
要及
时
避免。
#ifndef...#define...#endif
可以起到
这
个作用。
对
了,我一直没想通
VC++.NET
的
“#pragma once”
是怎
么
工作的。它
为
什
么
不需要用
变
量来
标记
不同的
头
文件?
为
什
么
只需要一行也能
标记
出整个
头
文件的所有内容?
第一部分、第二章
结
束
。
标题:
:
class
与
struct
首次考
虑
class
与
struct
的
关
系源自我
对类对
象占用内存数的
观
察。我
发现
VC++6
的
CString——
这么强
大的
类
——
它占内存居然是
4
字
节
(
sizeof(CString)
为
4
)。那
时
我就
认为
,
类对
象
仅仅
在内存中存放成
员变
量(《
C++ Primer
》称作
“
数据成
员
”
)而不存放成
员
函数。后来,
读
林
锐
博士的《高
质
量
C/C++
编
程指南》
时
,才看到了比
较
正
规
的
说
法:
C++
中
class
与
struct
没有本
质
的区
别
。
——
当
时
我将
这
句
话给
我朋友看
时
,他
还
表
现
出不相信的神色。
读
《
C++ Primer
》
时
,我已
经
熟知了
class
与
struct
的
关
系。但是我
还
是
细细
地没有放
过书
中的任何一个字,
还
是作点
记录
吧:
引用:用
class
和
struct
关键
字定
义类
的唯一差
别
在于默
认访问级别
:默
认
情况下,
struct
的成
员为
public
,而
class
的成
员为
private
。
笔
记
:不
过
,一般情况下公司与公司
间规
定
协议时
,喜
欢
将
类
定
义
写成
struct
,我想
这
可能是源于程序
员
从
C
语
言
继
承来的
习惯
,
还
有就是
协议
内容一般只包含数据,不包含函数,也
没有必要
规
定安全的
访问级别
。
引用:
编
程新手
经
常会忘
记类
定
义
后面的分号,
这
是个很普通的
错误
!
笔
记
:
书
中将
这
段文字
标
作一个地雷,我
还
是引用一下吧
。
标题:
:枚
举
枚
举
是我向来不太喜
欢
用的
东
西,几乎我
见过
的
每
本
书
都是
这样
介
绍
枚
举
的:
enum weekday {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday};
我看到
这
里,
总
是想:多浪
费
啊,与其
这样
,
还
不如直接用
0-6
这
些数呢。以后要写
“weekday Today = Sunday;”
,哪有
“int Today = 0;”
舒服。
本
书
似乎早看
透了我的心理,于是,
讲
枚
举
不从枚
举
入手,偏从
const
常量入手:
引用:
const int input = 0;
const int output = 1;
const int append = 2;
虽
然
这种
方法也能奏效,但是它有个明
显
的缺点:没有指出
这
些
值
是相
关联
的。
枚
举
提供了一
种
替代方法,不但定
义
了整数常量集,而且
还
把它
们
聚集成
组
。
笔
记
:大
师
就是大
师
,他
们
写
书
能切中要害,
让读
者明白
标
准制定者的苦心。
另外,本
书
在
这
儿冷不丁地提了一个冷冰冰的概念:常量表达式。
引用:常量表达式是
编译
器在
编译时
就能
够计
算出
结
果的整型表达式。整型字面
值
常量是常表达式,
……
笔
记
:
让
我先汗一个。常量表达式必需是整数?我怎
么
不知道?那
“double pi = 3.14159;”
后面的字面
值
不属于常量表达式
吗
?
这
是个疑
问
,先
记
下来。
引用:枚
举类
型的
对
象的初始化或
赋值
,只能通
过
其枚
举
成
员
或同一枚
举类
型的其它
对
象来
进
行。
笔
记
:
这
跟
现实
生活中的
“
做人要
专
一
”
有点相似,呵呵。
简单
地
说
,你
选择
了用名字来代替数
值
,那就得始
终
如一地使用名字,不可以用数
值
或其它表达式。
比如
“weekday Today = Sunday;”
不可写成
“weekday Today = 0;”
,
虽
然
Sunday
就是
0
。
标题:
:
typedef
长
期以来,我一直在疑惑:
typedef
这
个
词
要它干什
么
?因
为
没有它我照
样
可以完成所有任
务
。而有了它我反而
觉
得无法适
应
。比如
“UINT i;”
,
为
什
么
不写作
“unsigned int i;”
?本
书
用
简
短的三句
话
告
诉
我它存在的意
义
:
引用:
typedef
通常被用于以下三
种
目的:
为
了
隐
藏特定
类
型的
实现
,更
强调
使用
类
型的目的;
简
化
复杂
的
类
型定
义
,使其更易理解;允
许
一
种类
型用于多个目的,同
时
使得
每
次使用
该类
型的目的明确。
笔
记
:第三
种
目的
对
我的启
发
特大。以后就
“typedef unsigned int age;”
,呵呵。
标题:
:引用
引用是
C++
的特色,一般用在函数的参数中。按有些
书
本的
说
法,叫
“
普通
变
量的用法,指
针变
量的效果
”
。
书
中本
节
没有
讲诉
引用在函数参数中的用法,只提了
“
给变
量起个
别
名
”
这
一个用
处
(
毕
竟本
书
才
开头
)。
说实
在的,如果撇
开
函数参数,
还
真想不到引用有什
么
用
处
。
引用
这
个概念本身也不
难
理解(除了
对
C
程序
员
来
说
有些不
习惯
以外),但是引用的符号却增加了理解它的
难
度,我
经
常在
论坛
上看到有初学者
对
“&”
和
“*”
两个符号的疑惑,他
们问
的
问题
可以
说
非常基
础
,但却表
现
出了
这
个
问题
的
难
以理解的特点:
C
语
言中的指
针
已
经够复杂
的了,加再上一个引用,引用与指
针
有着千
丝
万
缕
的
联
系,
这
就算了,而且
还
用了
“&”
这
个符号。真
让
初学者忙昏了
头
。呵呵。下面四行程序,用到了两个
“&”
和两个
“*”
,但是它
们
的意
义
却全然不同:
int a;
int &b = a;//&
用在定
义
中
仅
表示
变
量的性
质为
引用
int *c = &a;//*
用在定
义
中
仅
表示
变
量的性
质为
指
针
,
&
用在表达式中表示取地址
int d = *c;//*
用天表达式中表示取指
针变
量所指的
变
量的
值
写下以上文字,我
觉
得有些越
权
了。
这
些内容估
计
在本
书
后面会
详谈
的,我心急了点
。
标题:
:
const
常量的作用域
仅为
一个
CPP
文件
如果将
变
量定
义
放在任何
{}
的外面,
则该变
量是全局的,
这
个
规则
不适用于
const
常量。
以前
虽
然心里
隐隐约约
有
这
个感
觉
,但是从未正面考
虑过这
个
问题
。之所以
隐隐约约
有此感
觉
,是因
为
我
认为编译
器并不
为
const
常量
开
劈内存空
间
。
我曾
经专门
做
过测试
:程序如下:
const int i = 5;
int *p;
p = (int *)&i;
cout << *p << "/t" << i << endl;
(*p)++;
cout << *p << "/t" << i << endl;
测试结
果
发现
const
常量也是可以通
过
某些途径改
变
其
值
的,但是改
变
不起作用。我的解
释
是,
编
程器在生成机器
码时
将所有的
i
直接替
换
成了
5
。使得上面两个
cout
语
句都成了
“cout << *p << "/t" << 5 << endl;”
。
回想起以前做
过
的
测试
,便不
难
理解
“const
常量的作用域
仅仅为
定
义
它的
CPP
文件
”
,因
为编译
器是
对每
个
CPP
文件
单
独
编译
的,只有在
连
接
时
才会
去理会
CPP
之
间
的
关
系。
虽
然本
书
才看了个
开头
,
书
中
还说
“
我
们
将会在
2.9.1
节
看到
为
何
const
对
象局部于文件
创
建
”
。我不打算跳
跃
式地
阅读
本
书
,所以,我
还
是先保留我自己的理解吧。
const
常量也
还
是可以成
为
全局常量的,方法是在定
义
的
时
候就加上
extern
。看到
这
里,我
终
于明白了《
extern
的困惑》中的困惑:原来,那
种
“
有些多余,而且增加了出
错
的可能性
”
的做法在常量的
处
理上派上了用
场
。
标题:
:
变
量的作用域
作用域
这
个概念是程序
员
都耳熟能
详
的知
识
点。
这
部分知
识
我几乎可以跳
过
,不
过
我
还
是
认
真
阅读
了相
关
内容。
阅读过
程中
还
是有体会的:
有无数
书
本曾
经
提醒
过
我:少用(尽量不用)全局
变
量,多用局部
变
量。其
实
,即使是局部
变
量,也
还
有作用域大小的,局部
变
量的作用域也是越小越好。原因自然和少用全局
变
量一个道理。正如
书
中所言:
“
通常把一个
对
象定
义
放在它首次使用的地方是一个很好的
办
法。
”
以往,我使用局部
变
量
总
是把它放在函数的
开头
,表示
这
些
变
量在本函数中起作用。
唯一的例外是
for
语
句的循
环变
量(
for (int i=0; i
)。以后我就改改,把局部
变
量的作用域
缩
小到
仅仅
用到它的
语
句
块
。
标题:
:
extern
的困惑
extern
用来告
诉
程序:你不用
为
我的
变
量
开
劈内存空
间
,你只要知道
这
个
变
量
别处
已
经
声明
过
了。所以,我
总
是在程序包含多个
CPP
文件
时
才
这样
用:
1
、在某一个
CPP
文件中直接定
义变
量,如
int i = 0;
2
、其它
CPP
文件中声明
变
量,如
extern int i;
但是,
书
中介
绍
exturn
还
可以用来定
义
,如:
extern double pi = 3.1416;
特点是
该
extern
语
句包含
变
量的初始化。
我
觉
得
C++
标
准
这样
做有些多余,而且增加了出
错
的可能性。因
为
“extern double pi = 3.1416;”
完全可以用
“double pi = 3.1416;”
来代替,
这样
做可以
让
定
义
与声明划清界
线
。
毕
竟定
义
只能有一次,而声明可以无数次。如果没有
这
个特性,可以
让
程序
员简单记为
“
不
带
extern
的只能有一次,
带
extern
的可以有无数次,而且
extern
同
时
不能指定初始
值
。
”
这
一特性的支持,使原本
简单
的
规则变
得
复杂
,但没有
带
来灵活(众所周知
,
C++
的
复杂
是以高度灵活
为补尝
的)。
这
个段落
还让
我
认识
到了我以前使用
extern
的不足。我以往在同一个
CPP
文件中只使用一次
extern
,所以我往往是在文件
头
部用
extern
语
句来声明一下
变
量,
这样
做
虽
然没有什
么错
,但却会
导
致上下翻
查
:有
时
在文件的某一
处
用到
变
量
时
,想看一下它的声明,不得不把
滚动
条拖到
顶
上去
查
看。如果在用到它的段落
开头处
再声明,明
显
比
顶
部声明要好一些
。
标题:
:
变
量初始化
int ival(1024);//
直接初始化
int ival = 1024;//
复
制初始化
以前的我常用第二
种
用法,原因很
简单
:从来没
见过
第一
种
用法。直到后来学
习
了林
锐
博士的《高
质
量
C/C++
编
程指南》。
那本
书
在
讲类
的构造
时说
道:
CMyClass b = a;
这种
形式看起来像
赋值
,
实际
上
调
用的是拷
贝
构造函数。
那本
书给
我的感
觉仅仅
停留在
类变
量的初始化中,一直没有
过
渡到内置
变
量
类
型。直到后来,我在用
VC++.NET
的向
导
功能
编
程序
时
,才
发现
向
导
帮我
产
生了
类
似于
“int i(100);”
这种语
法来初始化。
本
书
重点
强调
:初始化不是
赋值
,
引用:当定
义
没有初始化的
变
量
时
,系
统
有
时
候会帮我
们
初始化
变
量。
这时
,系
统
提供什
么样
的
值
取决于
变
量的
类
型,也取决于
变
量定
义
的位置。
……
内置
类
型
变
量是否初始化取决于
变
量定
义
的位置。在函数体外定
义
的
变
量都初始化
为
0
,在函数体内定
义
的内置
类
型
变
量不
进
行自
动
初始化。
……
(
类类
型)通
过
定
义
一个特殊的构造函数即默
认
构造函数来
实现
的。
这
个构造函数被称作默
认
构造函数,是因
为
它是
“
默
认
”
运行的。
……
不管
变
量在哪里定
义
,默
认
构造函数都会被使用。
笔
记
:林
锐
博士的《高
质
量
C/C++
编
程指南》中
说
,如果
类
没有定
义
无参数构造函数或拷
贝
构造函数,系
统
会自
动产
生
这
两个构造函数,它
们
采用最
简单
的
“
值传递
”
和
“
位拷
贝
”
来完成构造。
标题:
:
变
量和
变
量名
引用:
对
象是内存中具有
类
型的区域。
笔
记
:
这
句
话说
得很直白,也只有
这样
面向
C++
熟
练
工的
书
才可以
这样说
,
毕
竟初学者不知道内存与
变
量的
关
系,或者
还
没考
虑
到。
引用:
C++
还
保留了一些
词
用作各操作符的替代名。
这
些替代名用于支持某些不支持
标
准
C++
操作符号集的字符集。它
们
也不能用作
标识
符(此
处
指
变
量名,
偷
猫
标
)。
一般的
书
只提到
C++
变
量名不可以使用
关键
字,本
书还额
外提了
这样
一句,然后列出一个表,有
and
、
bitand
、
compl
、
not_eq
、
or_eq
、
xor_eq
、
and_eq
、
bitor
、
not
、
or
、
xor
。不
过
据我在
VC++.NET
上
测试
,
这
些是可以用作
变
量
标识
符的。
都
说变
量名可以含
“_”
,
还
可以用
“_”
开头
,但我一直没有
试过
光光用一个
“_”
来做
变
量名,正好
习题
里有,我就
试
了一下,果然可以的
。
标题:
:
cout << (wchar_t
类
型
变
量
)
体
验
《
C++ Primer
》
说
了,字符常量或字符串常量前加
L
,表示
wchat_t
类
型,于是我
试
了一下:
程序如下:
char a = "a";
wchar_t b = L"a";
cout << a << endl;
cout << b << endl;
结
果如下:
a
0012FEBC
晕
,怎
么
出
现这
个
结
果?
让
我再
试
。
程序如下:
char a = 'a';
wchar_t b = L'a';
cout << a << endl;
cout << b << endl;
结
果如下:
a
97
由此可
见
,
cout
的
<<
运算符没有
对
wchat_t
的重
载
(或不健全)
。
标题:
:内置
类
型之精度
选择
引用:
……
大多数通用机器都是使用和
long
类
型一
样长
的
32
位来表示
int
类
型。整型运算
时
,用
32
位表示
int
类
型和用
64
位表示
long
类
型的机器会出
现应该选择
int
类
型
还
是
long
类
型的
难题
。在
这
些机器上,用
long
类
型
进
行
计
算所付出的运行
时
代价
远远
高于用
int
类
型
进
行同
样计
算的代价。
……
决定使用哪
种
浮点型就容易多了:使用
double
类
型基本上不会有
错
。在
float
类
型中
隐
式的精度
损
失是不能忽
视
的,而
double
类
型精度代价相
对
于
float
类
型精度代价可以忽略。
事
实
上,有些机器上,
double
类
型比
float
类
型的
计
算要快和多。
long double
类
型提供的精度通常没有必要,而且
还
需要承担
额
外的运行代价
。
标题:
:内置
类
型之
int
和
bool
第一部分,第二章
引用:
C++
标
准
规
定了
每
个算
术类
型的最小存
储
空
间
,但它并不阻止
编译
器使用更大的存
储
空
间
,事
实
上,
对
于
int
类
型,几乎所有的
编译
器使用的存
储
空
间
都比所要求的大。
笔
记
:确
实
如此,
VC++
中
int
和
long
是一
样
大。
VC++.NET
增加了
对
_int64
的支持。
引用:字符
类
型有两
种
:
char
和
wchar_t
,
wchar_t
类
型用于
扩
展字符集,比如
汉
字和日
语
。
笔
记
:我怎
么
不知道
wchar_t
?我自己用包含
汉
字的字符串
时
用的也是
char
。
:(
看到
bool
型,我心里
对
VC++
有些气
愤
。因
为
VC++
里有一个
BOOL
宏。它的原型
为
“typedef int BOOL”
。既然
C++
标
准已
经
有
bool
型,
VC++
加入
BOOL
的用意很明
显
:迎合更多的
编
程
习惯
。但是,即使非要增加
对
BOOL
的支持,我
认为
原型
应该这样
:
“typedef bool BOOL”
,
这样
更易于理解。
当然了,
长
期以来我一直没有注意到
bool
确
实
也不
应该
。
但是正是
BOOL
的存在,阻碍了我
对
bool
的理解。
标题:
:第一章:快速入
门
第一章:快速入
门
本章的存在使本
书变
得不像一本
“
规
范
书
”
,似乎成了
“
入
门书
”
,
这
可能是后来版本新加入的内容。以至于
这
一章
节
被排除在任何一个
“
部分
”
之外。
本章
“
无厘
头
”
地
简
要介
绍
了
cin
、
cout
、注
释
、
while
、
for
、
if
等概念。
这么
多
东
西,
每
一个都介
绍
点皮毛,然后
组
合成一个
综
合
实
例。
我称其
为
“
无厘
头
”
有以下原因:如果本
书
面
对
不了解
C++
的
读
者,那
么这
一章似乎是有用的,但是
C++
的入
门
者使用本
书显
然很
难
入
门
,我不了解国外的情况怎
样
,至少我身
边
的人是
这样
;如果本
书
面
对
已
经
了解
C++
的
读
者,那
么
,
这
些
东
西都不用介
绍
,
读
者也可以看懂那个
“
综
合
实
例
”
,而且所
谓
的
“
综
合
实
例
”
也没有存在的必要。
不
过
有一个小小的收
获
:
int main()
{
return -1;
}
如果返回
值为
-1
,
Windows
的
CMD
中运行也没有任何
额
外信息,
说
明
Windows
并不
报
告运行失
败
。
标题:
:《〈
C++ Primer
〉
阅读
笔
记
》前
言
在
读
本
书
之前,我已
经
有
过
一段
编
写
C++
程序的
历
史,如果
连
C
语
言也算在内,可以追溯到十年前。用
BASIC
语
言
编
程序的
历
史
则
有十四年
(1992-2006)
。
长
期
编
程序中所使用的参考
书
无非有两
种
:介
绍
算法的
书
和介
绍语
法的
书
。我所
买
的参考
书
往往是同
时
介
绍
两者的。而
对语
法的介
绍
,
则
只是基于某一个
编译
器。
于是,
这
十年来,我所学
习
的
“C/C++”
,从本
质
上
说
只是
Turbo C
和
Visual C++
,
对
C/C++
本身的理解也是被
编译
器
过滤
的内容。不是我不想去了解
C/C++
的本
质
,只是我
对
C/C++
的
“
法典
”
有着与生
俱
来的恐惧。
这
个恐惧直到我
发现
了《
C++ Primer
中文版》,当我捧起
这
本
C++
的
“
圣
经
”
时
,我
终
于能理解
为
什
么每
有几十万人冒着被
踩
死的危
险
前去朝圣。
请
允
许
我用
“
圣
经
”
来比
喻这
本
书
,我知道
这样
并不合适,因
为绝
大多数中国人并不知道
“
圣
经
”
的地位,但是我搜遍大
脑
的
每
一个角落也找不到其它合适的比
喻
。
这
源于中国人缺乏信仰。
在半年前,我曾
经带
着无限的敬仰
阅读
了林
锐
博士的《高
质
量
C/C++
编
程指南》,并且及
时
培
养
/
修正了我的
编
程
习惯
。当然了,正如《
C++ Primer
》所言:
“
什
么
是
C
或
C++
程序的正确格式存在着无休止的争
论
……”
。所以,我所修正的只是
“
自由体
”
,而不是与林
锐
矛盾的方面。令我感到欣慰的是,《
C++ Primer
》居然也用一定的笔默来
讲
格式与
风
格,不同的是,它同
时
介
绍
几
种风
格,然后作出一个略
带倾
向性的建
议
。同
时
,
书
中
还
告
诫读
者:
“
一旦
选择
了某
种风
格,就要始
终
如一地使用。
”
从
现
在起,我就要捧起
这
一本四五厘米厚、七百多
页
的圣
经
,在
阅读过
程中,
难
免会有重点、要点要
记录
。我比
较爱书
,不愿在
书
上做
标记
,只好
选择
了
BLOG
这种
形式来做
读书
笔
记
。
谨
以此作
为
我的
BLOG
开
篇
说
明吧
。