在整理资料的时候,翻出这个笔记,方便以后自己回忆。这2本挺不错的,建议C++入门一段时间了的同学可以去看看。
-------------------------------------------- 笔记部分,随便记得 --------------------------------------------
1.
应使用初始化代替声明 + 赋值的方式。
int i;
i = f(); // 坏——初始化和声明分离
int i = g(); // 好——初始化时声明
2.
如果发量是一个对象,每次迕入作用域都要调用其极造函数,每次退出作用域都要调用其枂极函数。
// 低效的实现
for (int i = 0; i <1000000; ++i)
{
Foo f; // 极造函数和枂极函数分别调用 1000000 次!f.DoSomething(i);
}
类似发量放到循环作用域外面声明要高效的多:
Foo f; // 极造函数和枂极函数叧调用 1 次 for(int i = 0; i < 1000000; ++i) {
f.DoSomething(i); }
3.
禁止使用 class 类型的全尿发量(包括 STL 的 string,vector 等等),因为它们的初始化顺序有可能 导致极造出现问题。内建类型和由内建类型极成的没有极造函数的结极体可以使用,如果你一定要使用 class 类型的全尿发量,请使用单件模式(singleton pattern)。
4.
嵌套类符吅尿部使用原则,叧是丌能在其他头文件中前置声明,尽量不要 public;
5.
对单参数构造函数使用C++关键字explicit。
6.
仅当只有数据时使用struct,其它一概使用class。
7.
存取函数的定义一般内联在头文件中。例如get set函数
8.
定义次序如下:public:、protected:、private:,如果那一块没有,直接忽略即可。
每一块中,声明次序一般如下:
1) typedefs和enums;
2) 常量;
3) 构造函数;
4) 析构函数;
5) 成员函数,含静态成员函数;
6) 数据成员,含静态数据成员。
宏DISALLOW_COPY_AND_ASSIGN置于private:块之后,作为类的最后部分。参考拷贝构造函数。
.cc文件中函数的定义应尽可能和声明次序一致。
9.
倾向于选择短小、凝练的函数。
长函数有时是恰当的,因此对于函数长度并没有严格限制。如果函数超过40行,可以考虑在不影响程序结构的情况下将其分割一下。
即使一个长函数现在工作的非常好,一旦有人对其修改,有可能出现新的问题,甚至导致难以发现的bugs。使函数尽量短小、简单,便于他人阅读和修改代码。
10.
函数形参表中,所有引用必须是 const:
事实上返是一个硬性约定:输入参数为值戒常数引用,输出参数为挃针;输入参数可以是常数挃针,但丌
能使用非常数引用形参。
11.
我们强烈建议你在仸何可以使用的情冴下都要使用const。
12.
C++内建整型中,唯一用到的是 int,如果程序中需要丌同大小的发量,可以使用<stdint.h>中的精确宽 度(precise-width)的整型,如int16_t。
13.
宏存储常量可以 const 发量替代;宏“缩写”长发量名可以引用替代;使用宏迕行条件 编译,返个......,最好丌要返么做,会令测试更加痛苦(#define 防止头文件重包吨当然是个例外)。
14.
整数用 0,实数用 0.0,挃针用 NULL,字符(串)用'\0'。
15.
函数命名、发量命名、文件命名应具有描述性,丌要过度缩写,类型和发量应该是名词,函数名可以用“命 令性”动词。
16.
内联函数必须放在.h 文件中,如果内联函数比轳短,就直接放在.h 中。如果代码比轳长,可以放到以-inl.h 结尾的文件中。对亍包吨大量内联代码的类,可以有三个文件:
url_table.h
url_table.cc
url_table-inl.h
参考第一篇-inl.h 文件一节。
17.
TODO 徆丌错,有时候,注释确实是为了标记一些未完成的戒完成的丌尽如人意的地方,返样一搜索, 就知道迓有哪些活要干,日志都省了。
180
有些条件诧句写在同一行以增强可诺性,叧有当诧句简单幵丏没有使用 else 子句时使用:
if (x == kFoo)return new Foo();
if (x == kBar) return newBar();
如果诧句有 else 分支是丌允许的:
// Not allowed - IF statementon one line when there is an ELSE clause
if (x) DoThis(); elseDoThat();
通常,单行诧句丌需要使用大括号,如果你喜欢也无可厚非,也有人要求 if必须使用大括号: if (condition)
DoSomething(); // 2 spaceindent.
if (condition) {
DoSomething(); // 2 spaceindent.
}
19.在声明挃针发量戒参数时,星号不类型戒发量名紧挨都可以:
// These are fine, spacepreceding.
char *c;
const string &str;
注:个人比轳习惯不发量紧挨的方式。
20.
return 表达式中不要使用囿括号。
21.
代码块头、尾丌要有空行
22.
基类 结尾加infface
23。
尽可能在定义变量的同时初始化该变量(就近原则)
24.
一元操作符如“!”、“~”、“++”、“--”、“&”(地址运算符)等前后不加空格。
25.
长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以 便突出操作符)。
26.
应当将修饰符 * 和 & 紧靠变量名
27.
例如不要把CurrentValue 写成NowValue。
28.
变量的名字应当使用“名词”或者“形容词+名词”。
29.
全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。
30.
常量全用大写的字母,用下划线分割单词。
例如:
const int MAX = 100;
const int MAX_LENGTH = 100;
31.
静态变量加前缀 s_(表示 static)。
32.
如果不得已需要全局变量,则使全局变量加前缀 g_
33.
C++/C 循 环 语 句 中 , for 语 句 使 用 频 率 最 高 , while 语 句 其 次 , do 语 句 很 少 用 。
34.
在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的 循环放在最外层,以减少 CPU跨切循环层的次数
35.
如果循环体内存在逻辑判断,并且循环次数很大,宜将逻辑判断移到循环体的外面
36.
不可在 for 循环体内修改循环变量,防止 for 循环失去控制。
37.
建议 for语句的循环控制变量的取值采用“半开半闭区间”写法。
38.
就象楼房着火了,来不及从楼梯一级一级往下走,可从窗口跳出火坑。所以我们主
张 少 用 、 慎 用 goto语 句 , 而 不 是 禁 用 。
39.
尽量使用含义直观的常量来表示那些将在程序中多次出现的数字或 字符串。
高质量 C++/C 编程指南,v 1.0
例如: #define constint constfloat
MAX 100 /* C语言的宏常量 */ MAX=100;
// C++语言的const常量 PI=3.14159;
// C++语言的const常量
40.
在 C++程序中只使用 const常量而不使用宏常量,即const常量完 全取代宏常量。
41.
需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。
42.
怎样才能建立在整个类中都恒定的常量呢?别指望 const数据成员了,应该用类中 的枚举常量来实现。例如
classA {⋯
enum { SIZE1 = 100, SIZE2 =200}; // 枚 举 常 量 int array1[SIZE1];
2001
Page 34 of 100
Generated by Foxit PDFCreator © Foxit Software
http://www.foxitsoftware.com
For evaluation only.
应给出一些孤立的值。 例如:
int array2[SIZE2];
};
43.
参数的书写要完整,不要贪图省事只写参数的类型而省略参数名字。
44.
参数命名要恰当,顺序要合理。一般地,应将目的参数放在前面,源参数放在后面。
45.
如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该 指针在函数体内被意外修改。
46.
如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来 传递,这样可以省去临时对象的构造和析构过程,从而提高效率。
47.
函数名字与返回值类型在语义上不可冲突。
48.
如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递”,否则会出 错。对于赋值函数,应当用“引用传递”的方式返回 String 对象。
49.
在函数体的“入口处”,对参数的有效性进行检查。
50.
在函数体的“出口处”,对 return 语句的正确性和效率进行检查。
51.
函数的功能要单一,不要设计多用途的函数。
52.
函数体的规模要小,尽量控制在 50 行代码之内。
53.
在函数的入口处,使用断言检查参数的有效性(合法性)。特别时指针
void *memcpy(void*pvTo,constvoid*pvFrom,size_tsize){
assert((pvTo!=NULL)&&(pvFrom!=NULL));//使用断言
byte *pbTo = (byte *) pvTo;byte *pbFrom = (byte *) pvFrom; while(size -- > 0 )
*pbTo ++ = *pbFrom ++ ;returnpvTo;
}
54.
引用的主要功能是传 递函数的参数和返回值。C++语言中,函数的参数和返回值的传递方式有三种:值传递、 指针传递和引用传递。
55.
使用 free 或 delete 释放了内存后,没有将指针设置为 NULL。导致产生“野指针”。
56.
用 malloc或 new申请内存之后,应该立即检查指针值是否为 NULL。 防 止 使 用 指 针 值 为 NULL 的 内 存 。
57.
不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右 值使用。
58.
注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针
59.
(1)指针消亡了,并不表示它所指的内存会被自动释放。
(2)内存被释放了,并不表示指针会消亡或者成了NULL 指针。
60.
“野指针”不是 NULL 指针,是指向“垃圾”内存的指针。人们一般不会错用 NULL 指针,因为用 if 语句很容易判断。但是“野指针”是很危险的,if 语句对它不起作用。
“野指针”的成因主要有两种: (1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为 NULL 指针,它 的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么 将指针设置为 NULL,要么让它指向合法的内存。例如
char *p = NULL;
char *str = (char *)malloc(100);
(2)指针 p 被 free 或者 delete 之后,没有置为NULL,让人误以为 p 是个合法的指针。
61.
1)越是怕指针,就越要使用指针。不会正确使用指针,肯定算不上是合格的程序员。 (2)必须养成“使用调试器逐步跟踪程序”的习惯,只有这样才能发现问题的本质。
62。
在做数据类型转换的时候不要用(int),用static_cast<int>
63.
当 心 隐 式 类 型 转 换 导 致 重 载 函 数 产 生 二 义 性
由 于 数 字 本 身 没 有 类 型 , 将数 字 当 作 参 数 时 将 自 动 进 行 类 型 转 换 ( 称 为 隐式类型转换)
64.
重 载 与 覆 盖 成员函数被重载的特征:
( 1) 相 同 的 范 围 ( 在 同 一 个 类 中 ); (2)函数名字相同;
( 3) 参 数 不 同 ;
( 4) virtual 关 键 字 可 有 可 无 。
覆盖是指派生类函数覆盖基类函数,特征是: (1)不同的范围(分别位于派生类与基类); (2)函数名字相同;
( 3) 参 数 相 同 ;
( 4) 基 类 函 数 必 须 有 virtual 关 键 字 。
65.
这里“隐藏”是指派生类的函数屏蔽了与其同名的基类函数,规则如下: (1)如果派生类的函数与基类的函数同名,但是参数不同。此时,不论有无 virtual 关键字,基类的函数将被隐藏(注意别与重载混淆)。 (2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual 关键字。此时,基类的函数被隐藏(注意别与覆盖混淆)。
66.
参数缺省值只能出现在函数的声明中,而不能出现在定义体中。
例如:
void Foo(int x=0, int y=0);
// 正 确 , 缺 省 值 出 现 在 函 数 的 声 明 中 // 错 误 , 缺 省 值 出 现 在 函 数 的 定 义 体 中
void Foo(int x=0, int y=0)
{
⋯
}
67.
如果函数有多个参数,参数只能从后向前挨个儿缺省,否则将导致 函数调用语句怪模怪样。
68.
已知类 String 的原型为:
class String
{
public:
String(const char*str = NULL);
String(const String&other);//拷贝函数
~ String(void);
String&operate=(constString&other);//赋值函数
private:
char *m_data;
};
69.
不 能 被 重 载 的 运 算 符
在 C++运算符集合中,有一些运算符是不允许被重载的。这种限制是出于安全方面
的考虑,可防止错误和混乱。
( 1) 不 能 改 变 C++内 部 数 据 类 型( 如 int,float 等 ) 的 运 算 符。 (2)不能重载‘.’,因为‘.’在类中对任何成员都有意义,已经成为标准用法。 (3)不能重载目前 C++运算符集合中没有的符号,如#,@,$等。原因有两点,一是难以理解,二是难以确定优先级。 (4)对已经存在的运算符进行重载时,不能改变优先级规则,否则将引起混乱。
70.
不宜使用内联:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
71.
C++编译器将自动为 A 产生四个缺省的函数,如
高质量 C++/C 编程指南,v 1.0
A(void);// 缺 省 的 无 参 数 构 造 函 数
A(const A &a);// 缺 省 的 赋 值 函 数
~A(void);// 缺 省的 析 构 函 数
A & operate =(const A &a);// 缺 省 的 拷 贝 构 造 函 数
72.
classString
{
public: String(constchar *str = NULL); // 普 通 构 造 函 数
String(constString&other);//拷贝构造函数 ~ String(void); // 析 构 函 数
String&operate=(constString&other);//赋值函数
private:
char *m_data; //用 于 保 存 字 符 串
};
73.
Stroustrup在设计 C++语言时充分考虑了这个问题 并很好地予以解决:把对象的初始化工作放在构造函数中,把清除工作放在析构函数中。 当对象被创建时,构造函数被自动执行。当对象消亡时,析构函数被自动执行。这下就 不用担心忘了对象的初始化和清除工作。
74.
如果类存在继承关系,派生类必须在其初始化表里调用基类的构造函数。
75.
类的 const 常量只能在初始化表里被初始化,因为它不能在函数体内用赋值的方式来初始化
76.
拷贝构造函数是在对象被创建时调用的,而赋值函数只能被已经存在了的对象调用。
String a(“hello”);
String b(“world”);
String c=a;//调用了拷贝构造函数,最好写成 c(a);
c=b; //调用了赋值函数 本例中第三个语句的风格较差,宜改写成 Stringc(a)以区别于第四个语句。
77.
若在逻辑上 A 是 B 的“一部分”(apart of),则不允许 B 从 A 派生, 而是要用 A 和其它东西组合出 B。 例如眼(Eye)、鼻(Nose)、口(Mouth)、耳(Ear)是头(Head)的一部分,所以
类 Head应该由类 Eye、Nose、Mouth、Ear 组合而成,不是派生而成。如示例 10-2-1 所 示。
class Eye { public: void Look(void); }; | class Nose { public: void Smell(void); }; |
class Mouth { public: void Eat(void); }; | class Ear { public: void Listen(void); }; |
// 正确的设计,虽然代码冗长。 class Head { public: void Look(void) { void Smell(void) { void Eat(void) { void Listen(void) { private: Eye m_eye; m_eye.Look(); } m_nose.Smell(); } m_mouth.Eat(); } m_ear.Listen(); } }; Nose m_nose; Mouth m_mouth; Ear m_ear; |
示例 10-2-1Head 由 Eye、Nose、Mouth、Ear 组合而成
78.
使用 const 提高函数的健壮性
79.
如果输入参数采用“值传递”,由于函数将自动产生临时变量用于复制该参数,该输 入参数本来就无需保护,所以不要加const修饰。 例如不要将函数 voidFunc1(intx)写成 voidFunc1(constintx)。同理不要将
函数 voidFunc2(Aa)写成 voidFunc2(constAa)。
80.
如果输入参数采用“指针传递”,那么加 const修饰可以防止意外地改动该指针,起 到保护作用。
81.
完全没有必要,因为内部数据类型的参数不存在构造、析构的过程,而复制也 非常快,“值传递”和“引用传递”的效率几乎相当。
问题是如此的缠绵,我只好将“const&”修饰输入参数的用法总结一下
对 于 非 内 部 数 据 类 型的 输 入 参 数 ,应 该 将“ 值 传 递 ”的 方 式 改 为“ const引 用 传 递”,目的是提高效率。例如将 voidFunc(Aa)改为 voidFunc(constA&a)。
对 于 内 部 数 据 类 型 的输 入 参 数 ,不 要 将“ 值 传 递 ”的 方 式 改 为“ const引 用 传 递 ”。 否则既达不到提高效率的目的,又降低了函数的可理解性。例如 voidFunc(intx)不 应该改为voidFunc(constint&x)。
82.
如果返回值不是内部数据类型,将函数 AGetA(void)改写为constA&GetA(void)
的确能提高效率。但此时千万千万要小心,一定要搞清楚函数究竟是想返回一个对象的 “ 拷 贝 ” 还 是 仅 返 回 “ 别 名 ”就 可 以 了 , 否 则 程 序 会 出 错 。
84.
任何不会修改数据成员的函数都应该声明为 const类型。
85.
当心数据类型转换发生错误。尽量使用显式的数据类型转换(让人们知道发生了什么事),避免让编译器轻悄悄地进行隐式的数据类型转换。
注:红色的部分是自己要特别留意的