尽可能使用const

尽可能使用const

const定义语义约束:制定一个不被改动的对象,编译器会强制实施这项约束。只要某值保持不变时事实,就应该确实说出来,这样编译器可以确保这项约束不违反。

const多才多艺:可以用在classes外部修饰global或namespace作用域中的常量,或修饰文件、函数、或区块作用于中被声明为static的对象。它可以用来修饰classes内部的static或non-static成员变量。面对指针,也可以指出指针本身、指针所指之物、或两者都不是const

char greet[] = "hello";
char *p = greet;            // 非常量指针,非常量数据
const char *p = greet;      // 非常量指针,常量数据
char * const p = greet;     // 常量指针,非常量数据
const char * const = greet; // 常量指针,常量数据

如果关键字const出现在*号左边,表示被指物是常量;如果出现在*号右边,表示指针自身是常量;如果出现在*号两边,表示被指物和指针两者都是常量。

如果被指物是常量,可以将关键字const写在类型之前,也可以写在类型之后,*号之前。具体如下:

void f1(const Widget *pw);  // f1获得一个指针,指向一个常量的Widget对象
void f1(Widget const *pw);  // f2获得一个指针,指向一个常量的Widget对象

STL迭代器以指针为根据塑模出来,故迭代器作用是T *指针。声明迭代器为const就像声明指针为const一样(声明一个T *const),表示这个迭代器不的指向不同的东西,但它所指的东西的值可以改动。如果希望迭代器所指的东西不可被改动,则定义为const T *指针即可。举例如下:

std::vector<int> vec;
const std::vector<int>::iterator iter = vec.begin();  // iter为T * const
*iter = 10;    // 正确可以改变
++iter;        // 错误,iter是const不能改变

std::vector<int>::const_iterator cIter = vec.begin(); // cIter为const T*
*cIter = 10;   // 错误,*cIter是cosnt
++cIter;       // 正确,可以改变cIter

const最具威力的用法是面对函数声明时的应用。在一个函数声明式内,const可以和函数返回值、各参数、函数自身(如果是成员函数)产生关联。令函数返回一个常量值,往往可以降低因客户错误而造成的意外,而又不至于放弃安全性和高效性。一个良好的用户自定义类型的特征是他们避免无端地与内置类型不兼容。而对于const参数,就像local const对象一样,在必要的时候使用它们。除非有需要改动参数或local对象,否则请将他们定义为const。可以防止意外输入。举例如下:

class Rational{...};
const Rational operator* (const Rational &lhs, const Rational &rhs);

const成员函数

将const实施于成员函数目的,是为了确认该成员函数可作用于const对象身上。这类函数重要原因:1)它们使class接口比较容易被理解。得知哪个函数可以改变对象内容,哪个函数不可以很重要。2)它们使操作const成为可能。这是代码高效的关键。C++高效的一个根本方法是pass by reference-to-const方式传递对象。但前提是const成员函数可用来处理取得的const对象。

C++重要特性:两个成员函数如果只是常量性不同,可以被重载。举例如下:

class TextBlock{
public:
    const char &operator[](std::size_t position) const  // operator[] for const对象
    { return text[position]; }
    char &operator[](std::size_t position)              // operator[] for non-const对象
    { return text[position]; }
private:
    std::string text;
};
TextBlock的operator[]s可以被这么使用:
TextBlock tb("hello");
std::cout << tb[0];     // 调用non-const TextBlock::operator[]
const TextBlock cbt("World");
std::cout << ctb[0];    // 调用const TextBlock::operator[]

真实程序中const对象大多用于passed by pointer-to-const或passed by reference-to-const传递结果。可以使用下面调用:

void print(const TextBlock& ctb)   // ctb为const
{
    std::cout << cbt[0];   //调用const TextBlock::operator[]
}

只要重载operator[]并对不同的版本给于不同的返回类型,就可以令const和non-const TextBlocks获得不同的处理:

std::cout << tb[0];    // 正确,读non-const TextBlock
tb[0] = 'x';           // 正确,写non-const TextBlock
std::cout << ctb[0];   // 正确,读const TextBlock
ctb[0] = 'x';          // 错误,写const TextBlock

上面错误原因在于对一个const版本的operator返回值实施赋值动作错误。注意:non-const operator[]的返回类型是个reference to char,不是char。如果是char,则tb[0] = ‘x’将不能通过编译。原因是函数返回类型是个内置类型,改动函数返回值从来就不合法。

bitwise const:一个更改了“指针所指之物”的成员函数虽然不能算是const,但如果只有指针(而非其所指物)隶属于对象,那么此函数为bitwise const不会引发编译器异议。

logical const:一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端侦测不出的情况下才得如此。

利用C++的一个与const相关的摆动场,mutable(可变的)。mutable释放掉non-static成员变量的bitwise constness约束。

在const和non-const成员函数中避免重复

令non-const调用其const兄弟是一个避免代码重复的安全做法——即使过程中需要一个转型动作。举例如下:

class TextBlock{
public:
    const char &operator[](std::size_t position)const 
    {
        ...
        return text[position];
    }
    char& operator[](std::size_t position)   // 现在只调用const op[]
    {
        // 将op[]返回值的const转除为*this加上const调用const op[]
        return const_cast<char&>(static_cast<const TextBlock&>(*this)[positon]);
    }
}

上例中有两个转型动作。第一次用来为*this添加const(这使接下来调用operator[]时得以调用const版本),第二次则是从const operator[]的返回值中移除cosnt。它可以避免代码重复效果。它运用const operator[]实现了non-const版本。**注意:**const成员函数承诺绝不改变其对象的逻辑状态,non-const成员函数却没有这版承诺。如果在const中调用了non-const函数,就是冒了这样的风险:你曾经承诺不改动的那个对象被改动了。const成员函数调用non-const成员函数是一种错误。因为对象有可能被改变。要使这样的代码通过编译器,需要使用const_cast将*this身上的const性质释放掉。non-const成员函数本来就可以对其对象做任何动作,故在其中调用一个const成员函数不会带来风险。

const是个奇妙非比寻常的东西。在指针和迭代器身上,在指针、迭代器及reference指涉的对象身上;在函数参数和返回类型身上;在local变量身上;在成员函数身上,林林总总不一而足。应该应可能使用它。

注意:

1)将某些东西声明为const可帮助编译器侦测出错误用法。const可别施加于任何作用域内的对象、函数参数、函数返回类型。成员函数本体。

2)编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”(conceptual constness)。

3)当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复。

参考文献:Effective C++,侯捷

你可能感兴趣的:(Const)