用 const 限定类的成员函数

来源:互联网 发布:linux 物理内存管理 编辑:程序博客网 时间:2024/06/06 06:34

(一)

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


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

除此之外,在类的成员函数后面加 const 还有什么好处呢?楼主告诉我们的:“获得能力:可以操作常量对象”,其实应该是常量(即 const)对象可以调用 const 成员函数,而不能调用非const修饰的函数。正如非const类型的数据可以给const类型的变量赋值一样,反之则不成立。 

对于const成员函数,"不能修改类的数据成员,不能在函数中调用其他不是const的函数",这是由const的属性决定的,楼主说得完全正确。 

请看下面一个完整的例子,然后我再作一些说明。 
#include <iostream>; #include <string>; using namespace std;  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; }; // output student's name and score void output_student( const Student& student ) {   cout << student.get_name() << "\t";   cout << student.get_score() << endl; }  int main() {   Student stu( "Wang", 85 );   output_student( stu ); }


设计了一个类 Student,数据成员有 name 和 score,有两个构造函数,有一个设置成员数据函数 set_student(),各有一个取得 name 和 score 的函数 get_name() 和 get_score()。请注意 get_name() 和 get_score() 后面都加了 const,而 set_student() 后面没有(也不能有const)。 

首先说一点题外话,为什么 get_name() 前面也加 const。如果没有前后两个 const 的话,get_name() 返回的是对私有数据成员 name 的引用,所以通过这个引用可以改变私有成员 name 的值,如 
  Student stu( "Wang", 85 );  stu.get_name() = "Li";

即把 name 由原来的 "Wang" 变成了 "Li",而这不是我们希望的发生的。所以在 get_name() 前面加 const 避免这种情况的发生。 

那么,get_name() 和 get_score() 这两个后面应该加 const的成员函数,如果没有 const 修饰的话可不可以呢?回答是可以!但是这样做的代价是:const对象将不能再调用这两个非const成员函数了。如 
const string& get_name(); // 这两个函数都应该设成 const 型int get_score();void output_student( const Student& student ) {   cout << student.get_name() << "\t"; // 如果 get_name() 和 get_score() 是非const成员函数,这一句和下一句调用是错误的  cout << student.get_score() << endl; }

由于参数student表示的是一个对const Student型对象的引用,所以 student 不能调用非const成员函数如 set_student()。如果 get_name() 和 get_score() 成员函数也变成非const型,那么上面的 student.get_name() 和 student.get_score() 的使用就是非法的,这样就会给我们处理问题造成困难。 

因此,我们没有理由反对使用const,该加const时就应该加上const,这样使成员函数除了非const的对象之外,const对象也能够调用它。


转载 :http://www.chinaunix.net/old_jh/23/300602.html


(二)

常量函数是C++对常量的一个扩展,它很好的确保了C++中类的封装性。在C++中,为了防止类的数据成员被非法访问,将类的成员函数分成了两类,一类是常量成员函数(也被称为观察着);另一类是非常量成员函数(也被成为变异者)。在一个函数的签名后面加上关键字const后该函数就成了常量函数。对于常量函数,最关键的不同是编译器不允许其修改类的数据成员。例如:

    class Test

    {

    public:

    void func() const;

    private:

    int intValue;

    };

    void Test::func() const

    {

    intValue = 100;

    }

    上面的代码中,常量函数func函数内试图去改变数据成员intValue的值,因此将在编译的时候引发异常

    当然,对于非常量的成员函数,我们可以根据需要读取或修改数据成员的值。但是,这要依赖调用函数的对象是否是常量。通常,如果我们把一个类定义为常量,我们的本意是希望他的状态(数据成员)不会被改变。那么,如果一个常量的对象调用它的非常量函数会产生什么后果呢?看下面的代码:

    class Fred{

    public:

    void inspect() const;

    void mutate();

    };

    void UserCode(Fred& changeable, const Fred& unChangeable)

    {

    changeable.inspect(); // 正确,非常量对象可以调用常量函数

    changeable.mutate(); // 正确,非常量对象也允许修改调用非常量成员函数修改数据成员。

    unChangeable.inspect(); // 正确,常量对象只能调用常理函数。因为不希望修改对象状态。

    unChangeable.mutate(); // 错误!常量对象的状态不能被修改,而非常量函数存在修改对象状态的可能

    }

    从上面的代码可以看出,由于常量对象的状态不允许被修改,因此,通过常量对象调用非常量函数时将会产生语法错误。实际上,我们知道每个成员函数都有一个隐含的指向对象本身的this指针。而常量函数则包含一个this的常量指针。如下:

    void inspect(const Fred* this) const;

    void mutate(Fred* this);

     也就是说对于常量函数,我们不能通过this指针去修改对象对应的内存块。但是,在上面我们已经知道,这仅仅是编译器的限制,我们仍然可以绕过编译器的限制,去改变对象的状态。看下面的代码:

    class Fred{

    public:

    void inspect() const;
    private:

    int intValue;

    };

    void Fred::inspect() const

    {

    cout << "At the beginning. intValue = "<< intValue << endl;

    // 这里,我们根据this指针重新定义了一个指向同一块内存地址的指针

    // 通过这个新定义的指针,我们仍然可以修改对象的状态。

    Fred* pFred = (Fred*)this;

    pFred->intValue = 50;

    cout << "Fred::inspect() called. intValue = "<< intValue << endl;

    }

    int main()

    {

    Fred fred;

    fred.inspect();

    return 0;

    }

    上面的代码说明,只要我们愿意,我们还是可以通过常量函数修改对象的状态。同理,对于常量对象,我们也可以构造另外一个指向同一块内存的指针去修改它的状态。这里就不作过多描述了。

    另外,也有这样的情况,虽然我们可以绕过编译器的错误去修改类的数据成员。但是C++也允许我们在数据成员的定义前面加上mutable以允许该成员可以在常量函数中被修改。例如:

    class Fred{

    public:

    void inspect() const;

    private:

    mutable int intValue;

    };

    void Fred::inspect() const

    {

    intValue = 100;

    }

    但是,并不是所有的编译器都支持mutable关键字。这个时候我们上面的歪门邪道就有用了。

    关于常量函数,还有一个问题是重载。

    #include <iostream>

    #include <string>

    using namespace std;

    class Fred{

    public:

    void func() const;

    void func();

    };

    void Fred::func() const

    {

    cout << "const function is called."<< endl;

    }

    void Fred::func()

    {

    cout << "non-const function is called."<< endl;

    }

    void UserCode(Fred& fred, const Fred& cFred)

    {

    cout << "fred is non-const object, and the result of fred.func() is:" << endl;

    fred.func();

    cout << "cFred is const object, and the result of cFred.func() is:" << endl;

    cFred.func();

    }

    int main()

    {

    Fred fred;

    UserCode(fred, fred);

    return 0;

    }

    输出结果为:

    fred is non-const object, and the result of fred.func() is:

    non-const function is called.

    cFred is const object, and the result of cFred.func() is:

    const function is called.

    从上面的输出结果,我们可以看出。当存在同名同参数和返回值的常量函数和非常量函数时,具体调用哪个函数是根据调用对象是常量对像还是非常量对象来决定的。常量对象调用常量成员;非常量对象调用非常量的成员。

    总之,我们需要明白常量函数是为了最大程度的保证对象的安全。通过使用常量函数,我们可以只允许必要的操作去改变对象的状态,从而防止误操作对对象状态的破坏。但是,就像上面看见的一样,这样的保护其实是有限的。关键还是在于我们开发人员要严格的遵守使用规则。另外需要注意的是常量对象不允许调用非常量的函数。这样的规定虽然很武断,但如果我们都根据原则去编写或使用类的话这样的规定也就完全可以理解了。

转载:http://www.cnblogs.com/yc_sunniwell/archive/2010/07/14/1777416.html





原创粉丝点击