尽可能使用const修饰符

简介: 尽可能使用const修饰符

1.const修饰符概述


const修饰符允许你指定一个语义上的约束(即指定了一个只能读不能修改的对象),而编译器会强制实施这项约束。当你在对象前加入const进行修饰时,你就告诉了编译器或其他程序员这个对象的值只能保持不变。使用const关键字能够大大提高我们程序的健壮性。


const应用场合有很多。可以用它在类的外部修饰global或namespace作用域中的常量;可以用来修饰文件、函数、区块作用域中被声明为static的对象;也可以用来修饰类的内部的static和non-static成员变量。对于指针,你也可以指出指针自身、指针所指物或者两者都是或都不是const。


2.const修饰符在指针中的应用


常量指针:如果const出现在星号左边,表示指针所指物是常量。有两种等价的写法,见下例。


指针常量:如果const出现在星号右边,表示指针自身是常量。指向常量的常指针:如果const出现在星号两边,表示指针所指物和指针自身都是常量。


1const int *p;  // 常量指针
2int const *p;  // 常量指针
3
4int * const p;  // 指针常量
5
6const int * const p;  // 指向常量的常指针


STL中的迭代器是以指针为根据构建出来的,因此迭代器的作用就像一个T* 指针。声明迭代器为const就像声明指针为const一样(即声明了一个指针常量T* const 指针),它表示这个迭代器不能指向其他的东西,但是它所指的东西的值是可以改变的。如果你希望迭代器所指的东西不可以被修改(即希望STL能模拟出一个常量指针const T* 指针),你需要的是const_iteartor。如下例所示:


1std::vector<int> vec;
 2....
 3
 4const std::vector<int>::iterator iter = vec.begin(); // 定义了一个指针常量iter 
 5*iter = 10;  // 指针所指的东西可以被修改,所以此句正确!
 6++iter;  // 指针自身不能修改,所以此句错误!
 7
 8std::vector<int>::const_iterator cIter = vec.begin(); // 定义了一个常量指针cIter
 9*cIter = 10;  // 指针所指的东西不能被修改,所以此句错误!
10++cIter;   // 指针自身可以修改,所以此句正确!


3.const修饰符在函数中的应用


在一个函数声明内,const关键字可以在函数的返回值、函数的参数、函数自身(前提是这个函数是类的成员函数)中得到应用。


3.1 在函数返回值中的应用


令函数返回一个常量值,往往可以降低因客户误操作而造成的意外,而又不至于放弃安全性和高效性。如下例所示:


1class Rational{
2...
3const Rational operator* (const Rational& lhs, const Rational& rhs);
4};   // 重载*运算符函数返回了一个常对象


为什么上述重载*运算符函数会返回一个常对象呢?原因是如果不这样的做的话,当程序员不小心打错一个字符(即把==错打成赋值运算符),就会变成对两个数值的乘积再做一次赋值情况。如下例所示:


1Rational a, b, c;
2...
3(a * b ) = c;   // 此处应该是(a*b) == c,但是由于单纯的打字错误导致的。


如果a和b都是C++中的内置数据类型,上面的(a * b) = c直接会报错。将重载*运算符函数函数返回一个const,可以防止像(a * b) = c那样没意思的赋值操作,这也是函数返回值加const修饰的原因。


3.2 在函数参数中的应用


对于const修饰的函数参数,它就类似于local const对象一样,你可以在必要的场合使用它。除非你有需要改动的参数或local对象,否则请把它们声明了const。


3.3 const成员函数


const应用于成员函数上的目的是为了使该常成员函数可以作用于const对象上。常成员函数之所以重要的原因:a.它们可以使class接口比较容易被理解。因为可以得知哪个函数可以改动对象内容而哪个函数不可以。b.它们可以使操作const对象成为可能(常对象只能调用常成员函数,而普通对象既可以调用常成员函数,也可以调用非常成员函数)。


1#include <iostream>
 2
 3using namespace std;
 4
 5class square
 6{
 7private:
 8    int w, h;
 9
10public:
11    int getArea() const; // 常成员函数
12    // int getArea();       // 非常成员函数
13    square(int w, int h) : w(w), h(h){};
14};
15
16int square::getArea() const
17{
18    return w * h;
19}
20
21// int square::getArea()
22// {
23//     return w * h;
24// }
25
26int main()
27{
28    //const square a(3,4);  // 常对象
29    //cout << a.getArea() << endl;
30    square b(2, 4);  // 普通对象
31    cout << b.getArea() << endl;
32
33    return 0;
34}


c.常成员函数不能用来更新类的非静态成员变量d.常成员函数不能调用该类中没有用const修饰的成员函数,只能调用常成员函数。因此,下面的程序将会报错。


1#include <iostream>
 2
 3using namespace std;
 4
 5class square
 6{
 7private:
 8    int w, h;
 9
10public:
11    // int getArea() const; // 常成员函数
12    int getArea();       // 非常成员函数
13    square(int w, int h) : w(w), h(h){};
14};
15
16// int square::getArea() const
17// {
18//     return w * h;
19// }
20
21int square::getArea()
22{
23    return w * h;
24}
25
26int main()
27{
28    const square a(3,4);  // 常对象
29    cout << a.getArea() << endl;
30    // square b(2, 4);  // 普通对象
31    // cout << b.getArea() << endl;
32
33    return 0;
34}


e.两个成员函数如果只是常量性的不同(成员函数是否加const修饰),可以被重载。


1#include <iostream>
 2
 3using namespace std;
 4
 5class square{
 6private:
 7    int w, h;
 8public:
 9    int getArea() const; // 常成员函数,它与普通成员函数之间形成了重载关系
10    int getArea();         // 普通成员函数
11    square(int w, int h) : w(w), h(h){};    
12};
13
14int square::getArea() const    
15{
16    return w * h;
17}
18
19int square::getArea()
20{
21    return w * h;
22}
23
24int main()
25{
26    const square a(3,4);
27    cout << a.getArea() << endl;
28    square b(2,4);
29    cout << b.getArea() << endl;
30
31    return 0;
32}


4.bitwise constness与logical constness


bitwise constness认为成员函数只有在不更改对象的任何成员变量(static除外)时,才可以是const。即它不更改对象内的任何一个bit。编译器只需要寻找成员变量的赋值动作即可检查出违反点。因此,常成员函数不可以更改对象内任何非静态成员变量。


然而,许多成员函数虽然不具备const性质却也能通过bitwise测试。例如,一个更改了指针所指物的成员函数虽然不能算是const,但如果只有指针自身隶属于对象,那么此函数也不会引起bitwise编译错误。如下所示:


1#include <iostream>
 2#include <vector>
 3#include <string>
 4#include <cstring>
 5
 6
 7// bitwise-constness
 8class CTextBlock{
 9public:
10    CTextBlock(const char* str){
11        pText = new char[strlen(str) + 1];
12        memcpy(pText, str, strlen(str) + 1);
13    }
14
15    // 常成员函数
16    char& operator[] (std::size_t position) const{
17        return pText[position];
18    }
19
20    ~CTextBlock(){
21        delete pText;
22    }
23private:
24    char* pText;
25};
26
27int main(){
28    const CTextBlock cctb("CurryCoder");
29    char* pt = &cctb[0];  
30    *pt = 'C';  // 更改了指针所指向内容的成员函数,显然它不能算是常成员函数。但是,bitwise-const并不会报错!
31
32    std::cout << *pt << std::endl;
33    return 0;
34}


logical constness认为一个const成员函数可以修改它所处理的对象内的某些bits,但只有在客户端检测不出的情况下才能这样做。


下面的程序中,对常成员函数length()的修改显然不是bitwise const,因为textLength和lengthIsVaild都可能被修改。这两个数据被修改对const CTextBlock对象而言虽然可以接受,但编译器不同意。解决方法:利用C++的一个与与const相关的mutable。mutable释放掉非静态成员变量的bitwise constness约束。如下例所示:


1#include <iostream>
 2#include <vector>
 3#include <string>
 4#include <cstring>
 5
 6// logical-constness
 7class CTextBlock
 8{
 9public:
10    CTextBlock(const char *str)
11    {
12        pText = new char[strlen(str) + 1];
13        memcpy(pText, str, strlen(str) + 1);
14    }
15
16    // 常成员函数
17    char &operator[](std::size_t position) const
18    {
19        return pText[position];
20    }
21
22    std::size_t length() const;
23
24    ~CTextBlock()
25    {
26        delete pText;
27    }
28
29private:
30    char *pText;
31    mutable std::size_t textLength;
32    mutable bool lengthIsVaild;
33};
34
35std::size_t CTextBlock::length() const
36{
37    if (!lengthIsVaild)
38    {
39        textLength = strlen(pText);
40        lengthIsVaild = true;
41    }
42    return textLength;
43}
44
45int main()
46{
47    const CTextBlock cctb("CurryCoder");
48    char *pt = &cctb[0];
49    std::cout << "长度是: " << cctb.length() << std::endl;
50    return 0;
51}


5.在const和non-const成员函数中避免代码重复


当const成员函数与non-const成员函数中存在大量重复性的代码时,可以用其中的non-const成员函数调用const常成员函数。注意:使用const成员函数调用non-const成员函数是不可以的!如下例所示:


1#include <iostream>
 2#include <vector>
 3#include <string>
 4
 5class TextBlock
 6{
 7public:
 8    TextBlock(const std::string &s) : text(s) {}
 9    // const成员函数
10    const char &operator[](std::size_t position) const
11    {
12        // 边界检验
13        // 志记数据访问
14        // 检验数据完整性
15        // ....
16        return text[position];
17    }
18    // non-const成员函数
19    char &operator[](std::size_t position)
20    {
21        // 边界检验
22        // 志记数据访问
23        // 检验数据完整性
24        // ....
25        return text[position];
26    }
27
28private:
29    std::string text;
30};
31
32
33/* 
34const成员函数与non-const成员函数中存在大量重复性的代码,因此使用在non-const成员函数中调用const成员函数即可。注意:反过来则不行。
35当中涉及两个转型动作:
36    第一次用来为*this添加const(强迫执行了一次安全转型static_cast),第二次是从const operator[]的返回值中移除const(只能用const_cast完成,没有其他选择)
37 */
38
39class TextBlock
40{
41public:
42    TextBlock(const std::string &s) : text(s) {}
43    // const成员函数
44    const char &operator[](std::size_t position) const
45    {
46        // 边界检验
47        // 志记数据访问
48        // 检验数据完整性
49        // ....
50        return text[position];
51    }
52    // non-const成员函数
53    char &operator[](std::size_t position)
54    {
55        // 边界检验
56        // 志记数据访问
57        // 检验数据完整性
58        // ....
59        return const_cast<char&> (static_cast<const TextBlock&> (*this)[position]);
60    }
61
62private:
63    std::string text;
64};


6.总结


(1).将某些东西声明为const可以帮助编译器检测出错误用法。const可以应用在任何作用域内的对象、函数参数、函数返回类型、成员函数本体上。

(2).编译器强制实施bitwise constness,但你编写程序的时候应该使用概念上的常量性(即logical constness)。(3).当const和non-const成员函数有着实质等价的实现时,令non-const版本调用const版本可避免代码重复,但反过来调用则不可以。


7.参考资料



[1].https://www.cnblogs.com/meituanqishoualin/p/11607564.html[2].https://www.cnblogs.com/guyan/archive/2012/03/16/2400705.html

相关文章
|
7月前
|
数据安全/隐私保护 C++
C++ const 限定符的全面介绍(二)
C++ const 限定符的全面介绍(二)
79 0
|
7月前
|
C++
C++ const 限定符的全面介绍(一)
C++ const 限定符的全面介绍(一)
72 0
|
5月前
const修饰指针
const修饰指针
31 0
|
6月前
|
编译器 C++
类和对象(6):const成员,&/const &重载
类和对象(6):const成员,&/const &重载
|
7月前
|
编译器 C++
|
7月前
|
安全 编译器 Linux
【C++中的const函数】何时与如何正确声明使用C++ const函数(二)
【C++中的const函数】何时与如何正确声明使用C++ const函数
72 0
|
7月前
|
安全 算法 编译器
【C++中的const函数】何时与如何正确声明使用C++ const函数(三)
【C++中的const函数】何时与如何正确声明使用C++ const函数
65 0
|
7月前
|
安全 编译器 C++
【C++中的const函数】何时与如何正确声明使用C++ const函数(一)
【C++中的const函数】何时与如何正确声明使用C++ const函数
124 0
|
7月前
|
编译器 C++
在 C++ 中const 成员函数的运用
在 C++ 中const 成员函数的运用
|
7月前
|
C++
C++类中的const使用
C++类中的const使用