數據類型
數據類型決定了數據的意義和對數據的操作。包括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和解釋方式。
關於類型的選擇建議:
- double與float,選擇double。double精度高,而且計算代價與float差不多,有些機器甚至double快。 long double代價太大。
- 在數值一定不爲負,選擇unsigned
- int和long的長度一般是一致的,32bit。long long是64bit。
- 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=π 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指針,而不能通過普通指針。(因爲,類型決定了操作)
- const指針(常量指針):指針是對象,表示指針是常量,說明其始終指向一個對象,對象的值可以改變。 常量指針跟普通const變量差不多,都需要初始化,初始化之後值不改變。
- 定義形式:
double *const p
: 首先p是個常量,然後*,說明其是指針類型,最後double表明其是一個指向double類型的const指針。const double *const pip=&pi
- 定義形式:
- 指向常量的指針(pointer to const)。不能用於改變其所指對象的值。(不只是不能更換所指的對象,而且不能更換所指對象的值) 例如:
-
頂層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。