c++入墳: const

 前言

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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值