C++ Primer读书笔记

 
注:本文 www.Eachfun.com
          (整理说明:本资料是我在网上无意间找到的,读起来感觉不错,但由于原文是每章一个网页的格式,读起来不是很习惯,而且也不方便保存,所以我花了 2 个多小时的时间将所有网页的内容综合整理了一下,但最后才发现,文章的顺序颠倒了,所以各位如果愿意阅读本文的话,请从后面向前读,每个红色“标题”代表一章,如有不便还请各位见谅,或到原文网站阅览)
标题: 函数再
函数是 C++ 提出来的概念,但是在 C 中却未必没有。比如 “1+3” “1.0+3.0” 然都是加法,做的却不是同的操作: 编译 器要因操作数的不同而 用不同的加法操作。只是 C 言中除了内部 量可以参与运算以外,没有 这么 高深的概念。 构体 也只是内存数据的 组织 方法,而不 整个 构体的 理。所以,在 C 编译 器明明做了 似于重 的事情,却可以像雷 做好事不留名
   C++ 展出了 ,并且 予了 很高的期望, 象也能像内置 象一 参与一切运算。那 ,就拿加法运算来 编译 器如何知道 类对 象的加法 该调 用哪一个 详细 的操作代 ?于是,即使不出 普通函数的重 ,至少运算符是要重 的。
  林 博士在《高 C++/C 程指南》中 函数的必要性提了另一个理由: 的构造函数名称必 名相同,而 常要定 多个不同的构造函数。那就只好重 了。
   于普通程序 ,我 完全可以不用考 这么 深。重 函数 至少 还带 来了另一个好 :不用 记忆 多个不同的函数名了,也不用 函数起名而 汁了。不 书还给 出了一个建 :并不是任何 候都有必要重 函数的,有的 候不同的函数名可 以直 来好多信息, 用重 只是 牲了名称中的信息。
 
标题: :重 函数的概念
引用:出 在相同作用域中的两个(可以是两个以上 —— 猫注)函数,如果具有 相同的名字而形参表不同, 函数。
  本 节开头 第一句 出了重 函数的定 :重 函数必 符合两个条件:一是出 在相同的作用域中、二是函数名字相同而形参表不同。
  其中第一个条件一般人往往是不去想的,其 函数名相同而作用域不同的函数大大存在,比如在 MFC 中就有。它 是完全不相干的函数。
  第二个条件 可以 详说 一下:函数名字相同当然不在 下, 是函数被称 的根源。之于形参表不同,可能表 在形参个数不同、可能表 在形参 型不同、 可能表 在形参 序不同。
  如果要 可以 不是重 函数的情况。
  一、如果既在同一作用域下、名称也相同、形参表也相同, 后者被 视为 前者的重 声明。 —— 函数可以重 声明,因 函数的声明并不 生目 ,但是函数的定 不允
  二、如果既在同一作用域下、名称也相同、形参表也相同,但是返回 不同, 后者被 视为错误 的声明。函数不可以只凭返回 来区分,因 为调 用函数的 候只凭名称和形参来 选择 函数,而不凭返回 。再究其原因,一是因 函数的返回 可以被 弃;二来即使不 弃,将返回 值赋 予另一个 量之前没必要 检查 我需要什 么样 的返回 ,而能否 赋值 也与函数本 身无
  三、有些 候看起来形参表不同, 实际 上是完全相同的, 本第 229 页讲 了四 组这样 的例子:
Record lookup(const Account &acct);
Record lookup(const Account &);//
在于有没有 形参命名
typedef Phone Telno;
Record lookup(const Phone&);
Record lookup(const Telno&);//
只是 给类 型取了个
Record lookup(const Phone&, const Name&);
Record lookup(const Phone&, const Name& = "");//
在于 形参提供了默 认值
Record lookup(Phone);
Record lookup(const Phone);//
在于是否 const   
  其中第三 可能会 生函数的形参个数不同的假像,其 可缺省的形参并没有减少形参的个数。第四 有点不容易搞清:因 有的 候可以凭是否 const 来重 ,比如引用 传递 和指 针传递
 
标题: :文件的 组织
一个程序往往由多个源文件 成, 些代 究竟 应该 放在哪个源文件里、哪些代 可以放在同一个源文件里、哪些代 必需分 放。 是一个管理 面的 问题
   它是管理 面的 问题 ,是因 为这 些代 组织 往往没有惟一的准 。但是它 们还 是有一定的 律的。
  首先, 件的 维护 是一个 复杂 的系 工程。代 组织应该 有利于 维护 应该 尽量把直接相 的内容放在同一文件、不相 的内容放在不同的文件里。如果 些代 码还 和疏,那就要分不同的文件 来存放了。
  其次, 件的代 是一个 格的 组织 体系。不同的内容之 可能是并列的,也可能有必要的先后 系。于是在 “#include” 候要注意 序。
  最后,也是最重要的一点,有些代 在同一工程中可以重用(或必 重用),有些代 在同一个工程中只能出 一次。可以重用的有 的声明、函数的声明、 量的声明等,不可以重用的是 体、函数的 体、 量的定 等。那 ,把可以重用的内容放在 h 文件中,把不可以重用的放在 cpp 文件中是一个好 法。
  拿 的声明和 例,如果把一个 的所有内容一古 放在同一个文件中,将可能出 现问题 。因 在其它用到 类实 例的地方都必 须让类 的声明 ,所以我 往往在文件 部加个 “#include” 体也被 编译 多次,在 时产 生冲突。
  在前文中曾提到 ,内 函数是惟一允 (也是必 )在 编译时让 函数 体可 的的函数。所以内 函数可以放在 h 文件中。 C++ 规则 中有一句正好与此照 :在 的声明中直接写出的函数被 认为 是内 函数。
   Visual C++ 给类 的文件起默 ,文件名往往与 名一致。如果 名由 “C” 开头 文件会是除去 开头 “C” 字以外的其它文字。如 “CMyClass” ,它的代 存放在以下两个文件中: “MyClass.h” “MyClass.cpp” 中。原因是 VC++ 议类 名以 C 开头 ,至于 在文件名中不出 现开头 “C” ,可能是出于微 习惯
 
标题: 的构造函数
引用:构造函数是特殊的成 函数。
  笔 :构造函数的确是一 特殊 的成 函数。它的特殊性至少表 在以下几个方面:一是它的 用不用程序 操心,只要 类对 象被 建它就会被 用,而且它不允 被程序 员显 式地 用。二是它 是必需的,如果程序 员偷懒 编译 器将自 动创 简单 的构造函数。三是它 的名字不用程序 多考 ,直接与 名相同。四是它 没有返回
  下面 详说这 几个特性:
  一、它 类对 象被 动调 用, 象可能有以下方法:程序中用声明 量的 句直接声明 建,或者在程序中用 new 关键 动态创 建。 方法都可以 象,也都可以 象数 。只要有一个 象被 建,构造函数就被 用一次。
  如果程序 式地 用构造函数那是不行的。正因 如此,构造函数中 有一 特定的部分叫 初始化列表 ,通 它程序 可以 用基 或成 的构造函数。必竟 设计 千差万 ,如果某个 的基 或(和)成 有多个构造函数,那 该类 指定用哪一个构造函数,否 则类 的功能将大打折扣。 用构造函数不是程序 的事,程序 应该 管也管不了。初始化列表 解决 问题 而生,所以只有构造函数才有初始化列表,其它函数不能有。
  上面 到的 大打折扣 究竟是怎 的折扣呢?如果 不能指定基 和成 用哪一个构造函数,那就只好 让编译 器去挑了,构造出来的 象往往不符合要求,只好 用基 和成 的其它函数,比如 赋值 函数或其它 行参数 定的函数 —— 当然,基 和成 包含 这样 的函数。 这样 就浪 源。
  二、 包含构造函数 —— 确切地 是必 包含无参数构造函数和拷 构造函数 —— 原因是因 用是自 的。如果 两个函数根本就没有,你 如何 用?所以, C++ 也不含糊,你要是 得写,它就帮你写一个 简单 的。 简单 就意味着至少要 失一些功能,如果 类设计 得比 较复杂 (比如包含指 操作) 可能引起灾 性事故。
  三、函数名与 名一致。构造函数的名称是必 特殊的,即使 个特殊不表 在与 名相同,也必 找到另一个 规则 实现 。因 要自 动调 些函数,你就必 须让 知道哪些函数是构造函数。
  第四个特性直接改 C/C++ 言的一条 规则 C 定,如果函数没有明 指出返回 型,那 C 认为 返回 int 型。 C 言之所以可以有 规则 ,一是因 返回 int 的函数很多,二是因 即使没有返回 ,也必 指明 void 。当 制定 规则 的人无法 料到, C++ 中居然会出 void 都不是的返回 的函数, void 然表示不返回任何 ,必竟与 构造函数的 没有返回 是两 事。于是, C++ 定:在定 或声明函数 ,没有 式指定返回 型中不合法的。当然 的构造函数除外。
  构造函数的出 有它的可行院 厝恍浴?尚行允怯捎 ++ 包含成 函数,既然 可以包含普通的成 函数,那 包含特殊的函数自然也不在 下。必然性是由于 象往往必 须经过 特定的初始化。 C++ 到来之前, C 言中的数据 型只是内置 型。 于内置 象,如果忘了初始化,大不了 象失去作用,但是不会 致大的 问题 。比如一个 int ,无 内存如何随 机,它的取 都不会超 int 能表达的范 行运算也不会 生危 (溢出不能算危 ,即使初始化 的数据也不能保 不溢出,而且溢出只是一 种逻辑问题 )。但是 在的 这么简单 了,忘了初始化往往将 来运行 错误 。于其 次都要考 数据的初始化, 不如把 个初始化写成 一的函数, 动调 用来得既安全又方便
 
 
标题: 的成 函数
C 言中的 构体最大的区 就是 可以 函数,而 构体只是一个内存 。所以,要提 就不得不提成 函数。
   的成 函数与普通函数(全局函数)相比,最根本的区 实现 的封装性。封装性的第一个表 访问权 限:都是函数,但是你能 访问 哪个不能 访问 哪个却可以 定。第二个表 是直 ,通 过类 (或指 )来 用函数, 人的直 就是 提供的功能 。你好像 “Bird.Fly();” 一目了然。
  在理解 this 以前要想 底理解成 函数是有困 的,我就曾以 例中保存了函数的副本。要不然, 同一个 的不同 个函数有不同的效果呢?原来,在函数所有的形参之外, 有一个不用你操心的参数 this ,它是一个指 的目 就是函数的 用者。 这么 就明白了。
  函数形参表后加入 const 就成了 “const 函数 这样 的函数保 用者自身不被修改。如 CString GetLength() 函数,你只能 取它的 度,不能修改它的内容或 度。加入 const 的作用倒不是怕 用者修改,而是防止 写函数的人不小心改 象。因 百密 有一疏,万一在某个不 修改数据的函数中改 了数据(比如将 “==” 写成 “=” ),或者万一 用了另一个非 const 的成 函数都将可能引起 错误 。在 写函数前就先加上 const 可以 记编译 器来帮你 检查
   const 加在形参表的后面 得有些怪怪的,造成 怪怪的 原因就是因 函数的形参表中没有 this ,也就没有能用 const 来修 西了。林 锐说 大概是因 其它地方都已 被占用了 并不是根本原因。
 
标题: :内 函数
函数 应该 了改善 C 言中的宏替 的不足而 生的吧。因 宏替 预编译 中直接展 的,展 开过 程中将 生意想不到的 果。 典型的有 “#define MAX(a, b) (a) > (b) ? (a) : (b)” “result = MAX(i, j)+2;” 将被展 开为 “result = (i) > (j) ? (i) : (j) + 2;” 然外面再加一 括号可以解决以上 问题 ,但是 “result = MAX(i++, j);” 被展 后将 i 被自增 1 了两次。 (以上例子摘自林 博士的《高 C++/C 程指 南》第 66 ,林 叫做 边际
   C++ 用内 来取代宏替 ,大大提高了安全性。 然内 函数也是 编译时 的,但是它能 行安全 检查 的成 函数(原因是内 函数能 够处 this ,宏却不能)。
  引用:内 联对编译 器来 只是一个建 编译 器可以 选择 忽略 个建
  笔 :也就是 ,有些函数你想内 编译 器也不一定会采 。因 函数 然减少了函数 用的 开销 ,却增加了程序的体
  内 函数是唯一允 许实 体多次被 编译 的函数。原因是 编译 器必 编译这 个函数体,才能在 编译 函数 用的地方 行合 理地展 明在多个 CPP 文件 成的工程中,可能有不止一个 CPP 文件中要有函数的 体。 既然 这样 ,就放 进头 文件吧
本文本的 评论 有:
得象 max() 和以前的数 越界一 的事 , 都可以 归纳为 一句 , 那就是 ,C 提供了 大的工具 , 那些不会使用的人才会出 现这种错误 . 个数 越界也管理不好的 , 是去写武侠小 .
比如火 药发 明了以后 , 可以用来炸山 路什 , 道因 有人用于 , 就怪 个火 功能不 完善 ?
是这样的,我们不应该怪C标准不好,
虽然它不能让result = MAX(i++, j);这种问题得到解决,
产生i被自增两次这样的结果,程序员应该自己去避免。
但是,如果标准有进步了,我们倒是因为祝贺它一下。
 
 
标题: :局部 象与静 局部
首先向 明了 名字的作用域 象的生命周期 两个概念,不 ,理解了就行了。前者是空 概念:指程序 还处 在代 码阶 段的 个名字的可 ,后者是 时间 概念:指程序运行 程中 象的存在 时间
  函数的形参以及函数内部声明的 象都是局部 象,它 的作用域就是函数内部,但是它 的生命周期却未必是函数的 程。 看起来有点摸不着 头脑 ,原因在于 C++ 的函数中允 存在以 关键 “static” 声明的静 态对 象。
  也就是 ,静 态对 象是 这样 一个 象:它的生命周期很 ,可以跨越 函数的 用,哪怕 函数 24 用一次,它也是全天候存在的。但是要想 访问 她,却只有函数正在 行的 候才行。
   于以上特性,我 专门 写了两个 测试 函数, 函数 途返回局部 象的引用或指
int& GetInt()
{
   int t=3;
   return t;// 警告
}
int* GetInt2()
{
   int t = 3;
   return &t;// 警告
}
  以上两个警告 生的原因是函数返回了 临时对 象的引用或地址。但是如果将 t 的声明改成 “static int t=3;” 就不再 示警告。
  静 局部 象似乎 为节约 统开销 做了准 。不 认为这 个特性不 应该 用。只有确 有必要 让对 象生 命周期跨越多次 应该 把它声明 (比如 统计 函数被 用的次数)。否 将提高造成 BUG 的可能性,使 高效率 的程序成 空中楼
标题: :默 认实
没什 偷懒 更舒服的了,所以我喜 认实 参的函数,我 写允 认实 参的函数。
  在形参表中,如果允 某些形参具有默 认值 按从右到左的方向排列。以上 C++ BASIC 是一 的,但是 C++ BASIC 有一点区 ,就是在函数 C++ 从右 边开 始缺省 参,而 BASIC 却可以任意缺省而不 次序(只要有逗号表示那里缺了个 西即可)。所以,同 样设计 函数, C++ BASIC 要多考 一个 问题 设计带 有默 认实 参的函数,其中部分工作就是排列形参,使最少使用默 认实 参的表参排在最前,最可能使用默 认实 参的形参排在最后。
  形参的默 认值 竟究写在声明中 体中?我曾 经试过 ,在某 些情况下写在声明中或 体中一 可行。但是,事 上写在 体中是 错误 的做法。只有当函数 体和函数 用在同一个源文件中,而且函数 体在 用前被 编译时 ,将形参的默 认值 写在 体中才可通 过编译 实际 这种 情况,函数根本就不用声明。
  将默 认值 写在 体中不 仅仅 是能否通 过编译 问题 还关 系到程序 设计 的理念。 一是函数的 实现 本来就与参数是否有缺省 ,所以,没有必要 缺省 在函数的定 体中。二是参数的缺省 可能会改 然修改函数的声明比修改函数的定 要方便。 (《高 C++/C 编译 指南》第 63
   里,本 书给 了我一个大大的惊 :原来默 认实 参的默 认值还 可以是任何表达式。以前,我一直是 这样 写的: “int GetInt(int i=3);” 然没人跟我 这样说过 ,但是我始 后面的默 认值 只能是常量。想不到 可以是需要求 量甚至是更 复杂 的表达式:
int GetInt(const int i = 3);
int GetInt2(const int j = GetInt());//
居然可以 这样
  学 了,感 C++ Primer 》!
 
标题: :函数的声明与
注:本 中提到了 声明 两个 。我倒是 认为 将后者改 更好。
  函数的 体就是 实实 在在的函数内容,它 定了 个函数怎 样执 行, 没有什 的。那 函数 么还 要有声明呢?
   这样 做的目的之一是告 诉编译 器: 然你 没有 到函数本身,不知道函数是怎 样执 行的,但是我先告 个函数的名称、参数与返回 ,你就先 编译 吧。至于 个函数究竟干什 ,等到 接的 候再
   设计 合理的程序,其代 存放在不同的文件中,函数的 体只能有一个,存放在某一个源文件中。其它源文件中如果要用到 个函数,就在 个文件中加入函数的声明。
   这样 做的目的之二是函数的提供者与使用者往往不是同一个人,甚至不是同一个企 。出于 种种 目的,函数的提供者可能并不想(或不必) 使用者知道 个函数的具体内容,只要使用者能 用就行。 这种 情况下,函数的提供者只需要提供一个声明 使用者即可。 ——C 言的 函数就是 这样 的。
  然而 在需要用到函数的文件中加入函数的声明 也有好 法与笨 法。将声明 句重写一遍自然 ,但是 这样 做有两个明 的缺点:一是 烦琐 、二是不易修改。所以,函数的声明 应该 放在 文件中,哪儿要,就在哪儿包含。 就好像我家没有 摆许 多盆 花而是 多面 子。我在哪儿都能看到 花, 水却只要 一盆。
   个理 也适用于 C++ 的声明写 进头 文件,而 体却写 程序文件。不同的是, 的声明不像函数的声明那 只有一句 ,而是一个完整的
 
 
标题: 递归
引用:直接或 用自己的函数称 为递归 函数。
  引用: 递归 函数必 一个 止条件,否 函数将永 远递归 下去, 意味着函数会一直 用自身直到程序耗尽。
  初 识递归 候,的确有些不容易搞明白。 得当 的教科 书为 此画一个 ,用一 来表示要 A B 、要 B 又要先 C …… ,用另一 表示算好了 C 就可以算 B 、算好了 B 就可以算 A …… 例程序与一个 图结 合,如此 实讲 道理,要 递归 自然稍容易些。
  要写 递归 函数就得 递归 的妙用,要写没有 错误 递归 函数 悟其数学原理。我倒是 这样 的函数与 数学 归纳 有些相通之 。不同的是,数学 归纳 是先求 界条件,再去往无 方向 归纳 。而 递归 是从无 方向向 算的。函数如何 行,与我 如何写没有必然的 系,于是,我 在写程序的 候也可以先写 界条件。 这样 做可以在程序 开头 先把可能的 问题给 排除掉。 远递归 下去 的可能性自然被降低。比如求 乘的函数:
//
程序一、 上的例子
int factorial(int val)
{
   if (val > 1)
     return factorial(val-1);
   return 1;
}
//
程序二
int factorial2(int val)
{
   if (val <= 1)
     return 1;
   return factorial2(val-1);
}
  程序二的写法与程序一没有区 ,但可以告 自己 递归 止条件。防止一不小心就写了个
  似乎 大多数 递归 函数都可以用循 来解决。 方法迁就了不同的 象:循 用少量的 算机 源、大量的人力来解决 问题 递归则 用大量的 算机 源、少量的人力来解决 问题 。所以,在 算机速度和存 量都不大的年代,曾有人反 对递归
   汉诺 问题 是只有用 递归 才可以解决的 问题 ,其 只有要求解 汉诺 塔的移 动过 程才必 递归 ,如果只要求解移 次数,那 用循 也不成 问题
对本文本的评论有:
阶乘的函数写错了.
int factorial(int val)
{
  if (val > 1)
    return val* factorial(val-1);
  return 1;
}
晕,我忘了相乘了,哈哈。
 
标题: return
引用: return 句用于 束当前正在 行的函数,并将控制 返回 给调 用此函数的函数。
  引用: return 句有两 形式: reutrn; return expression;…… 第二 形式提供了函数的 果。
  笔 :以上第一句 话说 return 的两个作用之一: 束函数。 return 的作用之二是提供函数的返回
   return 句的两 形式,情式一只能用于无返回 的函数,情式二可以用于有返回 的函数也可用于无返回 的函数。
  如果函数有返回 ,就必 用形式二来 束, 而易 的。
   于没有返回 的函数,可以不写 return 句, 式的 return 生在函数的最后一个 句完成 。也可以用形式一来 束, 这种 用法一般用在函数中 ,判断某些条件之后就立即 束,后面的 句不再 行。如果用形式二来返回,那 express 是另一个没有返回 的函数。如:
void FuncA();
void FuncB()
{
   return FuncA();
}
  个人 认为这种 写法不是好 习惯 ,因 看起来 FuncB 有了返回 ,如果 逻辑 上有 需要,我 认为 写成以下格式更好:
void FuncB()
{
   FuncA();
   return;
}
  在 BASIC 中,函数的返回 束是由两个不同的 实现 的。前者是一个 函数名 赋值 句,后者 “Exit Function” 句。 这种设计 除了不如 C++ 以外, 容易出事。比如在函数 开头 函数名 一个默 认值 ,然后根据某些条件 其它特定的 Exit 。如果写函数 不小心漏了某个 赋值语 句,函数将 BUG C++ 不会 这种类 型的 BUG
  引用:千万不要返回局部 象的引用。
  引用:千万不要返回局部 象的指
  笔 :以上两句是黑体的 标题: 专门进 行了 讨论 。不 过这 错误虽 重,却不 理解。知道了就好了。
   main() 是一个很特殊的函数,它的特殊性在 有体 。引用: 返回 型不是 void 的函数必 返回一个 ,但此 规则 有一个例外的情况:允 主函数 main 没有返回 束。 …… 编译 器会 式地插入返 0 句。
 
标题: 传递 的函数与字符串函数
如果将数 为实 参来 用函数,函数接收到的形参其 是一个指 。数 名是可以 转换为 的,但是数 名和指 针毕 竟不等价。所以, 这样传递 果是 失了数 原有的一些特性。最大的 失莫 sizeof 大小的 测试 看以下程序:
void FuncA(int *temp)
{
   cout << sizeof(temp) << endl;
}
void FuncB(int temp[])
{
   cout << sizeof(temp) << endl;
}
void FuncC(int temp[20])
{
   cout << sizeof(temp) << endl;
}
int main()
{
   int a[10];
   cout << sizeof(a) << endl;
   FuncA(a);
   FuncB(a);
   FuncC(a);
   return 0;
}
  三个函数的写法各有不同,但是 果却是一 的。其中 FuncC 的写法尤其容易 解。因 为编译 器不管你 传递 的是多大的数 (甚至不管是不是数 ),但是函数的写法却在暗示程序 员这 个数 20 个成 。如果 参成 20 个, 果就是 没有起到完全的作用,如果 参成 不到 20 ,那就指 越界了。
   避免 这样 尬,有 将指 与容量一起 入函数: “void FuncD(int temp[], _size_t Size);” ,或者 传递 两个指 “void FuncE(int* Begin, int* End);” 这样 做当然好,不 C++ 有另一 种办 法可以不用 这么 ,那就是引用 传递 “void FuncF(int (&temp)[10]);” 这样 的函数只允 int[10] 入,大小不符的数 或非数 的指 都无法 入。 这样 就保 10 的正确性, sizeof 都省了。
   C 言的字符串 理函数大概是 有的可以不受此 束的函数了。字符串就是字符数 ,但是在 传递 字符数 组时 ,可以只 而不管大小。因 C 言中的字符串都是以 NULL 尾的。前 子有人在 论坛 及字符串和字符指 系。回答是: C 言的字符串是用字符数 存放的,而 是借助于字符指 。但是,要能 这样 的操作,有两个条件必 须满 足:一是所有字符 连续 放置在以指 针开头 的内存中、不跳 ,二是有一个 定的 束符。 int[] 之所以不能 这样 做,是因 第二个条件无法
 
标题: :函数的引用返回
引用是 给变 量取一个 名,所以引用 传递 会直接 量本身的 传递 。它的最大好 是可以把 别处对变 量的改 保留下来,第二好 是它提高了性能:如果函数的返回 是一个引用,那 ,如上文所 ,它会 节约 构造、 赋值 和析构 程。但是,函数返回引用往往会 来一些意想不到的 错误 :比如返回 临时变 量的引用。
// 一个 错误 的函数
int &Max(int i, int j)
{
   return i>j ? i : j;
}
  以上函数的 错误 在于, i j 在函数 束后会被 放。 的引和也将失效。如果用 个返回 值给别 赋值 ,将会 得一个垃圾。 VC++.Net 以上 return 示警告。
  那 ,如果返回一个全局 的引用呢? 当然是可以的,但是,一来程序 设计 中不建 使用 多的全局 量,二来全局 量即使不返回也可以 访问 这样 做的唯一用途就是把函数做右 其它 赋值
int m;// 全局
int &MaxByGlobal(int i, int j)
{
   return m = i>j ? i : j;
}
int a, b, c;
c = MaxByGlobal(a, b);//
用法一、用返回 值赋值
MaxByGlobal(a, b); c = m;//
用法二、不用返回 值赋值
  当然,以上 MaxByGlobal 函数也不是一无是 ,能用返回 赋值 程序 来更好的可 性。 只是 这样 的函数 设计 本身不被建
  那 ,函数返回引用用得最多的就是返回形参了。因 形参可以用引用 传递 ,引用的形参不是函数内部的局部 量, 这样 做是可取的:
int &MaxByRef(int &i, int &j)
{
   return i>j ? i : j;
}
  上面 个函数和上文中的 “int Max(int i, int j)” 函数如此相似,但是它省去了三次构造、 赋值 和析构。
  另外一 用法就是在 的成 函数中返回 类对 象自身了,典型的是 “operator +=” 函数之
MyClass &MyClass::operator +=(const MyClass &other)
{
   // 某些
   return *this;
}
  以上函数返回的是自身的引用。因 为类 的成 函数也可以写成全局函数 “MyClass &operator +=(MyClass &Left, const MyClass &right)” ,而且在 函数的 用中 实际 存在着 this 传递 。所以,以上 个函数依然可以看作返回了形参的引用。
   于返回引用的函数, 有一个好玩的 像。即返回 值还 可能可以被 赋值 。如 “(a += b) = c;” 这样 的形式。 这种 写法明 ,但是如果函数返回了非 const 的引用, 个表达式的确是合理的。所以,上面的 “operator +=” 函数 要修改一下,将返回 “MyClass&” “const MyClass&”
  返回引用并不是 处处 可用的,正如《引用 传递 用范 》中提到的一 :不能用引用来 传递临时值 。有 候我 的确要 生一个 临时对 象并返回它,那就不能返回引用。典型的有 “operator +” 函数:
const MyClass MyClass::operator +(const MyClass &other) const
{
   MyClass Temp;
   // 某些
   return Temp;// 里只能返回 象,因 Temp 是局部
}
 
标题: :函数的非引用返回
函数最多可以返回一个 ,也可以不返回任何 (也有 返回 void” 法)。之所以最多只能返回一个 ,因 只有 这样 才能在表达式中使用。比如 “y=Sin(x);” ,如果 Sin 函数返回多个 个表达式就失去了意 。之于 可以不返回任何 经历过 BASIC 的人 应该 更能理解。因 BASIC 中把有返回 的程序段叫函数,没有返回 的程序段 叫做 子程序 。很 然, 子程序 就是完成一个特定的功能后 束的程序段。
  函数的返回 没有 型限制,可以是内置 量,也可以是 类对 象。无 是内置 类对 象,都有着 律。但是, 律在 C++ 到来之前很少有人去理会, 竟内置 型太 通,以至于程序 根本不去考
  在 C 代,所有的返回 都是局部 量。如下列程序:
//
程序一:
int Max(int i, int j)
{
   return i>j ? i : j;
}
//
程序二:
char *StrCpy(char *Target, const char *Source)
{
   char *Temp=Target;
   while(*Source)
   {
     *Temp++ = *Source++;
   }
   return Target;
}
  程序二 人一个 错觉 认为该 函数返回的不是函数内部的局部 量。 错误 原因在于没有理解指 的本 。其 程序二和程序一一 ,返回 是形参之一。而形参就是作用域 函数内部的局部 量。
  理解了 返回 是局部 。因 为还 有一个很重要的概念没弄清。比如:
int a, b, c;
char d[10], e[10], *f;
//
其它
c = Max(a, b);//
句一
f = StrCpy(d, e);//
句二
  以上注 两行 句都有同一个 问题 :如果返回的 量作用域 限于函数内部,那 函数 束以后 该变 量就已 不存在了,那 么给 c f 赋值 的是什
   C C++ 有一个机制保 以上 赋值 正常 行:在函数 束前,先将要返回的局部 临时 一份到 内存( 个内存程序 知道,也无法知道)。然后将局部 销毁 ,函数正常 束。接下来用 中的 临时变 标变 赋值 赋值结 束后再把 临时变 销毁
  以上 程凭空多出一次 量构造、 制与 销毁过 程,好在 于内置 量来 这样 程所需的性能 出并不太多。但是 C++ 到来以后,函数的返回 值类 型可以是 类类 型。而 类对 象的构造、 制与 销毁 可能很 复杂 、很占用系 统资 源。于是 引用 传递 再一次 发挥 了它的威力
 
标题: :引用 传递 用范
经过 三篇文章的 述,函数的参数 传递应该 明朗了, 经过 一番 比,似乎引用 传递 是最 秀的一 种传递 方式。第一、它用法很 简单 似于 值传递 ,第二、它功能很 大, 似于指 针传递 ,第三、它很安全,可以避免指 针传递带 来的危 ,第四、它效率高,函数中不必要 象的 建、 赋值 放。第五、如果不希望 参被改 ,可以使用 const 形参 ……
  但是,天下没有 这么 便宜的午餐!引用 传递 不是倒 能用的。 个例子:
void Swap(int& a, int& b)
{
   int temp = a;
   a = b;
   b = temp;
}
  以上函数可以 行两个 int 量的交 。但是,很多情况下 函数不能 用:
int ia = ib = 1;
short sa = sb = 2;
const int cia = cib = 3;
Swap(ia, ib);//
正确
Swap(sa, sb);//
错误 short 不是 int 然可以 转换为 int ,但是 量不存在
Swap(cia, cib);//
错误 两个参数是 const
Swap(4, 5);//
常量不是 量, 似于将 short 传递给 函数
Swap(ia+ib, ia-ib);//
错误 ,表达式求 生的 临时值 不是
  其中将 const 参数 传递进 函数的做法, 然看起来有些荒 实际 上某些 候会不 做的。某个 量在定 候并不是 const 的,但是在 用某个函数的 候将它作 const 形参 入,而 函数内部再 Swap() 函数 量已 成了局部的 const 量。
  以上 个特性反 用是很有用的。在多人 作写程序的 候,或者写一个大型程序的 候。你不知道某函数是否用 const 来保 参数,但是你想保 参数。那 ,你就在自己写的原 函数中将 参数保 起来。 这样 ,当你 用某个没有 式指定 const 引用参数的函数 编译 器就会 报错
void funca(const int& a)
{
   funcb(a);// 错误
}
void funcb(int& b)
{
   ...;
}
int t;
funca(t);
以上程序会在注 的那行停止 编译 。因 在它 用了函数 b ,而 b 没有声明参数 const 然函数 b 中未必改 参数
 
 
标题: :形参与 参的 系之引用 传递
C++ 有了 引用 传递 后, 形参的改 不影响 被判无效。因 为传递给 函数的并不是一个 ,而是 量自身。在函数中定 的形参 是局部 量,但却是一个引用。 个引用的作用域 限于函数内部,但是由于它与 参就是同一回事,所以 它的操作完全等同于 对实 参的操作。比如你叫 黑旋 买鱼 ,或者叫 买鱼 ,去的都是同一个人。
   C++ 要有 引用 传递 回事?一 种说 法是只有引用才能达到操作符重 的目的, 个以后再 。但是,撇 开这 个不 ,形参是不是引用,直接影响了程序 行的效率。前面提到 ,函数 要用 参的 去初始化形参,初始化的 程包含了定 一个 量、然后 一个 两个 程,如果 量并不是内部 量,而是一个 类对 象,那 ,定 一个 类对 象可能很 复杂 ,而初始化 象一 会很 复杂 。而引用只是 给对 象取一个 名,不 及定 与初始化,离 作用域 也不用 放。
  相比之下,用指 针传递 可以避免 类对 象的定 、初始化与 放。只需要付出指 针变 量的定 、初始化与 放的代价。但是,指 杀伤 力太大。即使是熟 的程序 ,也不能保 证绝 不出 野指 ,野 的代价几乎无一例外是程序崩
  引用也不是吃素的,如果 针传递 帮你配了一把我家的 ,那 引用 传递 就是直接把我家的 财产 都交 了你。有 ,我 使用引用 传递仅仅 了效率,而不希望 参被修改,那就要 得把形参 标记为 const ,如 “UINT GetLength(const CString&)”
   便 一句,指 针传递 也可以 这样 做。把形参定 义为 指向 const 象的指 (而不是 const ),可以降低 杀伤 力,保 护实 参所 对应 的内存。如果是普通的 值传递 ,那 有没有 const 函数外 部并不影响。但是,我个人 认为 ,有 候加上 const 也是一件好事。如果程序的 逻辑 并不需要改 参数,而 实际 写了代 ,加上 const 可以 让编译 器帮我 找出 BUG ,如:
int Max(const int a, const int b)
{
   return a>b?a:b;
}
   VB 没有指 的概念,却有 值传递 地址 传递 两个概念。比如 “Function Func(ByRef i As Integer) As Integer” i 接受了 参后,它的改 能影响 参。它的 实质 似于 C++ 中的引用 传递
 
 
标题: :形参与 参的相互
形参的改 不影响 话说 起来 巧,但是要完全理解,似乎 有几个玄机。
  在我 表《函数的定 》一文后,有朋友 表意 ,提到了 函数 程中的入 与出 ,在此首先作个 明:我 的是《 C++ Primer 》,而不是《 编译 原理》,入 与出 讨论 。在 讨论 的尺度内,我 可以 这么认为 :形参是函数内部的一个局部 量, 局部 量在函数 被初始化,而初始化它的 值则 来自 参的 。也就是 ,它的定 与初始化 似于 “int i=3;” 。只是被分成两行写了,形参的定 写在函数的定 中,如: “int ttt(int b)” ,初始化写在了 用中 “cout << ttt(a) << endl;” —— 参看上一篇文章《形参与 参概念》。
  那 ,在函数中无 b ,被改的始 是形参 个局部 量,函数 ,离 开这 个局部 量的作用域, 量被 放。
  但是, C 言的 针传递 形参能改 变实 的感 ,其 实这 是一个 解。 于指 针传递 ,函数的形参是一个指 传给 它的 参也 应该 是指 (或者能 转为 ,比如数 名、能 转换为 等)。在函数中,如果改 的改 就等同于 让这 个指 指向 别处 ),不会影响主 函数中的 参。但是,由于指 针对应 着一个内存地址,通 它可以改 内存的内容。所以,无 在函数内部的形参 是外部的 参,它 都可以影响同一内存的 。所以,指 针传递 可以把函数内部的影响 到函数外,但是, 到函数外的 不是形参,而是形参所指的内存。
   就好比我把我家的 你配了一把,我手里的 匙是 参,你手里的 匙是形参。你无 是把 匙折断 是磨短,都与我的 匙无 ,但是你用它 了我家的 却可以把我家洗劫一空。你影响的不是我的 匙,而是我的 财产
  上文 到, C++ 有了 引用 传递 后, 形参的改 不影响 被判无效。 就得提到 引用 传递 的概念了,下文再
本文本的 评论 有:
简单 , 用函数的 , 形参把 参克隆了一次 , 你再怎 形参 , 也与 参无 .
TNND
就是一个入 与出 栈过 程嘛 , 你可以去学学 汇编 .
:
mov cs1,100 //cs1=100;
push cs1 //
cs1 ;
pop cs2 //
中的内容出 栈给 cs2;
与另一句 等价 :
mov cs1,100
mov cs2,cs1
会使用上面的那 用法呢 ?
push pop 占用更少的 CPU 周期 . 所以 , 一般 用函数都用入 / 参数 .
 
 
 
标题: :形参与 参概念
到形参与 参,在 C++ 出来之前其 简单 ,就一句 :形参的改 不影响 参。 个状 直到 C++ 有了 引用 传递 才有改
  要弄清 个,首先得弄清形参与 参是什 么东 西。因 函数是一段 可以重用而不必重写 的代 次重用当然未必完全相同(不可否 有些函数 次重用都完全相同),那 不同在哪里呢?又怎 样产 生不同呢?一 方法是依靠随机,随机是个好 西,不要 了, 程序 都无法控制 用的 果。第二 方法是凭客 条件(比如运行 时间 、机器配置)。但是 些函数 用很窄, 似于 “y=Sin(x)” 这样 的函 数就 不能 这样 做。
  那 ,从 “y=sin(x)” 的形式看来,能决定函数怎 运行的唯一因素就是 x 了。函数的某次运行是受某一个 x 的影响并控制的,而下一次运行, 会受另一个 x 的影响。那 用函数者就有必要告 函数:我要用哪个 来控制你,而函数自己 有必要保存 ,直到函数 束。
   此,在函数内部建立一个 临时 的、局部的 量, 该变 量的作用域就是函数内部, 该变 量的作用 时间 就是从函数 行到 行。如果同一函数在同一 时间 有几个副本在 行( 这种 情况在多 线 程程序中会出 ),那 是互不相干的,它 部的 量也是互不相干的。 量就叫做 形参 ,全称形式参数。
   形式 是跟 实际 的,另一个参数就是 实际 参数,叫 ,在 用函数 将决定函数内部的形参的 参在函数中是否可 要取决于两个因素:一是 参的作用域,二是有没有被形参覆盖。先 第一个因素,如果只 C 言,那 的作用域就是全局与局部两 ,但是 C++ 作用域 一概念,由此第一个因素 复杂 了。第二个因素本身并不 复杂 ,但是如果没有引起程序 的注意,那 造成的 问题 是很 难发现 的。 看下以下程序:
int a;//
全局
int ttt(int a)//
函数的形参也叫 a
{
   cout << ++a << endl;
   return a;
}
int main()
{
   a = 3;
   cout << a << endl;
   cout<< ttt(a) << endl;
   cout << a << endl;
   return 0;
}
   程序中有一个全局的 a 量,但是在 ttt() 函数中却被另一个 a 覆盖了,所以, ++a 没有影响到全局的 a ,如果把函数定 “int ttt(int b)” 有不同的
  以上把 形参 提了 这么 多,主要目的 形参的改 不影响 。字数不少了,留到下篇文章再 吧。(我 得我写得不像 读书 ,倒像是教材了。 呵呵
 
标题: :函数的定
得在哪本 上看到 ,函数的定 义为 有名称的一段代 大概地 明了函数的 实质 :首先、它是一段代 ,其次、 段代 可以被重 使用而不必重 复编 写,第三、它是有名字的,在需要重用的 候凭名字来 用。
   法到了 C++ 复杂 了。原因之一是 C++ 支持函数重 ,也就是 了同名函数。 编译 器在 编译时产 生不同的函数名,但那必竟是 编译 器的事, 于程序 就是同一个函数名。原因之二是 C++ 支持运算符重 ,可以用一个 似于 “+” 号的运算符来 用函数。运算符重 着是 了配合 类对 象的运算,因 如果没有 仅针对 内置 型,运算符是没必要重 的。 —— 试验 了一下,自定 了一个 “int operator +(int i, int j)” 函数, 果没有通 过编译
  于是,到了 C++ 中,函数的概念被修改 函数由函数名以及一 操作数 型唯一地表示 ,依我看, 这样说还 来, 应该说 函数由作用域、函数名以及一 操作数 型唯一地表示 ,理由很 简单 ,因 在不同的作用域中可以出 名称相同、参数 型也相同的函数,除非把 作用域 :: 函数名 合起来看作一个函数名。
  函数 函数体没有任何 制性要求,哪怕函数体 空也可以。不 ,无 是空、一句 名, 是多句 句,花括号一定不可少。在 里,包括在花括号内的若干行 句不能再 视为 一个 句了 —— 能放 句的地方也能放 简单语 句,而 简单语 句可以不使用花括号。
  不管你如何看待 这组 花括号,有一点是肯定的:花括号内部是一个作用域。那 ,内部定 量就只有在内部使用了。 就是局部 量,在任何函数(包括 main() )内部定 量都是局部 —— 初学者可能以 main() 内部定 量是全局 量。
  有一 内部 量的定 与以往的定 方式不一 ,那就是函数的参数。不同之 在于:一是它 用逗号分隔,二是不允 “int i,j” 这样 的方式定 组变 量。我想,也 正是因 所有定 用逗号分隔,才造成不允 后者的吧, 这样 来歧 ——j 没有指定 型。如果用分号来分隔,那 后者的 方式也 就可以了。 C++ 准的事,我没有能力来 为标 准出 划策,只能妄加猜 了。
  函数的返回 也是一个 型,与 量的 型一 ,它可以是内置 型,也可以是 类类 型, 可以是引用和指
  引用:在 C++ 准化之前,如果缺少 式返回 型,函数的返回 将被假定 int 型。
  笔 :据我 测试 ,在 VC++.NET 中, 这样 做是可以的。照 这么说 VC++.NET 仍然没有按照 C++ 准做?或者 VC++.NET 迁就了老程序
对本文本的评论有:
函数的参数当然不能使用类似int i,j的方式,因为调用函数的时候,涉及到的不仅仅是定义参数,还有把要处理的变量入栈,调用的函数运行前的第一件事,是把被入栈的变量出栈.
这与int i,j定义变量做的事完全不同,所以,不按定义变量的方式写,也很正常.
如果偷猫兄一定要写得一样,那就自己做一个编译器吧.
 
标题: :函数概念
入第七章学
   函数 个概念在 C/C++ 是很 人的。原因在于,好多 C 言入 门书 的第一章第一 “C 言是由函数 成的 ,初学者学到 里,就好像是 C 的大 就被一个麻袋套在 上,什 也看不 了。那些 还举 了一个例子,然后 照着例子 个程序是由 main() scanf() printf() 函数 成的 ……” 。我 啊,初学者第一天上 C ,哪里会管什 函数不函数的。
   BASIC 做得不 ,倒不是 BASIC C++ 好,而是 BASIC 容易入 。在 开头 节课 不必理会 这么复杂 西,学了 “Let “Print 就可以 简单 的算法了。然后提到的 函数 是包括数学函数在内的 内部函数 。我 在数学里学 函数 概念,知道 “y=Sin(x)” 是一个函数, 在在 BASIC 里学到一 的函数,自然容易入 。等 一切都熟悉了,再去学 自己写的函数 —— 自定 函数,会更加理解程序中的 函数 概念。
   VB 与早期的 BASIC 相比,使用了 事件 驱动 原理。画完界面就得面 函数了 ,但是 VB 事件 法来回避了。初学者可以不知道 “Private Sub Command1_Click()” 究竟代表什 ,只要知道那是 控件被 单击 行的代 了。等到后来,学 自定 函数 后,必然会恍然大悟。
  回到 C++ 中,学 之初用到的函数的确是 成的 函数,但是正因 为过 早地提到了函数概念, 致了初学者无所适从。有没有 法呢?当然有了,至少《 C++ Primer 一直到第七章才 始提 函数 二字。
  另外: VB 中有 函数 子程序 两个不同的概念,如今 子程序 又叫 ,除了使用不同的 关键 字以外,它 的惟一区 是有没有返回 C 将它 合并了,都叫函数。其 VB 里的函数也可以 弃返回 ,只是 VB 里没有与 “void” 对应 ,无法定 不返 的函数,才不得已出此下策
 
 
标题: try catch assert
程序 是要慢慢成 的,比如 错误处 这种 事情,就不是一 始就面 的。当我 们编 的程序 很小,小到 “cin>>i; cout<<i;” 这样 的程度, 错误处 理不是我 要学 的目 。但是,一旦 用的程序,那 ,无 周到,无 精良。意外 免的。 些意外可能来自程序 设计 不到位、可能来自用 错误 操作、 可能来自机器与网 的不确定因素。
  没有什 比追踪 错误 难过 的事了, 得有一回我在追踪一个 VB 程序的 错误 经过长时间测试 ,我 发现 程序在运行中突然 生很大的跳 :函数 A B B C ,在 C 程中,居然会突然跳到 A 中。后来追 查发现 ,原来 A 中有一行 “On Error Goto” 句。 一个 句,影响了我 调试 C 函数。从那以后,我明白了,除非程序要 布了,否 则别 动错误处 理。
   C++ VB 不一 VB 用一句 “On Error Goto” 错误处 理后,在 函数 束之前一直有效(除非 式地 关闭 它)。如果 生了异常, 理代 要根据异常的 来分析异常的 型。而 C++ 可以 选择 可能出 异常的内容放 try 后的 中。一个函数内部可以有多个 try ,而 try 又可以附 多个 catch 理。 应该说 C++ 中的异常 理更灵活,当然也更容易出 。我前 生的 错误 就是在 ADO 理后只有 “catch(_com_error *e)” ,但是 实际 上出 的异常却不是 “_com_error” 的, 果仍然抓不往异常。
  异常 理和 assert 系有些 以捉摸。一方面它 各有各的作用,另一方面它 会互相影响。我就曾 上面吃 过亏 :我的程序是在服 器上运行的,从来没人会 着服 器看,所以我的程序不允 许弹 对话 框。我写了比 完善的异常 理,无 么错误 ,都 记录进 LOG 文件,然后 继续 运行。但是我却 是用 DEBUG 模式 编译 的, 果异常到来 try 没起作用,倒是 assert 起作用了, 了个 对话 框在那儿。 件事 我的启 是: 自己是程序的客 就可以用 DEBUG 模式 编译
对本文本的评论有:
错误捕捉是很烦人,我的感觉是能在try代码段外解决的错误,就尽量在外头自己解决,尽量少依靠try来处理捕获错误.
在网络编程中,有些错误是无法预知的,比如网络连接断了,数据库当了...好象在这些情况下,用try比较好.
我有一次写的一个服务程序,用户用了一段时间后,经常会异常中止,查来查去查不出原因,后来才发现是ORACLE的日志满了,这个错误显然我在写程序的时候没有想过,丢脸啊...
 
标题: break continue goto
break continue 的使用范 一致,两都可以用于循 ,其中 break 可以用于 switch 。功能上也有一定的相似性, break 就相当于退学, continue 相当于跳 break ,程序究竟跳到哪儿比 好理解。 但是 continue 究竟跳到哪儿去了,初学者可能有些疑惑,不妨就当它跳到了循 体最后一句 句的后面。
  如果它 们处 在由多重循 switch 成的圈圈里,那 包括它 的最里 起作用。于是, 想一下子跳出多重循 的人可能忘不了 goto
  引用:从上世 60 年代后期 始,不主 使用 goto 句。 …… 所有使用 goto 的程序都可以改写成不用 goto
  笔 goto 是一个很有争 句, 本建 少用或不用它,我个人的 习惯 决不用。不 ,至于 上世 60 年代 法,我倒是一直不知道。因 我自己学 BASIC 1994 年,那 候学的是 行号的 GW-BASIC goto 是必 用到的 句。莫非当 学校 开设 程居然是落后二十年的内容?
  林 博士 goto 另有看法,他 错误 程序 自己造成的,不是 goto 过错 goto 至少有一 神通,它能从多重循 中咻地一下子跳到外面, …… 就像房子着火了,来不及从楼梯一 往下走,可从窗口跳出火坑。 ……” (《高 C++/C 程指南》第 32
  我写的程序目前 没有超越三 。从最里 往外跳,如果跳一 ,就 break ,如果跳两 或三 ,一是 这种 可能性很小,二是如果真的碰到了,我就用其它条件来控制外 是否 继续 break ,自从 1997 构化的程序 设计 以来,我的确完全抛弃了 goto ——VB 中的 “On Error Goto” 除外,出 现错误 ,自然不管在哪一 ,都 我跳 进错误处 理中。
   goto 的目 是一个 号, 号的起名倒有点意思,因 为标 号只用于 goto ,所以它的名字可以与任何 量名以及其它 标识 符一 而不 生重名。以前的程序是 行号的,所以就 “goto 行号 在程序不 行号了,但是允 在任何地方加 号。 编译 器在碰到它 候,大概就是凭其后 的冒号来判断 个名字不需要 检验 合法性。那 C++ 中已有的 “public:” 算不算 号呢?
   此,我做了个 实验 实验 内容一是我在 的声明里加入了一行 “pub:” ,二是我在程序段中加入了一行 “public:” 发现 两都都不能通 过编译 。也就是 实验 明在 义这样 的地方不允 使用 号(也用不着,因 它不在任何函数内部, goto 是运行 的事,与 编译 ,而且 goto 不允 跨函数跳越。), 实验 明在程序段中的 号不允 使用保留字
对本文本的评论有:
不主张使用GOTO语句是为了让程序看起来顺眼而己.看:模块化的代码.其实的确没什么大不了的.记住,当你的程序被编译成机器代码以后,里面的跳转全是JMP,相当于GOTO.
 
自从和草莓对骂以来,你就学会了狡辩。
我有跟你讨论机器码吗?
程序设计的风格是为了程序维护,
不是为了编译。
 
标题: while for
while 中有一个怪事: 似于 “while (int i = GetInt())” 这样 句,在条件中定 一个 量,在 for 中非常常 ,也很好理解。但是用在 while 中却有所不同,如果用在 while 中,那 么每 次循 都会 经历 一次 建和撤 程。 —— 天, 是不要 这样 写吧。幸 是在 while 前面定 并初始化 量的。
   do-while while 有着不一般的 系,所以几乎所有的 本都是把它 放一起 的。当年学 BASIC ,花了不少的功夫去学 当型循 直到型循 。的确,当型和直到型都有存在的必要,因 程序的确有 种逻辑 需要。于是 C BASIC 以及 PASCAL 等程序 言都提供了 。不 提供 提供,怎 用却是程序 自己的事。就我个人而言,我 是喜 用当型循 。因 当型循 可以模 出直到型循 的效果来。比如以下四段代 ,它 是完全一致的:
//
1
do
{
  循 ;
   BoolVal = 表达式 ;
}while (BoolVal);
//
2
BoolVal = 1;//
True
while(BoolVal)
{
  循 ;
   BoolVal = 表达式 ;
}
//
3
do
{
  循 ;
}while (
表达式 )
//
4
while(1)
{
  循 ;
   if (! 表达式 ) break;
}
   for 句的 序和 逻辑 是最 难讲 清的了。如果知道了,就是 这么 回事。如果不知道,不 上半天口舌是 不清的。原因在于 for 包括四个互相 关联 句,其中三个在 “for” 后面的括号里,另一个作 体存在。 BASIC 要将 for 句定 义为 “For i=M To N Step t” 的格式。
   for 括号里的三个 句是可以省略的,最牛 B 的省略莫 “for (;;)” 了。会 这样 写的人,要 彻彻 底底地明白了 for 逻辑 的人,要 是一点不懂的人。我 得,如果要我 这样 写,我不如写 “while(1)” 了。
 
 
标题: if switch
不愧 为经 ,在 if 地方能避免 教, 色,真叫人佩服。
  大体上 if 要注意的就只有 else 的配 对问题 了。如果在 else 前方有多个没有配 if ,那就找最近的一个配 。如果要改 变这种 拉郎配 ,就加上花括号。
   是引用林 博士的一句 吧: “if for while do …… 论执 句有多少都要加 {} 这样 可以防止 写失 (《高 C++/C 程指南》第 16
   if 句曾有一个令我疑惑 了好久的 西: “else if” 究竟算什 ?因 BASIC 里有 “ElseIf” 关键词 ,而 C++ 中所 “else if” 是两个 词组 成的。中 插了个空格。我 都知道, C++ 句与 句之 插入若干个(包括 0 个)空格、 TAB 、回 都是一 的,那 ,如果我把 else 后插入一个回 ,不成了另一 种结 构的 if 句了 ?后来我仔 地分析一下 逻辑关 系,才豁然 朗:原来是 BASIC “ElseIf” 了我的理解。 C++ 中用哪 方法去理解都没区
  都 switch if 而出 的,但是 switch 然可以 if ,却并不是任何 候都能使用。使用 switch 有两个先决因素:一是所有的条件都必 编译时 常量。也就是 如果要在程序运行 再决定 case 后的条件,那是不行的。另一个因素是只能拿出若干个整数 来比 是否相等,既不能是浮点数,也不能比 大于或小于。
   switch 最容易出 的就是 break 句了。因 按常 思路,人 们总 两个 号之 句才是 应该执 行的。从 BASIC 来的人更加痛苦,因 BASIC 里不需要 似于 break 这样 句来表示 束。
  我的做法是,在打程序框架 ,先把 case 号和 break 写了,其余的再去完善。即使 逻辑 上不需要 break 句,也要写上 “//break;” 这样 可以提醒自己和 团队 的伙伴:此 并未 break ,而是的确不需要。
   default 是最理直气壮的了。因 的确有 候并不需要 default ,但是我的 经验 是要加上 default 以及它后面的 break ,原因同上,提醒自己和伙伴我没有
 
标题: 简单语 句与
贺进 入第 6 章的学
   简单语 句就是只有一句的 句, 也叫 ,是由多句 成的一个整体。 BASIC 也有 的概念,但是它 却是不同的概念: BASIC 简单语 视为 特殊的 ,而 C++ 块视为 特殊的 简单语 句。个人 认为 C++ 句的存在是 C++ 没有 “end if” 类语 句的缺陷。
   BASIC 中, if end if (行 if 除外)、 while wend do loop 。也就是 ,有 就有尾,所以, BASIC 编译 器不担心无法确定 的大小。 C++ 不同,它的 关键 字都没有 句。没有 标记 知道它的主体究竟是几行呢?所以, C++ 只好 定:所有 构的 句体都只能包含一句,而且必 包含一句(有且 有一句)。 话说 ,如果要多句,你也得做成一句的
  将多行做成一行,就是所 了。
   简单语 句,空 句和空 是不能不提的。空 句( )也是 句( ),只是它 也不干。空 句存在的原因,无非也是因 C++ 定了 句体必 是一句。 了,那些 构的 句体是 有且 有一句 ,不 仅仅 多于一句要写成一句的 ,反 ,如果没有任何内容,你 也得 造一句出来。于是 世了。
  以下 句就是一个典型的例子:
int s = 0;
for (int i=1,s=0; i<101; s+=i,++i) ;//

  空 句的存在 C++ 徒增了 度与危 性,很多初学者弄不清哪些 句要以分号 尾,哪些 句不要, 错误 地在 for() 后面加了个分号, 果使循 体被取消了循 格,而且有可能出 死循
 
 
标题: 转换
引用: 转换 也称 为强 转换
  笔 :我 得要提 转换 ,得从 C 格的 起。 里面可能有我个人的原因。因 我个人 习惯 C 格的 转换
  在 C 言中, 转换 就是用借助一 括号同 型名和表达式列出来,比如 “(int)t” “int(t)” 就是把 t 转为 int 型。
  引用:因 要覆盖通常的 转换 ,所以需 式使用 转换 …… 式使用 转换 的另一个原因是:可能存在多 种转换时 ,需要 选择 特定的 转换
  笔 :从外文 图书 译过 来的中国 图书 有个通病,就是 言不 。本 算是翻 得非常好的了,依然无法 这种 影响。上文的意思无非是 :我不希望使用默 转换规则 候,就可以 式地 定按我的要求 转换 。如果要 个例子,可以拿上文《 转换 转换 》中一个 成的例子:
int a = -3;
unsigned b = 3;
if (a == b)//
转换 转为 unsigned int
if (a == (int)b)//
式指定 转换为 int
   这种 用法更多地用于指 针类 型的 转换 。因 针类 型就是指 所指向 象的 型,而指 本身是没有 型区 的。所以,指向任何 型的指 可以互相 转换 。最典型的就是 void* 和其它 型之 的互 了,比如: “int* p = (int*)malloc(sizeof(int) * MaxSize);”
   有一 用法就是在 编译 器不允 许进 转换 候,比如将 const 转为 const 象。如:
const int t = 3;
int* p = (int*)&t;//
本来要写作 const int* p = &t;
   这种 用法 是少用 好,理由很 简单 编译 器之所以不允 许进 转换 ,就是 了保 数据,你非要破坏 这种 安全性自然不好。即使能确信 这样 做不 果, 这样 做至少是没有良好 格的。
   C++ 为显 转换 提供了四 不同的操作符: static_case dynamic_cast const_cast reinterpret_cast 。个人 认为 C 格的相比似乎都没有什 么进步
  引用: 转换关闭 或挂起了正常的 检查 烈建 程序 避免使用 转换 ,不依 赖强 转换 也能写好很好的 C++ 程序。
 
 
标题: 类对 象的 转换
与算 术类 型相比, 转换 复杂 。因 术转换 及到精度的 问题 ,而 类对 象的 转换 及到能否 转换 以及怎 样转换 问题
   转换 就是 转换 ,它会出 在你没有注意的地方。参看以下代
class CMyInt
{
public:
   CMyInt();
   CMyInt(int i);
   ~CMyInt();
private:
   int m_i;
};
CMyInt::CMyInt()
{
   m_i = 0;
   cout << " 无参数构造 ( 0)" << endl;
}
CMyInt::CMyInt(int i)
{
   m_i = i;
   cout << " 从整数构造 , 值为 " << i << endl;
}
CMyInt::~CMyInt()
{
   cout << " 析构 " << m_i << endl;
}
int _tmain(int argc, _TCHAR* argv[])
{
   CMyInt a;
   a = 3;
   return 0;
}
   行以上代 发现 ,程序中有两次构造与两次析构,原因是 “a = 3;” 赋值 表达式要将右 转换为 型。 转换 是通 过调 用构造函数来 行的。
  不 ,以上代 只是 测试 用的, 千万不要在 实际 工作中写 这样 的代 。因 为这样 做是很不科学的。 “a = 3;” 这样 的表达式,只要 CMyInt 提供了右 值类 int 赋值 操作符重 ,就可以避免使用构造函数来 转换 。操作符重 如下:
  在 public 段添加一行: “CMyInt& operator = (const int i);” 然后在 外部添加以下代
CMyInt& CMyInt::operator = (const int i)
{
   m_i = i;
   cout << "operator =" << i << endl;
   return *this;
}
  加了以上代 ,同 “a = 3;” 就不使用 转换 了,改 使用 赋值 操作。我写下以上 段程序的目的只是 验证 VC++ 中会有 类对 象的 转换 ,它 与算 术类 型的 转换 差不多:生成一个合适的 临时对 象,参与运算以后再把 临时对 放掉
标题: 转换 转换
于我 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<j)” 值输 出,然后 测试 一下 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 (i<MaxSize && Array[i]>0)” “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<a; ++i)
     for (int j=0; j<b; ++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<int>::const_iterator ivec = vec.begin();” ivec 并不是常量,只是它指向的元素会得到保 。如果要定 本身 const 的迭代器,要用 “const vector<int>::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<Max; i++);” 。但是,按本 法,我 这样 写似乎同 犯了两个 错误
   1 、本 中写 for 句中的第二个表达式(条件表达式) 时总 是用 != ,而不用 < 几天来 然心里 得奇怪,但是一直没去思考 里面的含 。本 没有急着告 所以然 ,只是提醒我 完本 的第二部分后就会明白。我等着吧 :)
   2 、我 得用 Max 这样 一个 量去代替某个需要用函数才 能返回的 可以增加运行效率,但是本 的建 与我的理解相反。它建 “i<ivec.size()” 数据 构可以 动态 …… 如果确 增加了新元素的 ,那 么测试 已保存的 size 环结 束条件就会有 问题 ,因 没有将新加入的元素 算在内。
  同 ,本 书为 了打消运行效率的 顾虑 提到了 函数 个概念。 没有到介 候,但是 C++ 是一个有机体,在 述某一个知 点的 候,不可能完全避免另一个知 点。 所以,我 是建 初学者不要急着 阅读
 
 
标题: vector
模板 了,可惜的是,在本 的第三章, 不能 让类 模板浮出水面,只能将就着提一下。
  引用:使用 模板可以 写一个 或函数定 ,而用于多个不同的数据 型。 ……vector 并不是一 数据 型,而只是一个 模板,可用来定 任意多 数据 型。
  笔 “vector<int> ivec;” 中, “vector<int>” 是一个数据 型, 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<Max; 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 明吧
 
  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值