大学IT网 - 最懂大学生的IT学习网站! QQ资料交流群:367606806
当前位置:大学IT网 > C++技巧 > C++的那些事:const用法面面观

C++的那些事:const用法面面观(2)

关键词:c++const  阅读(983) 赞(20)

[摘要]本文是对C++的那些事:const用法面面观的讲解,与大家分享。

六、const与类

其实类定义的对象,与普通的变量是一样的,用const修饰时,说明这个类是一个常量类对象,这个对象有下面2个特点:

1)不能改变其成员变量(非mutalbe成员)

2)不能调用其非const成员函数

 class AClass{
     public:
         int m_var;
         mutable int m_mutable_var;
         void setVar(int var){ m_var = var; }
         void printVar(){ cout << m_var; }
         void printVar_const()const { cout << m_var; }
     };
 
     const AClass ac;
     ac.m_var = 20; // Error:ac是一个const类,不能修改成员变量
     ac.m_mutable_var = 42; // ok 可以修改mutable修饰的变量
     ac.setVar(20); // Error: ac不能调用非const成员函数,而且这个成员函数还修改了成员变量的值
     ac.printVar();// Error:ac不能调用非const成员函数
     ac.printVar_const(); // ok

七、const与类的成员

1,const成员变量

const成员变量指的是类中的成员变量为只读,不能够被修改(包括在类外部和类内部)。

1)const 成员变量必须在类的构造函数初始化表达式中被初始化,即使在构造函数体内也不可以。

2)静态 const 成员变量需要在类外部单独定义并初始化(可定义在头文件)

 class constTestClass
 {
 public:
     const int var;
     static const int sci;
 public:
     constTestClass() :var(42){} // const成员变量必须在类的构造函数初始化列表中初始化
 };
 const int constTestClass::sci = 42; // static const成员变量需要在类外单独进行定义和初始化

类对象的实例化过程可以理解为包含以下步骤:首先,开辟整个类对象的内存空间。之后,根据类成员情况,分配各个成员变量的内存空间,并通过构造函数的初始化列表进行初始化。最后,执行构造函数中的代码。由于 const 成员变量必须在定义(分配内存空间)时,就进行初始化。所以需要在够在函数的初始化列表中初始化。const成员在初始化之后,其值就不允许改变了,即便在构造内部也是不允许的。

静态成员变量并不属于某个类对象,而是整个类共有的。静态成员变量可以不依附于某个实例化后的类对象进行访问。那么,静态成员变量的值,应该在任何实例化操作之前,就能够进行改变(否则,只有实例化至少一个对象,才能访问静态成员)。所以,静态成员变量不能够由构造函数进行内存分配,而应该在类外部单独定义,在实例化任何对象之前,就开辟好空间。又由于 const 成员变量 必须初始化,所以静态成员变量必须在定义的时候就初始化。

2,const成员函数

const成员函数指的是,此函数不应该修改任何成员变量。

1)传给const成员函数的this指针,是指向 const 对象 的 const 指针。

2)const成员函数,不能够修改任何成员变量,除非成员变量被 mutable 修饰符修饰。

 class constTestClass
 {
 public:
     int var;
     const int ci;
     mutable  int mci;
 public:
     void setVar(int i);
     void setMci(int i)const;
 };
 void constTestClass::setVar(int i)
 {
     var = i; // ok
     mci = i; // ok
     ci = i;  // Error:ci是一个const对象不能修改
 }
 void constTestClass::setMci(int i)const
 {
     var = i;    // ok
     mci = i;    // ok  mutable成员变量可以被const成员函数修改
     ci = i;    // Error
 }

在成员函数调用的过程中,都有一个this 指针被当做参数隐性地传递给成员函数(可能通过栈,也可能通过CPU寄存器)。这个this指针,指向调用这个函数的对象(这样,成员函数才能找到成员变量的地址,从而对其进行操作)。这个this指针,是个 const指针,不能修改其指向(你不希望这个对象的函数,修改了那个对象的成员变量,对吧?)。

传递给const成员函数的this指针,指向一个const对象。也就是说,在const成员函数内部,这个this指针是一个指向const对象的const指针。

mutable 修饰符使得const函数的行为有了一些灵活性。相当于提醒编译器,这个成员变量比较特殊,就不要进行任何只读检查了。

为什么 const 对象只能够调用const成员函数呢?,其实是这样的。由于对象本身通过 const 修饰,那么指向这个对象的指针也就是指向const对象的const指针了。换句话说,指向这个对象的this指针就是指向const对象的const指针。一般成员函数要求的this指针为:指向对象的const指针。所以此处发生了参数不匹配,无法进行调用。而 const 成员函数要求的this指针,恰恰是 指向const对象的const指针。所以依然能够调用。

八、const与函数

将函数的形参用const修饰是希望实参在函数内部不被修改,而一般函数接口可能会遇到以下三种情况:

1,const对象

2,指向const对象的指针

3,绑定const对象的引用

4,返回值是一个const对象

首先,我们看const对象的形参,这种接口用const修饰实际上没有任何意义,因为实参在传递给实参时是传递了一份副本,原实参是不会变化的。

 int main(void)
 {
     int var = 42;
     fun(var);
     cout << var << endl; // print 42
     return 0;
 }
 void fun( int i)
 {
     i = 10;
 }

通过上面代码可以看出,实参如果只能过值进行传递,函数接口不用const修改,也不会令实参的值改变。

而通过指针或引用传递给函数时,函数就可以通过形参来改变实参的值,这里如果需要对实参进行保护,则需要在函数接口声明形参为指向const类型的指针或引用。

 void fun( const int* p)
 {
     *p = 42; // error
     int var = 10;
     p = &var;  // 可以改变p本身的值
 }
 void fun(const int& p)
 {
     p = 42; // error,p是一个指向const对象的引用
 }

有的时候,我们需要函数的返回值是一个const对象,比如我们考虑一个有理数据类,我们给类定义了一个*的重载。

 class Rational{
 // ....
 };
 const Rational operator* (const Rational& lhs, const Rational& rhs);
 Rational a, b, c;
 a*b = c; // Error,因为左端为一个const对象

如果上面代码中重载操作符返回对象不是const类型,则a*b=c这个式子就成立,实际上这与我们的内置类型的算术运算原则违背了,而我们希望我们设计的类的操作意义要像内置内类一样。

参考博文:

[1]:C/C++中const修饰符用法总结

[2]:C++const变量使用技巧总结

«上一页12下一页»


相关评论