第一节
对于类的静态常量,只有int,char,bool型可以在类内赋初始值,其他如double,float或自定义类型都必须在类外赋初始值,注意,静态常量在类内不管有没有赋初始值,都只是声明,定义本身在类的外部实现,例如:
class CostEstimate {
private:
static const double FudgeFactor; // declaration of static class constant
... // goes in header file
};
const double // definition of static class
CostEstimate::FudgeFactor = 1.35; // constant; goes in impl. file
当类本身需要一个类属常量值(value of a class constant),如定义一个固定大小的数组时,可以用到enum,例如:
class GamePlayer {
private:
enum { NumTurns = 5 }; // "the enum hack" - makes NumTurns a symbolic name for 5
int scores[NumTurns]; / fine
};
第二节
对于用const修饰的指针来说,const在的前边说明修饰的是指针指向的数据,在的后边说明修饰的是指针本身,例如
char greeting[] = "Hello";
const char *p = greeting; //修饰p指向的greeting内的数据,也就是说,greeting内的数据无法修改
char * const p = greeting; //修饰指针本身,指针无法做自增或者自减运算
对于STL中的迭代器(iterator)来说,用const修饰类似于 T* const,也就是对指针本身的修饰,如果想修饰iterator指向的数据的话,使用const_iterator就可以了,例如
std::vector<int>const_iterator p;
在某些时候,函数的返回值声明为const是非常必要的,例如:
const Rational operator*(const Rational& lhs, const Rational& rhs);
这个函数是要计算两个rational对象的乘积,如果返回值不声明为const的话,类似 (a * b) = c; 这样的语句就会变成合法的,这显然与内建类型不和谐,一个好的 user-defined types(用户自定义类型)的特点就是要避免与 built-ins(内建类型)毫无理由的不和谐。
基于同样的道理,在类的成员函数中,如果两个同名同参同返回值的函数在只有常量性不同的时候是可以被重载的,例如:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const // operator[] for
{ return text[position]; } // const objects
char& operator[](std::size_t position) // operator[] for
{ return text[position]; } // non-const objects
private:
std::string text;
};
这样声明保证了对类的对象取“[]”操作总是正确的,而在试图改变一个const对象的下标值的时候就会报错,这与内建类型的意义相同。
而如果想在const函数中想修改一些数据成员的时候,可以对这些数据成员使用mutable关键字。
另外,两个仅仅常量性不同的同名函数,函数体本身也是相似的,为了避免重复写代码,可以在non-const函数中调用const型的同名函数,例如:
class TextBlock {
public:
const char& operator[](std::size_t position) const // same as before
{
return text[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return
const_cast<char&>( // cast away const on
// op[]'s return type;
static_cast<const TextBlock&>(*this) // add const to *this's type;
[position] // call const version of op[]
);
}
};
static_cast将this本身强制转换成const类型,而const_cast将const型的返回值移除const限定。这里补充一下四个cast运算符的定义:
const_cast:移除变量的const或volatile限定符
static_cast:类似于C语言中的强制类型转换,不提供类型检查
dynamic_cast:用于基类指针向子类指针的转换
reinterpret_cast:处理无关类型之间的转换,产生新的值,与原始参数具有相同比特位
补充:关于const_cast转换,在C语言中可以使用(type)来实现,不过强烈不建议这样使用,当然,移除变量的const限定需要定义一个新的变量来指向它,原本的const型始终是常量,而试图改变这个新变量的时候会产生未定义行为, 所以除了上述条款中的应用外,尽量不使用const_cast。
第三节
对于类的构造函数,使用成员初始化列表(member initialization list)比在函数体中直接赋值要好得多,这是因为如果参数列表是内建类型,那么两者没有什么不同,如果参数列表是自定义类的话,在赋值之前会默认先调用缺省构造函数。而且在成员初始化列表中,最好是全部列出所有的数据成员。
类中的对象总是以声明的顺序进行初始化,即使成员初始化列表中将顺序颠倒,初始化的顺序仍然是按照声明时候的顺序进行初始化的。
通过用 local static objects(局部静态对象)代替 non-local static objects(非局部静态对象)来避免跨 translation units(转换单元)的 initialization order problems(初始化顺序问题)。