C++ const常量成员函数

发布时间 2023-04-03 20:25:57作者: Bdathe

C++ const常量成员函数

const的的用法太多了,常量成员函数就是曾经让我迷惑的用法之一,即在成员函数的参数列表之后加上const。

this 指针

在说常量成员函数之前,必须得详细知道this指针,以前我们知道不论C++还是java中,this都可以表示对象本身,事实如此,而在C++中更准确的定义是:

this是一个额外的隐式参数,用来访问当前对象,而它的本质是一个常量指针

常量指针(const pointer),区别于指向常量的指针(const to pointer),前者表示指针的值不能被修改(地址不变),后者表示指针所绑定的对象的值不能被修改,由于this就指向当前对象,我们不能用this在去绑定其他对象,因此他就是一个常量指针。
假设现在有一个类class People则this的类型是:People *const

const成员函数

上面提到,this指针是一个非常量版本的常量指针,那么当我们创建常量对象的时候,不能把this绑定到一个常量对象上——即不能使用一个常量对象调用普通函数。
代码:

class people{
public:
    people(){}
    people(int x) :score(x){}
    ~people(){}
    int getScore(){ return score; }
private:
    int score;
};

int main() {
    const people p1(22);
    cout << p1.getScore() << endl; // error
    system("pause");
    return 0;
}

这段代码编译器直接报错:

对象包含于成员函数不兼容类型的限定符

这也就说明了普通指针(包括非常量指针和常量指针)无法绑定一个常量对象,这里指的是this指针无法绑定常量对象p1,所以p1无法调用普通的成员函数

那么现在问题又来了,怎么使隐式调用的this指针变成指向常量的常量指针?
答案就是在成员函数的参数列表后面加入关键字const,例如上述示例中:

int getScore() const { return score; }

这样我们就可以使用常量对象调用getScore()方法了

class people{
public:
    //...
    int getScore() const{ return score; }
    //...
};
int main()
{
    const people p1(22);
    cout << p1.getScore() << endl; // ok
    system("pause");
    return 0;
}

这样就叫做常量成员函数(const member function),我们可以使用非常量对象调用常量成员函数,这样做是合法的,因为我们可以使用指向常量的指针来绑定一个非常量对象,例如,下述语句合法:

double pi = 3.14;
const double * ptr =  &pi;

关于const成员函数的补充

对于一个成员函数来说,它有一个隐藏的参数,来表示对象的地址,例如:

class Foo
{
public:
    void hello(int a)
    {
        cout << "hello"+a<<endl;
    }
};
//...
int main()
{
    Foo foo;
    int a=2;
    foo.hello(a);
    //等价于
    foo.hello(&foo,a);
}

从上述代码看出,这里相当于有一个该对象地址的隐式参数传递,也就是说,从函数声明的角度,这里应该是这个样子:

class Foo
{
public:
    void hello(Foo* this ,int a)
    {
        cout << "hello"+a<<endl;
    }
};

当我们给成员函数的声明后面加上const以后,例如:

void hello(int a) const
{
    cout << "hello"+a<<endl;
}

这里就可以理解为有一个const的指针作为参数,如:

void hello(const Foo * this,int a) const
{
    cout << "hello"+a<<endl;
}

因为this是const的类型的,当然也就防止了成员函数对类中成员进行修改

直到上面的内容,我们可以这样理解:

class A
{
//...
public:
    // void func1(const A * this); 
    void func1() const;

    // void func2(A * this); 
    void func2();
}

我们在使用的时候可能有如下组合:

int main()
{
//...
    A a1;
    const A a2;
    a1.func1();  //等价于func1(&a1); //ok

    a1.func2();  //等价于func2(&a1); //ok

    a2.func1();  //等价于func1(&a2); //ok
    
    a2.func2();  //等价于func2(&a2); //error
}

类比解释:

情况1,用const的指针绑定普通对象,可行,类似于:

int a = 3;
const int *pa = &a;

情况2,用普通指针绑定普通对象,可行,类似于:

int a = 3;
int *pa = &a;

情况3,用const指针绑定常量对象,可行,类似于:

const int a = 3;
const int *pa = &a;

情况4,用普通指针绑定const对象,不可行,类似于:

const int a = 3;
int *pa= &a;  // error