条款21: 尽可能使用const

对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const,还有,两者都不指定为const:

char *p              = "hello";          // 非const指针,
                                         // 非const数据

const char *p        = "hello";          // 非const指针,
                                         // const数据

char * const p       = "hello";          // const指针,
                                         // 非const数据

const char * const p = "hello";          // const指针,
                                         // const数据

声明迭代器为const表示这个迭代器不得指向不同的东西,但它所指的东西的值是可以改变的,如果希望迭代器所指的东西不可被改动,可以使用const_iterator

vector<int>vec = { 0,1,2 };
const vector<int>::iterator iter = vec.begin();
*iter = 10;//ok
iter++;//出错
vector<int>::const_iterator it = vec.begin();
*it = 10;//出错
++it;//ok

 

const的一些强大的功能基于它在函数声明中的应用。在一个函数声明中,const可以指的是函数的返回值,或某个参数;对于成员函数,还可以指的是整个函数。


 

类的成员函数后面加 const,表明这个函数不会对这个类对象的数据成员(准确地说是非静态数据成员)作任何改变。

在设计类的时候,一个原则就是对于不改变数据成员的成员函数都要在后面加 const,而对于改变数据成员的成员函数不能加 const。所以 const 关键字对成员函数的行为作了更加明确的限定:有 const 修饰的成员函数(指 const 放在函数参数表的后面,而不是在函数前面或者参数表内),只能读取数据成员,不能改变数据成员;没有 const 修饰的成员函数,对数据成员则是可读可写的。

除此之外,在类的成员函数后面加 const 还有什么好处呢?那就是常量(即 const)对象可以调用 const 成员函数,而不能调用非const修饰的函数。非常量对象可以随意调用const或非const修饰的函数。正如非const类型的数据可以给const类型的变量赋值一样,反之则不成立。const 引用的是对象时只能访问该对象的const 函数

class student
{
public:
    student(){}
    student(const string &nm, int sc = 0)
        :name(nm), score(sc){}
    void set_student(const string &nm, int sc = 0)
    {
        name = nm;
        score = sc;
    }
    const string &get_name()const
    {
        return name;
    }
    int get_score()const
    {
        return score; 
    }

private:
    string name;
    int score;
};

void output(const student &stu)
{
    //正常调用
    cout << stu.get_name();
    cout << stu.get_score();
}

若get_name和get_score没有const修饰,则const student变量不能调用这两个函数

class student
{
public:
    student(){}
    student(const string &nm, int sc = 0)
        :name(nm), score(sc){}
    void set_student(const string &nm, int sc = 0)
    {
        name = nm;
        score = sc;
    }
    const string &get_name()
    {
        return name;
    }
    int get_score()
    {
        return score; 
    }

private:
    string name;
    int score;
};

void output(const student &stu)
{
    //编译出错
    cout << stu.get_name();
    cout << stu.get_score();
}

为什么 get_name() 前面也加 const。如果没有前后两个 const 的话,get_name() 返回的是对私有数据成员 name 的引用,所以通过这个引用可以改变私有成员 name 的值(不会报错),如

student stu("wang", 85);
stu.get_name() = "li";

令函数返回一个常量值,往往可以降低因客户错误而造成的意外

/*
返回const对象是为了避免出现以下这种错误
Rational a,b,c;
(a * b) = c;
若a、b、c是内置类型,直接出错,而一个良好的用户自定义类型的特征是避免无端地与内置类型不兼容
*/
class Rational{};
const Rational operator* (const Rational &lhs, const Rational &rhs);

 

-----------------------------------------------------------------------------------------------------------------------------------------------------

//length为const成员函数,不能修改成员变量,所以用mutable关键字修饰,这样即使在const成员函数内也可能会被改变
//被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
class CTextBlock
{
public:
    size_t length() const;
private:
    char* pText;
    mutable size_t textLength;
};

size_t CTextBlock::length() const
{
    textLength = strlen(pText);
    return textLength;
}

两个成员函数如果只是常量性不同,可以被重载(指该函数是否为const成员函数)

class TextBlock
{
public:
    TextBlock(char *str):text(str){}
    const char& operator[](size_t position) const
    {
        return text[position];
    }
    char& operator[](size_t position)
    {
        return text[position];
    }
private:
    string text;
};


int main()
{
    //调用non-const TextBlock::operator[]
    TextBlock tb("Hello");
    cout << tb[0] << endl;

    //调用const TextBlock::operator[]
    const TextBlock ctb("World");
    cout << ctb[0];
    

    system("pause");
    return 0;
}

 

当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复(effective c++ p24 例子)

 

posted @ 2014-08-15 13:17  合唱团abc  阅读(209)  评论(0编辑  收藏  举报