Effective C++筆記之三:儘可能使用const
一.const作用於迭代器
STL選代器系以指標為根據塑模出來,所以迭代器的作用就像個T*指標。宣告選代器為const就像宣告指標為const一樣(即宣告一個T* const 指標) ,表示這個迭代器不得指向不同的東西,但它所指的東西的值是可以改動的。如果你希望迭代器所指的東西不可被改動(即希望STL模擬一個const T* 指標) ,你需要
的是const_iterator:
std::vector<int> vec;
.....
const std::vector<int>::iterator iter = vec.begin( );
*iter = 10;// OK
++iter;// error
std::vector<int>::const_iterator cIter = vec.begin( );
*cIter = 10;// error
++cIter;// ok
二.const作用於自定義型別的物件
在定義物件時指定物件為常物件。常物件中的資料成員為常變數,例如:
#include <iostream>
using namespace std;
class Time
{
public:
void printf() const
{
//h = 10;//error C3490: 由於正在通過常量物件訪問“h”,因此無法對其進行修改
m = 10;// ok
cout << "Hour:" << h << "Minute:" << m << "Second:" << s << endl;
}
void show()// 不會導致編譯錯誤
{
h = 10;
}
private:
int h;
mutable int m;
int s;
};
int main()
{
const Time t;
t.printf();
system("pause");
return 0;
}
常物件t中的資料成員雖然未顯示定義為const資料成員,但它們都是常變數,無法修改它們的值。
常成員函式printf可以訪問常物件中的資料成員,但是不允許修改常物件中的資料成員,除非該資料成員被宣告為mutable。
普通成員函式show雖然不會導致編譯錯誤,但是無法被常物件呼叫,因為常物件、指向常物件的指標或引用只能用於呼叫其const型成員函式,而不能呼叫其非const型的成員函式。
三.const作用於函式
1.令函式返回一個常量值
這樣做往往可以降低因客戶錯誤而造成的意外,而又不至於放棄安全性和高效性,例如,考慮有理數(rational numbers)的operator*宣告:
class Rational{ ... };
const Rational operator* (const Rational& lhs, const Rational& rhs);
這個宣告能很好的杜絕由於筆誤而導致的如下操作:
Rational a,b,c;
if(a * b = c)
{
......
}
如果a和b都是內建型別,這樣的程式碼直截了當就是不合法。而一個"良好的使用者自定義型別"的特徵是它們避免無端地與內建型別不相容。
2.const引數
至於const引數,沒有什麼特別新穎的觀念,它們不過就像local const物件一樣,你應該在必要使用它們的時候使用它們。除非你有需要改動引數或local物件,否則請將它們宣告為const。只不過多打6個字元,卻可以省下惱人的錯誤,像是"想要鍵入'=='卻意外來鍵成'=的錯誤,一如稍早所述。
四.const資料成員
常資料成員的值是不能改變的,且必須初始化,因為常資料成員是不能被賦值的,關於初始化,詳見:Effective C++筆記之一:宣告、定義、初始化與賦值
#include <iostream>
using namespace std;
class Time
{
public:
void printf() const
{
//h = 10;//error C3490: 由於正在通過常量物件訪問“h”,因此無法對其進行修改
m = 10;
cout << "Hour:" << h << "Minute:" << m << "Second:" << s << endl;
}
//void show()// C2166: 左值指定const物件
//{
// h = 10;
//}
private:
//const int h;// 報錯,沒有初始化
const int h = 10;// 初始化
mutable int m;
const int s = 10;// 初始化
};
int main()
{
const Time t;
t.printf();
system("pause");
return 0;
}
對比“二.const作用於自定義型別的物件”小節,可看出const物件中const資料成員和非const資料成員的區別。
五.const成員函式
常成員函式只能引用本類中的資料成員(const和非const),而不能修改他們。
const是函式型別的一部分,在宣告函式和定義函式時都要有const關鍵字,在呼叫時不必加const。const員函式可以引用const資料成員,也可以引用非const資料成員。const資料成員可以被const成員函式引用,也可以被非const資料成員函式引用,但是不能被修改。
需要注意的是:
1.不要誤認為常物件中的成員函式都是常成員函式。常物件只能保證所有的資料成員的值不被修改。如果在物件中的成員函式為加const宣告,編譯系統把它作為非const成員函式處理。
2.兩個成員函式如果只是常量性(constness)不同,可以被過載。舉個例子
#include <iostream>
using namespace std;
class TextBlock
{
public:
TextBlock::TextBlock(string str) :test(str)
{
}
const char & operator[](size_t position) const
{
return test[position];
}
char & operator[](size_t position)
{
return test[position];
}
private:
string test;
};
int main()
{
TextBlock tb("Hello");
cout << tb[0] << endl;
const TextBlock ctb("World");
cout << ctb[0] << endl;
system("pause");
return 0;
}
3.常成員函式不能呼叫另一個非const成員函式。但是非const成員函式可以呼叫const成員函式。舉個例子:
在上述例子中,過載的兩個操作符函式體內的程式碼量小,感覺不到有什麼不妥。但是如果程式碼量大的話,可以讓非const operator[]呼叫其const兄弟來避免程式碼重複。
#include <iostream>
using namespace std;
class TextBlock
{
public:
TextBlock::TextBlock(string str) :test(str)
{
}
const char & operator[](size_t position) const
{
return test[position];
}
char & operator[](size_t position) // 先呼叫const版本的操作符,然後去掉返回結果的const屬性
{
return
const_cast<char&>(
static_cast<const TextBlock&>(*this)[position]);
}
private:
string test;
};
int main()
{
TextBlock tb("Hello");
cout << tb[0] << endl;
const TextBlock ctb("World");
cout << ctb[0] << endl;
system("pause");
return 0;
}
六.指向物件的常指標
將指向物件的指標變數宣告為const型並將之初始化,這樣指標值始終保持為其初始值,不能改變,即其指向始終不變。如:
int a = 1 , b;
int * const ptr1 = &a;
ptr1 = &b;// error C3892: “ptr1”: 不能給常量賦值
指向物件的常指標的值不能改變,即始終指向同一個物件,但可以改變其所指向物件(如b)中資料成員的值。
七.指向常物件的指標
1.如果一個變數已經被宣告為常變數,只能用指向常變數的指標指向它,而不能用一般的(指向那個非const型變數的)指標變數去指向它。
2.指向常變數的指標除了可以指向常變數,還可以指向非const變數。此時不能通過指標變數改變該變數的值。
3.指向常物件的指標常作為函式引數。
八.物件的常引用
在C++面向物件程式設計中,經常用常指標和常引用作函式引數。這樣既能保證資料安全,使資料不能被隨意修改,在呼叫函式時又不必建立實參的拷貝。
---------------------
作者:燦哥哥
來源:CSDN
原文:https://blog.csdn.net/caoshangpa/article/details/79437030?utm_source=copy
版權宣告:本文為博主原創文章,轉載請附上博文連結!