c++ this指针与const对象

  在 c++的const对象一文中已经介绍了const对象的属性,本文尝试从this指针的角度,再来分析const对象。

1. 问题引入

  同样的示例,定义一个类,并将其实例化为const对象:

//1_const_obj.cpp
class Obj
{
private:
    int pri_dat;

public:
    int get_pri_dat()
    {
        return pri_dat;
    }   
};

int main(void)
{
    const Obj obj;

    obj.get_pri_dat();

    return 0;
}

  编译报错:

$ g++ 1_const_obj.cpp
1_const_obj.cpp: In function ‘int main()’:
1_const_obj.cpp:17:12: error: uninitialized const ‘obj’
1_const_obj.cpp:19:18: error: passing ‘const Obj’ as ‘this’ argument of ‘int Obj::get_pri_dat()’ discards qualifiers

  大概是说obj对象没有初始化,函数get_pri_dat()不能被调用。

  前文中指出,obj是常量(const)对象,不允许被修改,所以不能通过obj对象去调用非const的成员函数,因为编译器担心非const函数会做出修改const对象中的成员的举动。好像也是这么一回事。当然这是从表面解释上述报错的原因,再往深层次分析,就得涉及顶层const、底层const和this指针。

2. 顶层const和底层const

  看下面定义:

int a1 = 5;
const int a2 = a1;      //顶层const

char a3 = 5;
char * const p1 = &a3;  //顶层const
const char *p2 = &a3;   //底层const
const char * const p3 = &5; //底层const

const float a4 = 1.34;
float * const p4 = &a4; //顶层const,报错
const float *p5 = &a4;  //底层const,正确

  在c++ primer 5e中,顶层const被称为常量指针,底层const被称为指向常量的指针,而没有提及指针常量。而在网上大多数资料说的:
  (1) 顶层const指的是const修饰的是指针指向的地址,即指针指向的地址值不可被改变,称为指针常量。
  (2) 底层const指的是const修饰的是指针指向的地址上的内容,即该内容为不可变,称为常量指针(指向常量的指针)。

  在这里我们不纠结常量指针和指针常量、指向常量的指针这3个概念,而直接以顶层/底层const替代。

  常量对象只能被底层const指针指向,反过来,底层const指针却既可以指向常量对象,也可以指向非常量对象,指向非常量对象时,不可以通过该指针修改所指向的非常量对象,但是不妨碍其他修改非常量对象的方式:

char a3 = 5;
const char *p1 = &a3;   //底层const
char* const p2 = &a3;   //顶层const

a3 = 6;         //正确
*p1 = 9;        //错误
*p2 = 5;        //正确
p2 = NULL//错误

  c++中的引用就是通过顶层const指针实现的(上面的p2指针),而c++类中的this指针则是通过顶层const指针实现的,即this中保存的地址不允许被改变。this指针是什么?看下面解释。

3. this指针

  代码语句:

obj.get_pri_dat();

  我们使用点操作符来调用obj对象的get_pri_dat()成员函数,实际上这是在替某个对象调用它。当我们调用一个成员函数时,用请求该函数的对象的地址初始化this。如上调用语句可以等价的认为:

Obj::get_pri_dat(&obj);

  成员函数get_pri_dat()的原型近似为:

Obj::get_pri_dat(Obj* const this);

  在成员函数内部,我们可以直接使用调用该函数对象的成员,无序通过成员访问运算符来做到这点,因为this指针所指的正是这个对象。任何对类成员的直接访问都被看做this指针的隐式引用,get_pri_dat()函数的实现等价于:

return this->pri_dat;

4. const成员函数

  普通成员函数,如上的get_pri_dat(),其this指针类型是指向所在类类型的顶层const指针,虽然它是隐式存在的,但是仍旧遵循指针的赋值规则,即this指针不能指向一个const对象。在前面我们将obj定义为const对象,显然相悖,这也是我们不能在一个常量对象上调用普通成员函数的原因。解决办法是将this指针声明为底层const指针,形似为:

Obj::get_pri_dat(const Obj* const this);

  然而this指针是隐式的,不会出现在参数列表中,所以c++的做法是允许将const关键字放在成员函数的参数列表之后,该const表明所在成员函数的this指针是一个底层const指针:

class Obj
{
//...
public:
    int get_pri_dat() const
    {
        //...
    }   
};

  this指针为底层const的函数,也称为常量成员函数。常量成员函数只能访问,不能修改所在对象的内容。

5. const对象和缺省的构造函数

  将get_pri_dat()成员函数声明为const属性之后,编译:

$ g++ 1_const_obj.cpp 
1_const_obj.cpp: In functionint main()’:
1_const_obj.cpp:22:12: error: uninitialized const ‘obj’

  还剩下报错obj对象没有初始化,在类中自定义构造函数后编译就通过了。这个具体原因在前文已经详细说明,不赘述。

你可能感兴趣的:(C/C++编程,c/c++语言,this指针,const对象,底层const,顶层const)