C++ Primer读书笔记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++ 中会有 类对 象的 转换 ,它 与算 术类 型的 转换 差不多:生成一个合适的 临时对 象,参与运算以后再把 临时对 放掉
标题: 函数再
函数是 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++ 中会有 类对 象的 转换 ,它 与算 术类 型的 转换 差不多:生成一个合适的 临时对 象,参与运算以后再把 临时对 放掉
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值