1、const 修飾普通變量
C/C++中常量用於記錄程序中不可更改的數據,在數據類型前加const,就得到了一個不可更改的常量。
- 常量聲明:
const用於定義常量
const int MAX = 100;//MAX的值在程序運行期間無法被修改,所以必須在定義時初始化。
2、const與指針的組合
如果被const修飾的數據類型是指針,要關心下const的位置,const在*左邊,表示指向的數據是常量;const在*右邊,表示指針本身是常量。口訣:左定值,右定向
- 常量指針:這種指針不能修改它所指向的內存中的值,但可以改變指針指向其他地址。可以用於函數參數,避免函數修改傳入的數據。
int a = 10;
int b = 20;
const int *ptr = &a; // 非常量指針轉換為指向常量的指針(轉換規則)
int const *ptrr; // 等價上面
// *ptr = 20; // 錯誤,不能修改x的值
ptr = &b; // ok
// int* p = ptr; // 錯誤,指向常量的指針不能隱式轉換為非常量指針,需要const_cast
int* pp = const_cast<int*>(ptr); // ok,但使用需謹慎,
// const_cast只能去除指針或引用的const屬性,不能去除對象本身的const屬性
- 指針常量:指針本身是常量,必須初始化,不能改變指針的指向,但指向的值可變。用於需要固定指向某個對象,但可能需要修改該對象內容的情況。
int a = 10;
int b = 20;
int* const ptr = &a; // ptr是一個常指針,不能改變ptr指向的地址
*ptr = 50; // ok
// ptr = &b; // 錯誤,ptr不能指向其他變量
- 完全常量指針:指向常量的常量指針,指針本身是常量,指針指向的數據也是常量。指針和值都不可變,是最嚴格的const指針。可以用於配置參數、固定常量等絕對不能修改的情況。
int x = 10;
const int* const ptr = &x;
//ptr = &y; // 錯
//*ptr = 20; // 錯
- 多層指針與const:指向上述指針的指針
const int** pp1;
int* const* pp2;
int const** pp3;
const int* const* pp4;
3、const與引用
- 常量引用:引用一個常量,不能通過引用修改數據。可以用於函數參數和返回值,避免拷貝,同時保證不修改原數據,且同時支持常量和變量參數。
int a = 10;
const int& ref = a;
// ref = 20; //錯
double d = 3.14;
//int& rj = d; // 錯誤
const int& ri = d; //ok,創建臨時int變量3,綁定ri到臨時變量(轉換規則)
- 綁定字面量:只有常量引用可以綁定字面量
const int& a = 10; // 可以用右值(字面量)或同類型的對象初始化(初始化規則)
const double& b = 5; // 可以用不同但相關的對象初始化,會創建臨時對象
const int& c = 5.5; // double隱式轉換int會丟失精度
4、const在類中的應用
- 常量成員變量:const成員變量必須在構造函數的初始化列表中初始化,不能在構造函數體內賦值
class Myclass{
private:
const int m_v;
public:
Myclass(int value):m_v(value){}// ok
// 錯誤
//Myclass(int value){
// m_v = value;
//}
};
- const成員函數:1、const成員函數不會修改類的非mutable成員變量,這是一種接口契約。2、const成員函數在編譯時會將'this'指針轉換為‘const T*'類型,因此不能修改成員變量。3、C++允許const和非const版本的成員函數重載,編譯器會根據對象是否為const來選擇調用哪個版本。4、const對象只能調用const成員函數,不能調用非const成員函數,非const對象可以調用const和非const成員函數。5、const對象的非mutable成員變量被視為const,可以通過const成員函數獲取成員變量的值。
// 對於非const成員函數,this的類型是 T*
// 對於const成員函數,this的類型是 const T*
class StringBuffer{
private:
int m_count;
string m_buffer;
mutable int m_accessCount;
public:
const char& operator[](size_t index) const{
//m_count++; // 錯
m_accessCount++; // ok
cout << "const char&" << endl;
return m_buffer[index];
}
char& operator[](size_t index){
cout << "char&" << endl;
return m_buffer[index];
}
int getCount() {return m_count;}
int constGetCount() const {return m_count;}
};
int main(){
StringBuffer buffer;
const StringBuffer constBuffer;
buffer[0] = 'A'; // char& 調用非const版本
//constBuffer[0] = 'B'; // 錯誤,const版本返回const引用,不能修改
char c1 = buffer[0]; // char& 調用非const版本
char c2 = constBuffer[0];// const char& 調用const版本
int count = constBuffer.constGetCount(); // ok
// count = constBuffer.getCount(); // 錯
count = buffer.constGetCount(); //ok
count = buffer.getCount(); //ok
}
類的設計原則:
對於不修改成員變量的函數,聲明為const,提供const和非const重載版本,以支持不同使用場景。合理使用const來約束接口,提高代碼的安全性。
實現高效的接口:
返回成員變量的const引用,避免拷貝,允許只讀訪問,同時保護內部數據。
5、const在實際開發中的最佳實踐詳解
- 優先使用const的原則:儘早const,儘可能const,對於不需要修改的數據,從設計初期就聲明為const
const double PI = 3.1415926535;
const std::string COMPANY_NAME = "TechCorp";
- 函數參數的最佳實踐:對於內置類型:對於簡單類型(int,double等),直接傳值即可。對於指針,根據是否需要修改指針指向的數據決定是否使用const;對於自定義類型和容器
void processValue(int value){}
void printValue(const int* valuePtr){}
本文章為轉載內容,我們尊重原作者對文章享有的著作權。如有內容錯誤或侵權問題,歡迎原作者聯繫我們進行內容更正或刪除文章。