C++ Primer第二章

數據類型

數據類型決定了數據的意義和對數據的操作。包括built-in type , customized type , complicated type provided by STL。


char:用於針對機器基本字符集(ASCII)
wchar_t,char16_t,char32_t:擴展字符集(Unicode)
C++11開始支持long long。

  • 計算機以“塊”處理內存,可尋址的最小內存塊稱爲字節(能夠表示機器基本字符集,爲8bit),存儲的基本單元稱爲字(一個字對應多個字節)。 字和字節都對應地址。 對於同一個地址,數據的類型不同決定了數據所佔的bit和解釋方式。

關於類型的選擇建議:

  1. double與float,選擇double。double精度高,而且計算代價與float差不多,有些機器甚至double快。 long double代價太大。
  2. 在數值一定不爲負,選擇unsigned
  3. int和long的長度一般是一致的,32bit。long long是64bit。
  4. char和bool一般不用於運算。 char在不同的機器上有可能是unsigned char,也可能是signed char,進行運算,對於可移植性有很大的影響(在0~127之內是不變的)。最好能夠明確signed char和unsigned char(在使用的時候)
  • 不能混用有符號數和無符號數,當有符號數和無符號數混在一起,有符號數會自動轉換成爲無符號數。(對於無符號數,當出現負數,就會自動取模轉換在無符號數的範圍之內,unsigned int a=-1, a=4294967295)

literal:字面值常量;(字面意思上能夠看出值的)

整形字面值常量:十進制,八進制(0),十六進制(0x)
字符串字面值:是常量字符構成的數組。編譯器會在字符串結尾添加一個空字符(’\0’)。

對於字面值常量,都是有其自己的默認類型的的,比如整形十進制默認爲int類型,這是由C++標準/編譯器規定的。如果想要特指其類型,可以通過前綴和後綴進行說明。

  • 字符串和字符字面值
前綴 含義 類型
u Unicode16字符 char16_t
U Unicode32字符 char32_t
L 寬字符 wchar_t
u8 UTF-8(僅用於字符串字面量) char
  • 整形字面值
後綴 含義
U/u unsigned
L/l long
ll\LL long long
  • 浮點字面值
後綴 含義
f/F float
l/L long double

變量

  • C++中,對象的定義:是具有某種數據類型的內存空間。(包括普通變量) 變量和對象認爲是同一個東西。

  • 初始化與賦值:(含義區別)

    • 初始化通過創建變量時賦予一個新值。
    • 賦值是把當前變量的值擦除,然後賦予一個新值。
  • 對於類想要保留其默認構造函數,只需要建立一個沒有參數和沒有內容的空構造函數即可。(對象建立後的屬性值取決於其數據類型默認的初始值)

class testClass
{
public:
    testClass();                    /* 默認構造函數 */
    testClass(int a, char b);        /* 構造函數 */
    testClass(int a=10,char b='c');    /* 默認構造函數 */

private:
    int  m_a;
    char m_b;
};
  • 通過關鍵字extern進行聲明變量,但是不能夠對其初始化賦值(初始化賦值,即使有extern,也相當於定義) extern int i 通過extern聲明變量,可以在其他的文件中聲明並且調用同一個文件中的變量。

  • 強類型語言:偏向於不容忍隱式類型轉換

    • 弱類型語言:偏向於容忍隱式類型轉換。 C++
    • 靜態類型:編譯的時候就知道每一個變量的類型,在編譯的時候進行類型檢查,出現錯誤屬於語法錯誤,不會生成可執行文件。 C++
    • 動態類型:編譯的時候不知道每一個變量的類型,因爲類型錯誤而不能做的事情是運行時錯誤。
  • 變量的作用域:被嵌套的作用域能夠屏蔽外層的作用域(即使外層是全局變量);不過,對於全局變量,一般不進行屏蔽。

int reused=42 ;
int main() {
    int reused=32;
    cout<<reused<<endl;   //32
    cout<<::reused<<endl;       //全局作用域沒有名字 , 當在塊中出現同名的,通過 '::'調用
}
/*理一理關於全局作用域,塊作用域,和using namespace:
無論有無namespace,與全局作用域,塊作用域都無關係;
但是對於namespace裏面的函數有關係,原來需要使用 xx::XXX調用,現在只需要XXX就可以調用,可以看成將namespace裏面的內容融入全局作用域
*/

指針和引用

引用(左值引用):reference(lvalue reference)

  • 引用的本質:引用就是爲對象起另一個名字(因此,引用不是創建對象,它是基於一個已有的對象,進行綁定(bind);它具有對引用對象的控制權)
  • 通過&來定義引用類型,定義必須初始化。 int ival=1024,int &refVal=ival(refVal是ival的別名,可以通過改變refVal的值改變ival的值) 引用一經初始化之後,就不能更改綁定對象!
  • 引用只是一個別名,不是對象(對象的定義:具有某種數據類型的內存空間),用例子來說明:
#include <iostream>
using namespace std;

int main()
{
    int a=3;
    int &b=a;
    cout<<&a<<endl<<&b;
}

0x786e98f1835c 
0x786e98f1835c 
//結果發現地址是一致的,說明引用僅僅是一個邏輯上的表示,實際上(內存)是不存在的。
  • 引用經過綁定之後,相當於直接操作本體(操作,性質都一樣)int reused=32; int &ref=reused; int &rf=ref;,rf和ref是一個意思

指針(pointer)

  • 與引用相比,它是一個對象,能夠實現多重指針。 可以變化指向的對象。 無需賦初值。
  • 引用是綁定;指針是指向。

void* :一個能夠存放任意對象地址的指針。我們無法直接操作void* 所指的對象,因爲,不知道對象的具體類型。

多重指針:通過* 的個數可以區分指針的級別。 ** :表示指向指針的指針。

int ival=1024;
int *pi=&ival;
int **ppi=&pi;   ppi是指向pi指針的指針;(順序)

指向指針的引用:

int i=42;
int *p;
int *&r=p;  //想要理解r的類型,方法是:從右到左閱讀r的定義!  (最右邊的是對變量的類型最直接的影響)先是&,說明是個引用;    然後其餘部分確定引用的類型,這裏是*,說明是對指針的引用。

r=&i;
*r=0;

const

  • const對象的值無法改變,必須初始化。 (const修飾的常量需要初始化)

  • 通過extern實現const在多個文件的共享。(這一點,我也不太瞭解,以後在實踐中遇到再解決)

  • const引用:

    • 對常量的引用。const int &ref; 不能修改綁定對象的值的。(常稱爲“常量引用”)
    • 不存在真正的常量引用。 因爲引用不是一個對象,因此無法讓引用恆定不變。也就是不存在 int &const ref
  • const與指針:

    • 指向常量的指針(pointer to const)。不能用於改變其所指對象的值。(不只是不能更換所指的對象,而且不能更換所指對象的值) 例如:const double *p(從右向左:首先,其是指針,然後double說明指向的對象是double型,const說明修飾的對象是const)(對於指針,強調的是指向對象的類型)
      • 對於const變量的地址,只能通過const指針,而不能通過普通指針。(因爲,類型決定了操作) const double pi=3.14 ; const double *ptr=&pi
      • 可以通過const指針指向普通變量,不過const指針依舊不能修改其值。(自認爲指向常量,自覺不去改變)
    • const指針(常量指針):指針是對象,表示指針是常量,說明其始終指向一個對象,對象的值可以改變。 常量指針跟普通const變量差不多,都需要初始化,初始化之後值不改變。
      • 定義形式:double *const p: 首先p是個常量,然後*,說明其是指針類型,最後double表明其是一個指向double類型的const指針。 const double *const pip=&pi
  • 頂層const和底層const:

    • 頂層const:表示對象本身是個常量(對任何數據類型使用)
    • 底層const:表示所指對象是個常量(與指針,引用等複合類型有關) (指針類型既有頂層,也有底層;引用只有底層)
    • 分清頂層/底層const能夠明白什麼能變,什麼不能變!
    • 在轉換中牽扯了很多隱式數據類型轉換,很多很雜;理解的關鍵在於:const能夠將非const當成const處理,非const不能處理const;(數據類型決定操作) (非const數據類型能夠像const對應的數據類型進行轉換)

constexpr和常量表達式

  • 常量表達式(const expression):值不會改變而且在編譯過程就能得到計算結果的表達式。 (字面值屬於常量表達式;用字面值常量初始化的const對象也是) const int sz=get_size();不屬於const expression,因爲無法再編譯得到結果。

  • C++11中,允許將變量聲明爲constexpr類型來驗證變量是否屬於常量表達式。(如果是,正確運行;否則會爆出bug)

constexpr int mf=20;
constexpr int limit=mf+1;
constexpr int sz=size();  //如果size是一個constexpr函數,纔是正確語句
//constexpr函數能夠使得在編譯期間得到結果;

處理類型

類型別名

  • typedef定義別名;(typedef double wages;)
  • C++11規定新方法,使用別名聲明來定義別名。 using SI=Sales_item;(使用using,SI作爲Sales_item的別名)(用等號左邊作爲等號右邊的別名)

  • 當類型別名指代的是複合類型,語句含義發生變化。
typedef char *pstring;
const pstring cstr=0;   //指向char的常量指針,而不是指向char常量的指針,與const char*含義不同
const pstring *ps;      //指向char型的常量指針的指針

理解關鍵:複合類型的定義包括基本數據類型和一組聲明符(* /&)。
在const pstring中,將pstring作爲基本類型,因此其是指針。
在const char * 中,const char是基本數據類型,* 作爲聲明符。 (如果* 作爲聲明符,那麼其前面的內容修飾指針所指向的內容;否則修飾本身)

auto

  • auto能夠讓編譯器通過初始值推算變量的類型。(必須有初始值) auto a=3;

decltype類型指示符

  • 選擇並且返回操作數的數據類型。(只得到其類型,但不計算實際表達式的值)
decltype(f()) sum=x;
decltype(ci) y=0;
  • 對於引用,decltype比較特殊,一般來說,引用初始化之後,我們對其調用就是調用引用對象;在這裏,decltype的對象如果是引用,那麼返回的類型是引用類型,而不是引用對象的類型。

  • C++的一些規範:

    • 爲了確保各個類中定義一致,類通常定義在頭文件中;
    • 頭文件通常包含那些只能被定義一次的實體,如類,const變量;(#ifdef #endif)
  • 預處理器(preprocessor):在編譯之前執行的一段程序。

    • 預處理器能夠配合頭文件保護符來使頭文件只引用一次。
    • 頭文件保護符依賴於預處理變量。預處理變量有兩種狀態:已定義/未定義。#define可以定義一個預處理變量(已定義),#fidef當且僅當變量定義且成真,#ifndef當且僅當變量未定義且成真,直到遇到#endif。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章