面向过程的编程风格
Procedural Programming
1.
C++
不允许改变改变
reference
所代表的对象,对
reference
的所有操作与对“
reference
所代表的对象”所进行的操作相同。
2.
以
by reference
方式传递对象当作函数参数时,复制的将是对象的地址,函数中对该对象的所有操作都相当是对传入的对象进行间接操作。
3.
pointer
和
reference
的最重要差异是,
pointer
可以为空,使用前一定要确保其值非
0
,而
reference
必定代表某个对象,不必作此检查。
4.
编译器无法根据函数返回值型别来区分两个具有相同名称的函数,因为返回值型别无法保证提供我们一个足以区分不同重载函数的情境。
5.
由函数指针寻址出来的函数,其调用方式和一般函数相同。
6.
可以给函数指针赋予初值,函数名称即代表了函数的地址。
7.
标准的或项目专属的头文件应用尖括号扩住;用户自行提供的头文件则使用引号。
2002-6-3
泛型编程风格
Generic Programming
1.
Standard Template Library (STL)
主要由两种组件构成:容器
container
和泛型算法
generic algorithm
<
通过
function template
技术,实现与容器及数值类型无关之功能
>
。
2.
容器分类:
<
切记:
vector
可以是空的,数组则否
>
序列式容器
sequential container
:
vector, list, deque……
关联式容器
associative container
:
map, set, ……
3.
iterator
及
const_iterator
实际上是各个容器定义式内的嵌套
nested
型别。
4.
使用泛型算法须
#include <algorithm>
,使用
function object
须
#include <functional>
。
5.
function object
是某种
class
的实体对象,该
class
对
function call
运算符进行了重载操作从而可使
function object
被当作一般函数来使用。令
function call
运算符成为
inline
,从而消除“通过函数指针来调用函数“时需付出的额外代价。
6.
function object adapter
会对
function object
进行修改操作。
7.
绑定配接器
binder
adapter
<bind1nd, bind2nd>
会使
bineary function object
变成
unary function object
;
negator
adaper
<not1, not2>
会逆转
function object
的真伪值;另外一种有用的
adapter
叫做
instertion
adapter
<back_inserter, inserter, front_inserter>
。
#include <iterator>
8.
map
被定义为一对数值,其中
key
通常是个字符串,扮演索引角色,另一个数值是
value
。
9.
任何一个
key
值在
map
或
set
内最多只有一份,若要多份相同
key
值,使用
multimap
或
multiset
。
完成日期:
2002-6-2
基于对象的编程风格
Object-Based Programming
1
.在
class
内部定义的
member
函数被自动视为
inline
函数。对
inline
函数而言,声明在
class
内部或外部并无区别,同
non-member inline
函数一样,它应于头文件中定义。
2
.
Triangular t();
被编译器视为一个函数定义!并不是声明或定义一个
Triangular
对象!
3
.以某个
class object
作为另一个
object
的初值时,会发生
default memberwise initialization
<
实际上是自动产生一个
copy constructor>
,可以为该
class
提供一个
copy constructor
来改变这一默认行为模式。
4
.若有必要为
class
撰写
copy constructor
,则同样有必要为它撰写
copy assignment operator
,除非撰写
copy constructor
的目的仅仅是为了激活编译器实施
NRV
优化。
5
.凡是在
class
主体以外定义的
const member function
,必须同时在声明与定义时都提供
const
关键字,
const
紧接于函数参数表之后。
6
.
member function
返回一个指向
member data
的
non-const reference
,实际上等于将该
member data
开放出去,允许程序在其它地方加以修改。由于函数可以根据参数
const
与否
而重载,故可以提供两份定义,一份为
const
版本,一份为
non-const
版本。
8.
设计
class
时,鉴定其
const member function
是一件很重要的事情!
9.
将
member data
声明为
mutable
表明:对该
member data
的修改不会破坏
class object
的常数性。
10.
欲以一个对象复制出另一个对象,先确定两个对象是否相同是个好习惯。
11.
运算符的重载规则:不可以引入新的运算符,除了
., .*, ::, ?:
4个运算符,其它运算符皆可被重载;运算符的操作数
operand
不可改变;运算符的优先级不可改变;运算符函数的参数列中必须至少有一个参数为
class
型别。
12
.
increment
和
decrement
运算符的前置及后置版本都可直接施行于
class object
其之上,编译器会自动为后置版产生一个
int
引数,其值必为
0
。
13
.所谓
friend
,具备了与
class member function
相同的存取权限,可以存取
class
的
private member
。
14
.只要
class
设计者显示提供了
copy assignment operator
,它就会被用来取代
default memberwise copy
行为。
15
.当编译器在编译过程中遇到函数调用,例如
lt(ival)
,
lt
可能是函数名称,可能是函数指针,也可能是一个提供了
function call
的
function object
。如果
lt
是个
function object
,编译器会在内部将此语句转化为:
lt.operator(ival)
;
16
.
function call
可以接受多个运算符,通常将
function object
当作参数传给泛型算法。
17
.为了取得某个
member function
的地址,只需对函数名称施以取址
address-of
运算符,同时,函数名称之前必须先以
class object
运算符加以修饰,而返回型别及参数表皆不需指明,如:
void (classname::*mfptr) (int) = &classname::mfname;
18
.注意所谓的
maximal munch
编译规则,如:
static vector<vector<int> > seq;
两个
”>”
号之间必须加有空格,否则无法成功编译!
19
.
pointer to member function
和
pointer to function
的一个不同点是:前者必须通过同类的对象加以调用。
.*
符号是针对
class object
的
pointer to member selection
运算符,
->*
符号是针对
pointer to class object
的
pointer to member selection
。使用它们时注意必须加上外围小括号!如:
(classobject.*mfptr)(par);
完成日期:
2002-6-18
面向对象编程风格
Object-Oriented Programming
1.
面向对象编程的两项最主要的特性是继承
inheritance
和多态
polymorphism
。
2.
动态绑定
Dynamic binding
是面向对象编程风格的第三个独特概念,即找出实际被调用的究竟是哪一个派生类的函数。而静态绑定
Static binding
则在程序运行之前就决议出应该调用哪一个函数。
3.
多态和动态绑定的特性只有在使用
pointer
或
reference
时才能发挥。
4.
staitic member function
无法被声明为虚拟函数。
5.
任何一个类只要有纯虚拟函数,程序就会因其接口的不完整而无法为它产生任何对象,这种类只能作为派生类的子对象
subobject
之用,而且派生类必须为所有纯虚拟函数提供确切的定义。
6.
根据一般规则,凡基类定义有虚拟函数,其
destructor
应声明为
virtual
。但
Stanley B.Lippman
并不建议在这个基类中将其
destructor
声明为
pure virtual
,而是提供空白定义:
inline baseclass::~baseclass(){};
7.
对于
public inheritance
,继承而来的
public
成员和
protected
成员,无论在继承体系中的深度如何,都可视为派生类自身拥有的成员。
8.
每当派生类有某个
member
与其基类的
member
同名时,便会遮蔽住基类的那份
member
,若要在派生类中使用继承而来的那份
member
,必须使用
class scope
运算符加以修饰。
9.
不可为抽象基类定义任何对象,它们扮演的角色是每个派生类的
subobject
,基于此点,一般将抽象基类的
constructor
声明为
protected
而非
public
。
10.
派生类之
constructor
,不仅必须为派生类之
data members
进行初始化操作,还需为其基类之
data members
提供适当的值。
copy constructor
和
copy assignment operator
的情形也一样,唯一棘手的是,必须明白调用基类的
copy assignment operator
:
base::operator = (rhs);
11.
改写基类提供的虚拟函数,派生类提供的定义其函数型别必须完全符合基类所声明的函数原型,包括参数列、返回型别、常量型
const-ness
。但是,对于“返回型别”有个例外:当基类的虚拟函数返回某个基类形式(通常是
pointer
或
reference
)时,派生类中的同名函数可以返回该基类所派生出来的型别。
12.
在两种情况下,虚拟函数机制不会出现预期行为:
1
)在基类的
constructor
和
destructor
内;
2
)使用基类的对象而非对象的
pointer
或
reference
。
13.
typeid
运算符是
RTTI
的一部分,可以用它来查询多态化的
class pointer
或
class reference
,获得其所指对象的实际型别。
typeid
运算符会返回一个
type_info
对象,其中存储着与型别相关的种种信息。
#include<typeinfo>
完成日期:
2002-6-19
异常处理
Exception Handling
1.
初学者常犯的错误:将
C++
异常和
segmentation fault
或是
bus error
这类硬件异常混淆在一起。
2.
在异常处理机制终结某个函数之前,
C++
保证函数中的所有局部对象的
destructor
都会被调用。
3.
auto_ptr
是标准程序库提供的
class template
,它会自动
delete
通过
new
表达式配置的对象。
auto_ptr
将
dereference
运算符和
arrow
运算符予以重载,使得我们可以像使用一般指针一样使用
auto_ptr
对象。
#include <memory>
4.
如果
new
表达式无法从程序的自由空间
free store
配置到足够的内存,它会抛出
bad_alloc
异常对象。如果要压抑不让
bad_alloc
异常被抛出,可以这么写:
somepointer = new (nothrow) someclass;
这样,如果
new
动作失败,返回值为
0
。
5.
标准程序库定义了一套异常类体系
exception class hierarchy
,其最根部是名为
exception
的抽象基类。
exception
声明有一个
what()
虚拟函数,会返回一个
const char*
,用以表示被抛出异常的文字描述。
#include <exception>
6.
ostringstream class
提供“内存内的输出操作”,输出到一个
string
对象上。当需要将多笔不同型别的数据格式转化为字符串表现式时,它尤其有用。
ostringstream
提供的
str()
可以返回对应的那个
string
对象。
#include <sstream>
7.
iostream
库也对应提供了
istringstream class
,如果需要将非字符串数据的字符串表现式转化为其实际型别,
istringstream
可派上用场。
8.
string class
的转换函数
c_str()
会返回
const char*
!