前言
c++的const和c++中別的東西結合,誕生出了一些會讓人感嘆還好我不學c++的語法,這篇文章應該還不算完整和齊全,但應該夠了。沒有包括靜態變量,全局變量,函數參數,類,n級指針與const相遇的情況並且不全,c++入墳: const2,滿上面的坑都弄完,這樣就把const關鍵字搞懂。
本文基於c++11及以上版本。
放個鬼東西鎮樓,這東西是可以過編譯的,合法的,c++語法。
class Const{
public:
const size_t* const conSt (size_t const * const cOnst) const {}
};
它表達了,它返回size_t*,並且保證不通過size_t*修改它指向的值,並且保證不指向別的變量。
同時,它叫做Const::conSt,並且它保證不修改成員函數。
同時,它接受一個參數cOnst,對這個參數,它保證不修改它指向的值,並且不修改它的指向。
你以為你永遠看不到這種東西?
這是std::string的成員函數at的參數,整個std::string充斥這種東西。
char& at (size_t pos);
const char& at (size_t pos) const;
精華整理
1. 種類
const
const + array
const + reference
const + pointer 有3種情況
const + function參數(省略,不展開,大概跟普通的一樣情況)
const + function返回值
const + 成員函數
const + 成員變量
2. 組合
const的目標是"防止值被更改",有哪些值可以保護呢?
以變量來說
1.通過變量去改動值
以指針來說
1. 指針重新指向新變量
2.通過指針去更改值
以reference來說
1.通過reference更改值
2.如果reference的目標是一個表達式或跟reference不同類型的變量,則會創建一個臨時變量,這是另一種情況,此時只保護自身,你應該讀不到那個臨時變量?:D
所以,有如下情況
1. 基本
值 int a = 10;
const 值 const int a = 10;
const 陣列 const int a[3] = {};
2. reference
const reference + 值
const reference + const 值
const reference + 不同類型的值或表達式
const reference + 不同類型的const值或表達式
3. pointer
const 指針修改目標 + 值
const 指針修改目標 + const 值
const 指針修改值+ 值
const 指針修改值 + const 值
const 指針修改目標 + const 指針修改值+ 值
const 指針修改目標 + const 指針修改值 + const 值
4. function return
理論上以上的情況都可以放到返回值裡面。
5. const member function
保護class的成員變量不修改
6. const member variable
在class創建時就確定值,直到生命週期結束也不會更改。
註:通過指針去修改變量 和 修改變量的值 是兩件不同的事情,需要不同的地方兩個const去避免,別的地方全部同理。
正文
#include <bits/stdc++.h>
// Source: https://www.cnblogs.com/wintergrass/archive/2011/04/15/2015020.html
// Source: https://blog.csdn.net/zkk9527/article/details/89502524
using namespace std;
#define LOG(var) Log(#var, var, __LINE__, __FUNCTION__)
#ifdef DEBUG
template <typename T>
void Log(const char* varName, T var, int line, const char* functionName) {
std::stringstream ss;
ss << "[" << typeid(var).name() << "] " << varName << " = [" << var << "]";
std::ios_base::fmtflags originalFlags = std::cout.flags();/*FORMAT FLAG*/\
std::cout.width(0);
std::cout << std::setfill(' ') << std::left << "LOG "\
<< std::setw(50) << ss.str()/*TYPE*//*VARIABLE*//*VALUE*/\
<< " At " << std::setw(15) << functionName /*FUNCTION*/\
<< std::dec << ",line " << std::setw(5) << std::setfill('0') << std::right << line /*LINE*/\
/*<< "File: " << __FILE__ */ /*FILE*/\
/*<< ", Thread ID: " << std::this_thread::get_id() << ", "*//*THREAD*/\
<< std::endl; \
std::cout.flags(originalFlags);
}
#else
template <typename T>
void Log(const char* varName, T var, int line, const char* functionName) {}
#endif
void func0(){
// 常數變量
// const int a0; //error: 常量出現必須初始化,右邊可以是表達式
const int a0a=10;// 與下行等價,請用這種
int const a0b=10;
//a0a++;//err
//a0b++;//err
// 常數int陣列
const int a1a[3]={1,2,3};// 與下行等價, 請用這種
int const a1b[3]={1,2,3};
}
void func1(){
// reference
//1. 常數引用值
const int &b0a=10; //與下行等價,請用這種
int const &b0b=10;
//b0a++; // err
//int &const d2=10; // err 未定義的行為
//2. 常數引用常數變量
const int b1a = 10;
//int &b1b = b1a; //err 只有const int&c才能reference到const
const int &b1c = b1a;
//b1c++;//err
//3. 常數引用非常數變數
int b2a =10;
const int &b2b= b2a;
++b2a;
//b2b++; // err
LOG(b2a);//11LOG [i] c1a = [20] At func2 ,line 00144
LOG(b2b); // 11 ,引用的是變數本身,數值跟隨變數改變
const int &b2c= b2a*2;
++b2a;
//c2++; // err
LOG(b2a); // 12
LOG(b2c); // 22 ,引用的是臨時變量(b2a*2),數值不跟隨變量改變,在其他情況也相同,例如下面的int reference引用double
double b3a = 10;
const int &b3b=b3a;//正确,输出a的值是3
++b3a;
//++b3b; // err
LOG(b3a); // 11
LOG(b3b); // 10
/*
總結:
const reference不保護被引用的變量,但保護本身,在引用的東西是相同類型且不是表達式時,跟普通引用一樣,值跟變量移動。若是,則編譯器為其創見臨時變量,值不跟變量移動。
*/
}
void func2(){
// pointer
const int a =6;
// int * aptr = &a; // err 不能把const int* 轉換為 int*
// (*aptr)++;
// 2. int, *, const組合形成三種情況去指向變量
int c0a = 10;
int c0ab = 5;
// 變量const
const int* c0b = &c0a;
c0b = &c0ab;
++c0a;
//(*c0b) += 1; //err
LOG(c0a); // 11
LOG(*c0b); // 5
int const *c0c = &c0a;; // 與上個例子等價,請用上面那種
// 指針const
int* const c0d = &c0a;
// c0d = &c0ab; // err
++c0a;
(*c0d) += 1;
LOG(c0a); // 13
LOG(*c0d); // 13
// 變量和指針都const
const int* const c0e = &c0a;
// c0e = &c0ab; // err
++c0a;
//(*c0e) += 1; // err
LOG(c0a); // 14
LOG(*c0e); // 14
int const * const c0f = &c0a; //與上個例子等價,請用上面那種
//3. 指向const變量
const int c1a = 20;
const int c1ab = 25;
// 變量const
const int* c1b = &c1a;
c1b = &c1ab;
//++c1a; //err
//(*c0b) += 1; //err
LOG(c1a); // 20
LOG(*c1b); // 25
// 指針const
// int* const c1d = &c1a; //err const int* 不能轉成 int*
// c0d = &c0ab; // err
//++c1a;
//(*c1d) += 1;
//LOG(c1a); // 13
//LOG(*c1d); // 13
// 變量和指針都const
const int* const c1e = &c1a;
// c0e = &c0ab; // err
//++c1a; //err
//(*c0e) += 1; // err
LOG(c1a); // 20
LOG(*c1e); // 20
/*
總結:
const int* 和 int const* 可以保護 通過指針訪問變量
int* const 可以保護 指針不指向別的變量
const int* const 和 int const * const 可以同時保護 指針訪問變量和防止指向別的變量
如果const 指針指向const變量,限制會更多,邏輯和上方相同,區別是不能直接更改變量了,只能讀。
*/
}
static int ga0a = 5;
const int* d0a(){
return &ga0a;
}
int* const d0b() {
return &ga0a;
}
const int& d0c(){
return ga0a;
}
void func3(){
int e0a = 10;
int* e0b;
const int* e0c;
int& e0d = e0a;
double* e0e;
//e0b = d0a(); // err const int*不能轉int*
e0c = d0a(); // const int*返回值 只能被類行為const int*的給接收。
e0b = d0b(); // int* const可以隱轉成int*
e0b = &e0a;
e0d = d0c(); // const int&可以隱轉成 int &
e0d = e0a;
// 我預想函數變量的部份會與普通變數情況一樣,懶得展開。
}
class f0a{
public:
int x;
void func () const {
//x=10; // err 成員const函數 不能修改成員變量
}
};
class f0b{
public:
const int x=10; //從c++11開始可以這樣,在之前必須在構造函數的成員列表裡初始化。
void func () {
//++x; //err
}
};
//int* const f1a() const{} //err 只有成員函數能用const修飾
int main(){
//func0();
//func1();
//func2();
func3();
f0b newf0b;
LOG(newf0b.x);
return 0;
}