《C++Primer》學習筆記(1-5章)



第一章 文件頭及聲明

關於externjava

使用extern 聲明而不定義,它是說明變量定義在程序其餘地方ios

全局不初始化的extern int i; 是聲明不定義;只要聲明而且有初始化式,那麼就是定義;帶有extern且有初始化的聲明(也是定義),好比extern float fval =2.34; 這種必須放在函數外面,不然出錯c++

文件B要訪問另一個文件A中定義的變量,那麼在B中必須先extern聲明一下,而且不須要include A。另外,A中定義的變量必定是全局變量。 git

Extern C程序員

 const常量在函數外定義默認是文件級,別人不可訪問。要想成爲程序級(被其餘文件訪問)必需要加extern。A.pp中extern const int ival=23;而在B.cpp中extern const int ival;聲明一下就可使用(聲明時const不是必須,但最好加上)。另外,const常量在聲明的時候必須初始化,若是是使用常量表達式初始化,最好放在頭文件去定義(頭文件特殊的能夠放定義的三個之一)。不然只能放在源文件中定義,並加上extern以能被多個文件共享。算法

struct MSGMAP_ENTRY {express

    UINT nMessage;數組

    void (*pfn)(HWND, UINT, WPARAM,LPARAM);安全

};函數

struct MSGMAP_ENTRY _messageEntres[] = {

    WM_LBUTTONDOWN, OnLButtonDown,

    WM_RBUTTONDOWN, OnRButtonDown,

    WM_PAINT, OnPaint,

    WM_DESTROY, OnDestroy

};

關於結構體的說明:

上面定義了一種結構體類型後,後面要定義數據類型時就要struct MSGMAP_ENTRY  變量名。 藍色部分就是當變量類型使用

第二章 變量和基本類型

1.      只有內置類型存在字面值,沒有類類型或標準庫類型的字面值(能夠這樣理解,是內置類型組成了其餘類型)。

C++中有整型字面值,浮點字面值,布爾字面值和字符字面值,字符串字面值,轉義序列,多行字面值

2.       下面

a)        ‘數字1’===0x31 (49)

b)        ‘A’===0x41 (65)

c)        ‘a’=====0x61(97)

3.        由空格,製表,換行鏈接的字符串字面值能夠鏈接成一個新的字符串

可是鏈接字符串與寬字符串的結果就不可預料了

代碼續行 \後面不能有空格,只能是回車,接下來的一行要從頭開始,沒有縮進   字符串換行要加/   ===》小線傾斜的方向不同

4.       1024f有錯,整數後面不能有f

5.       2.34UL有錯,浮點數後面不能有U

6.       標識符不能以數字開頭

7.       直接初始化與複製初始化 int ival = 123;  int ival(34);

8.       int ival = 09; 錯! 八進制數,不能有大於等於8的數字

9.       函數外定義的變量初始化爲0,函數內的變量不進行初始化(多是一些無心義的值可是合法的值,因此形成錯,全部編譯器難以發現全部這類錯)

因此,建議每一個內置類型對象都要初始化

在函數外定義的類類型對象使用默認構造函數初始化,對於沒有默認構造函數的類型,要顯式初始化,即便用帶參構造函數

10.   初始化不是賦值。初始化要分配空間並給初值,而賦值則是要替換當前值

11.   聲明與定義的區別

a)        定義要分配空間,一個變量程序中只能定義一次

b)       聲明用於向程序代表本身的名字和類型,程序中能夠出現屢次定義也是聲明,定義的時候也聲明瞭它的名字和類型

c)        使用extern 聲明而不定義,它是說明變量定義在程序其餘地方

函數內或外int i; 都是定義(都分配空間,但函數內沒有初始化爲0)  // 不能放在頭文件中。

d)       全局不初始化的extern int i; 是聲明不定義;只要聲明而且有初始化式,那麼就是定義;帶有extern且有初始化的聲明(也是定義),好比extern float fval = 2.34;這種必須放在函數外面,不然出錯

e)        文件B要訪問另一個文件A中定義的變量,那麼在B中必須先extern聲明一下,而且不須要include A。另外,A中定義的變量必定是全局變量。

f)        在正式編寫程序語句前定義的一些全局變量或局部變量,在C中爲聲明,C++中爲定義 ( int  a;//在標C中爲聲明,是不可執行語句;在C++中爲定義)

12.   定義在函數外的變量有全局做用域

13.  局部同名變量屏蔽了上層做用域的變量,爛程序,不易讀。在局部中要想使用全局中的同名變量在變量前使用域做用符::

14.  在內建數據類型的狀況下,++i與i++效率沒區別,在自定義數據類型的狀況下++i的效率高

15.   c++中,定義和聲明能夠放在任何能夠放語句的位置,因此,一般把一個對象定義在首次使用它的地方是一個很好的辦法

16.   const int pi = 3.14; 採用const易維護(在屢次出現3.14的地方使用const變量來代替)

再好比:

const int max = 23;

for (int i = 0; i != max; ++i)

標準C++中,for中定義的i只在語句做用域中(for語句),出了for就不可見了

17.   全局變量的程序級與文件級,   const全局變量

a)        普通變量在函數外定義就是程序級,別的文件要使用只先extern聲明一下就行。而且不用include變量定義的文件

b)      const常量在函數外定義默認是文件級,別人不可訪問。要想成爲程序級(被其餘文件訪問)必需要加externA.ppextern const int ival=23;而在B.cpp中extern const int ival;聲明一下就可使用(聲明時const不是必須,但最好加上)

c)       const常量在聲明的時候必須初始化,若是是使用常量表達式初始化,最好放在頭文件去定義(頭文件特殊的能夠放定義的三個之一)。不然只能放在源文件中定義,並加上extern以能被多個文件共享。

18.    關於const引用與非const引用與它們所綁定的對象的關係:(const引用是指向const對象的引用,非const引用是指向非const對象的引用)

a)        const引用類型必需要與它所引用的變量的類型一致(非const引用只能綁定到與該引用同類型的對象,而不能是右值)

int ival =12;

int &ref = dval 或34; //error  初始化的時候只能綁定到同類型的對象(不能是右值)。 非初始化的時候能夠被賦值,引用與其綁定的對象的值都會改變

b)       const引用能夠綁定到不一樣但相關(便可以轉化)的類型的對象(能夠是非const對象)或右值

好比[const] double dval= 3.14; //這裏要不要const都行

const int &refVal = dval; // warning,編譯器會中間把3.14轉成int temp的3,而後再給了refVal,這裏編譯不出錯,可是會給出警告

constint &refVal2 = 2.33; // warning,這裏const不可少,否則不使用右值

refVal2 = dval; // error, const引用的值不可改變

19.   enum color {red, blue=4, white};其中white是5

枚舉成員是常量而不能改變,因此初始化時要用常量表達式,const常量與整形字面值都是常量表達式

擴展color black = red; // 必須使用裏面定義的來做爲右值進行初始化

color pink = 3; // error

20.   設計類:從操做開始設計類。先定義接口,能夠決定須要哪些數據以及是否須要私有函數來支撐公有函數

21.   class與struct定義類僅僅影響的是默認訪問級別,struct爲public,class爲private

22.   c++支持分別編譯separatecompilation,頭文件和源文件。將main放在其餘的源文件中.

頭文件中有類定義,extern變量聲明,變量和函數聲明,帶來的莫大的好處,一是統一性:保證全部文件使用的同一聲明,二是易維護:須要修改時,只改頭文件就好了,易維護

設計頭文件時注意:聲明最好放在一塊兒。編譯頭文件須要必定的時間。有些C++編譯器支持預編譯頭文件,須要查手冊。

頭文件用於聲明,而不是定義——有三個例外類定義,用常量表達式初始化的const變量和inline函數的定義。它們能夠在多個源文件中定義(至關於頭文件被include到了多個源文件中),只要在多個源文件中的定義時相同的。

解釋:容許在頭文件中定義類和inline函數是由於編譯器須要它們的定義來產生代碼,對於類類型須要知道類對象的數據成員和操做才能分配空間。可是對於單個源文件A.cpp爲了不多重包含,在定義時須加#ifndef與#endif(頭文件應該有保護符,會被其餘文件包含)

若是將inline函數的定義放在某源文件中,那麼其餘源文件將永遠訪問不到

容許在頭文件中定義const常量的緣由:const常量是文件級做用域的,多個文件包含它互不影響,不算是重複定義。因此能夠在頭文件中定義。多個文件共享const變量的前提是必須保證多個文件使用的是相同的名稱和值,將它放在頭文件中,誰須要的時候就include它就行,安全方便。

另外要注意:在實際中,源文件裏大多編譯器只是像宏替換同樣的使用常量表達式來替換const變量,而沒有分配空間來存儲用常量表達式初始化的const變量

若是const變量不是用常量表達式初始化的,這就不該該放在頭文件中定義。此時它應該放在源文件中定義並初始化,加上extern以使它可以被多個文件共享。

23.  //Page58頁小字部分:編譯和連接多個源文件組成的程序

24.   關於inline函數

inline函數目的是:爲了提升函數的執行效率(速度)。以目標代碼的增長爲代價來換取時間的節省。

非內聯函數調用有棧內在的管理,包括棧建立和釋放的開銷。函數調用函數調用前保護好現場,返回後恢復現場,並按原來保存的地址繼續執行。對於短小且頻繁執行的函數,將影響程序的總體性能

C中能夠用#define,編譯器用複製宏代碼的方式取代函數調用,但沒有參數類型檢查。

 

有兩點特色注意的:
(1) 內聯函數體中,不能有循環語句、if語句或switch語句,不然,函數定義時即便有inline關鍵字,編譯器也會把該函數做爲非內聯函數處理。
(2) 內聯函數要在函數被調用以前定義,不然內聯失效。將它的定義放在頭文件中就很好的能夠作到這點,因爲是在源文件中先include,再後面調用的,就保證了調用以前定義

(3)關鍵字inline必須與函數定義體放在一塊兒才能使函數真正內聯,僅把inline放在函數聲明的前面不起任何做用。由於inline是一種用於定義的關鍵字,不是一種用於聲明的關鍵字。(根據高質量C/C++指南,聲明前不該該加,由於聲明與定義不可混爲一談)

25.                                                                                                                                                                                                                                                     複合類型compoundtype,引用,數組,指針

第三章標準庫類型

1.#include<string>

usingstd::string;

   using std::cout;

2.頭文件中定義肯定須要的東西

3. 注意區分string類型與字符串字面值

4. Windows下用命令行要編譯和運行.cpp文件

打開vs2005命令提示

cl /EHsc simple.cpp 生成exe文件//EHsc 命令行選項指示編譯器啓用 C++ 異常處理

若要運行 simple.exe 程序,請鍵入 simple 並按 Enter

要加入程序變量: simple < data\book_sales

vs2005中一樣也能夠加入程序變量,就在命令行參數裏

5.         

a)  cin.getline(char*, int, char) // 是istream流

// 能夠接收空格並輸出,它的參數有三個,第三個是結束符,默認爲'\n'

 

char ch[20];

       cin.getline(ch,5); 

       cout<< ch << endl; // 輸入abcdefg,輸出abcd最後一個爲'\0'

                            //這個\0是自動加上的,因此能夠cout << ch;

char ch[20];

       cin.getline(ch,5,'a');  // 遇到a結束,並加上\0

       cout<< ch << endl; //輸入xyamnop,輸出xy

 

b)       getline(cin, str), 須加#include <string> // 是string流

        #include <string>
        istream& getline( istream& is, string& s, char delimiter = '\n' );  // delimiter分隔符
    接收一行字符串,能夠接收空格並輸出,遇到回車返回,並丟掉換行符。

不忽略行開頭的換行符,若是第一字符是回車,那麼str將是空string

逐行輸出:

string str;

while(getline(cin, str)) {

       cout << line << endl; // 因爲不含換行符,須要endl刷新輸出緩衝區

}

逐詞輸出:

string str;

while (cin>> str) {

       cout << str << endl;

}     

c)        cin.get()吃掉沒有用的字符,讀到回車才退出

d)       char ch1[100],ch2[100];

cin >> ch1 >> ch2;  //接收字符串,忽略有效字符前的空白字符,以空白符(空格,TAB,回車)做爲輸入結束

getline(cin,str) 不忽略開頭的空白字符,讀取字符直到遇到換行符,讀取終止並丟掉換行符

e)        gets(str) 接收一個字符串,能夠接收空格並輸出。加#include<iostream>

與getline(cin,str)相似

f)        getchar()無參數,須加#include <string>,是C中的函數,儘可能少用或不用

6.        str.size()的實現(返回有效字符的個數,不含最後的空字符,它的類型是string::size_type)

    constchar *st = "The expense of spirit\n";

    int len = 0;

    while (*st) { ++len; ++st; }

str.size的應用

for (string::size_type ix = 0; ix !=str1.size(); ++ix)

7.        string對象能夠==, >=, <= !=操做

8.        string的賦值操做:str1 = str2;須先把str1的內存釋放,而後再分配能存放副本大小的空間,再複製過來

9.        string s2 = 「hello」 + s1; //error,開頭必須是變量

10. #include <cctype>  p77頁

isalnum(c)  字母數字

isalpha(c) 字母

iscntrl 控制字符

isdigit 數字

isgraph 不是空格可是能夠打印

islower 小寫

isprint 可打印

ispunct 標點

isspace 空格

isupper 大寫

isxdigit 十六進制數

tolower 變小寫

toupper

11. p78 使用C標準庫頭文件,使用#include <cname>不要用#include <name.h>,這樣保證標準庫文件中的名字與std中的一致

12. string s;

cout << s[0] << endl; // error

13. t3_10,輸入一個字符串,去掉裏面的標點:

ispunct(ch)

result_str+= ch;

14. string是數據類型,vector是模板 vector<string>是數據類型

15. vector<Student> v5(4); // 4個實例,必須有默認初始化構造函數

vector<int>v3(10,5); // 10個5

 

雖然能預先分配內存,但使用空vector來push_back更好

for(ix!=vector_size) vec.push_back(ix*5);

 

vector<int>ivec; //是個空vector,沒有分配空間,因此ivec[0]=3;//error

 

for(vector<int>::size_type ix = 0; ix != ivec.size(); ++ix) //因爲是動態變化的,因此這時使用函數

 

vector<int>ivec(10); // 10個0

for(vector<string>::const_iterator it = ivec.begin();it != ivec.end(); ++it) { // 若是ivec爲空,則for不執行

        //cout << *it << endl;

       *it = 42;// error遍歷只讀

   }

vector的操做p81 , ==, !=, < >=

16. 迭代器能夠==或!=操做,若指向同一個元素 則相等

迭代器的算法操做

 it+n size_type類型

 it-n  difference_type類型

 it1-it2 difference_type類型  it1或it2能夠是vec.end()

記住:push_back()等改變長度的操做,使存在的迭代器所有失效!!

vector<int>::iterator

17. bitset<n> b; // n個0 //n必須是字面值或用常量值初始化的const對象

bitset<n> b(u); // b是unsigned longu的一個副本

bitset<n> b(s); // 字符串

bitset<n> b(s, pos, n); // 字符串pos開始的n個

如:

bitset<16> bitvec1(0xffff); // 高十六個0去掉, 0到15位設爲1

0xffff表明32位的unsigned long,高十六個0,低十六個1

bitset<128> bitvec2(0xffff); // 31位以上的都是0

string s(「1100」);  // s[0]值爲1

bitset<8> bitvec3(s); // 讀入位集的順序是從右向左(用string最大下標元素來賦值給bitvec的最小下標值),結果是0000 1100,  這樣bitvec[0]的值是最右的那個0

size_t sz = bitvec1.size(); // #include <cstddef>

//一個與機器相關的unsigned類型,其大小足以保證存儲內存中對象的大小

    if (bitvec.test(i))

    if (bitvec[i])

    bitvec.reset(); 全爲0

    bitvec.set(); 全爲1

    bitvec.flip(1);

    bitvec[1].flip();

    bitvec.flip(); // 所有取反

    uLong = bitvec.to_ulong(); 取回unsigned long值  

       // 若bitvec的位數128超過了unsigned long長度,那以會產生overflow_error異常

    t3_24:

    1,2,3,5,8,13,21的相應位置爲1

    bitset<32> b;

    int x = 0, y = 1, z = x + y;

    while (z <= 21) {

       b.set(z);

       x = y;

       y = z;

       z = x + y;

}

第四章數組和指針

1.   int arr[32]; // 維數必須是字面值,枚舉常量,常量表達式初始化的const變量

上面這句話在函數外,則初始化爲0

定義在函數內,沒有初始化,但分配了空間

    int arr[5] = {1,2,3}; // 剩下的初始化0,如果類類型就使用默認初始化構造函數,  java中不行,不能寫維數的

    vector<int> ivec = {1,2,3}; // errorvector沒有這樣的初始化

2.  數組不能直接複製和賦值

3.    數組下標越界致使:buffer overflow

4.   比較兩個vector,先比較長度

5.   現代C++使用vector替換數組,使用string替換C風格字符串

6.   關於指針:

a)   string* s1, *s2看上去很很差,因此儘可能將*與變量名放在一塊

b)   避免使用未初始化的指針

C++沒有辦法檢查出未初始化的指針,使用它可能會致使基礎數據

若是要指向的對象尚未存在,那麼就先不要定義這個指針。若是要定義,就=0或NULL#include <cstdlib>

預處理器變量NULL不是在std命名空間中定義的,因此不是std::NULL

c)   void *

void* 這不叫空指針,這叫無確切類型指針.這個指針指向一塊內存,卻沒有告訴程序該用何種方式來解釋這片內存.因此這種類型的指針不能直接進行取內容的操做.必須先轉成別的類型的指針才能夠把內容解釋出來;

'\0'不是空指針所指的內容,而是表示一個字符串的結尾,不是NULL

真正的空指針是說,這個指針沒有指向一塊有意義的內存,好比說:
char* p;
這裏這個p就叫空指針.咱們並未讓它指向任意地點,又或者 char* p = NULL;這裏這個p也叫空指針,由於它指向NULL 也就是0,注意是整數0,不是'\0'

一個空指針咱們也沒法對它進行取內容操做.
空指針只有在真正指向了一塊有意義的內存後,咱們才能對它取內容.也就是說要這樣p = "hello world!"; 這時p就不是空指針了

後面會看到:不能使用void*指向const對象,必須使用const void *類型的指針保存const對象的地址

void *p; 只支持如下操做:

    與另外一指針比較

    做參數或函數返回值

    給另外一個void *賦值

7.   指針與引用的比較:

引用定義時必須初始化!

引用被賦值後改變的是所引用的對象

指針被賦值後將會指向另外一個變量的地址,不專注

8.   int arr[5];

int *p = arr;

int *p2 = p + 2;// 那麼,p的算術運算要求原   指針與計算出的新指針都要指向同一數組的元素,結果範圍必須在[0,5],//注意這裏5也算合法結果

    注意:C++容許計算數組或對象的超出末端的地址,但不容許對此地址進行解引用操做

    能夠把指針當作是數組的迭代器

 

ptrdiff_tn = p1 – p2; // #include <cstddef>

    // 與機器相關:size_t是unsigned,   ptrdiff_t是signed

注意,ptrdiff_t只保證指向同一數組的兩個指針間的距離,如果指向不一樣的指針,那麼error

 

t4_17:

p1 += p2– p1; 若p1與p2指向同一數組那麼始終合法,讓p1指向p2所指向的地址

9.   關於const指針

a)   指向const對象的指針

const double pi = 3.14; // 不能用普通指針來指向它 可是相反地,能夠用const double*的指針指向非const的變量

const double *cptr = 0或NULL; //這裏const限定的是所指向的對象,而不是cptr自己,因此定義的時候能夠不初始化, 藍const是必須的,防止經過cptr來改變所指對象的值

cptr = pi; // 也能夠在定義的時候初始化

指向const對象的指針能夠指向非const對象

 

注意:不能保證cptr指向的對象的值必定不可修改!它徹底有可能指向一個非const對象,將它賦給普通指針就能夠改

double dval = 2.34; // 非const

cptr = &dval; //能夠指向一個非const對象,但仍不可經過cptr來修改

const void *cpv = pi; // ok ,不能使用void*指向const對象,必須使用const void *類型的指針保存const對象的地址

t5_33: 將一個指向const對象的指針成void *

const string *ps;

void *pv;

pv = (void *)ps; // error!!!應該按下面寫

pv = static_cast<void*>(const_cast<string*>(ps));

把ps變成string*以後再轉成void*,就是說,先去掉const特性

static_cast<void*>跟(void *)差很少,就至關於強制轉化

//============找回原來指針值,與上例t5_33不要緊

對於void *pv;

小括號強制轉化pc = (char*)pv; // 找回原來指針值

或者pc =static_cast<char*>(pv);

b)   const指針 //不能再指向別的對象,但能改變所指向對象的值  

int ival = 32;

int *const ptr = &ival; // 從右向左讀:指向int對象的const指針

*ptr = 34; 則ival的值也變成34了。

但ptr = &ival2;  //error指針值不可變

c)   typedef與const指針

typedef string *pstring;

const pstring cstr; // 這裏不要簡單的理解爲const string *cstr, 這裏const限定的是指針,而不是所指對象!! string* const cstr 最好寫成pstringconstcstr; 好理解

10.  t4_19:

const int ival; // error必須初始化

int *const ptr1; // error必須初始化

const int *ptr2; // ok,能夠不用初始化

const int ival2 = 23; // 不能由指向非const對象的指針來指向它

int *const ptr2 = &ival2; // error,指向非const對象的指針不能指向const對象

11. p113講:C風格字符串儘可能少用,由於它經常帶來不少錯誤,是致使大量安全問題的根源!

c-style character string

const char *cp = 「hello world」;

while (*cp) {

    //TODO..

    ++cp;

}

c風格字符串的庫函數:#include <cstring>

strlen(s)// 返回有效長度

strcmp

strcat

strcpy

strncat(s1, s2, n) // 前n個字符, 返回s1

strncpy(s1, s2, n)

實參指針不能是空指針,即指向0NULL的指針。且指向以NULL結束的字符數組中的指針

必須確保目標字符串足夠大,不然形成安全漏洞

char ca[] = {‘a’,’b’,’c’};

strlen(ca); // error!!!

使用strn比strcat strcpy更安全

strncat鏈接時,第二個字符串要覆蓋掉第一個字符串的NULL

12. t4_25

兩個string類型的比較可使用>,< 而兩個C風格字符串比較使用strcmp

它們讀入:t4_26

string str;

cin >> str;

size_t str_size = 90;

char ch[str_size];

cin >> ch;

13. 建立動態數組, 在運行時肯定長度

int *p = new int[3];  //內置類型分配空間 無初始化

int *p = new int[4](); 值初始化爲0, 但沒有初始化值列表的形式

const int *p = new const int[22](); //最後的()是必須的,否則就沒有初始化const元素 ,雖然vs2005經過

string *pstr = new string[4]; // 分配空間 使用默認構造函數初始化

const char *p4 = s2.c_str();

14. int arr[0]; // error,不能爲0

int *parr = new int[0]; // ok, 能夠爲0,可是parr不能進行解引用操做

15. delete[] parr; // 漏掉[]編譯器不會發現錯,運行時錯

16. 使用string比C風格字符串的程序執行速度要快不少

17. 可使用C風格字符串對string對象賦值或初始化

C風格字符串能夠做爲左操做數與string對象相加

    char*ch = "hello ";

    strings("good");

    strings2;

    s2 =ch +s; //  ok

    s2= 「abc」 + ch + s; // error

18. 使用數組初始化vector

in iarr[6] = {0,1,2,3,4,5};

vector<int> ivec(iarr+1, iarr+4); //[a,b),不包括iarr[4]

19.  t4_34

   vector<string> ivec;

   string str;

   while (cin >> str) { // 讀入一組string放在vector中

       ivec.push_back(str);     

    }

//下面是將vector這組string拷到一個字符指針數組中。

   char **pa = new char*[ivec.size()];

   size_t k = 0; // #include <cstddef>

    for(vector<string>::iterator it = ivec.begin(); it != ivec.end(); ++it) {

       char *p =  new char[(*it).size() +1]; // 分配空間

       strcpy(p, (*it).c_str());  //拷貝 #include <cstring>

       pa[k++] = p;  

    }

20.  int *p[3];

int (*p)[3];

21.  使用typedef簡化多維數組

int ia[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};

typedef iint int_array[4];

int_array *p; // p也能夠定義爲 int (*p)[4];

for (p = ia; p != ia + 3; ++p;) {

    for(int *q = *p; q != *p+ 4; ++q) {

       cout<< *q << endl;

}

}

第五章表達式

1.   指針不能轉化爲浮點類型

2.   short             [-32768,32767]

unsigned short:      [0, 65535]

    int:                 [-21.4億, 21.4億]

3.   -21 % -8 值爲 -5

4.   對於位操做,因爲系統不能確保如何處理符號位,因此建議使用unsigned數!

 

左移補0

unsigned char ch = 0277;

ch = ~ch; 所有取反 flip操做

ch << 1; // 左移,右邊補0

       //右移左邊補0; 右移個數要小於8(操做數的位數)

對於signed,右移補0仍是符號位根據機器而定

 

bitset優於整數數據的低級直接位操做

bitset_quiz1.set(27);

int_quiz1 |= 1UL << 27;

bitset_quiz1.reset(27);

int_quiz1 &= ~(1UL << 27);

<<優先級高於<

因此 cout << (10 < 32); // ok

     cout << 10 < 32;   // error

 

5.    i + j = k; // error

int i;

const int ci = i; // ok, 未來ci無心義

6.    int a; int *p;

a = p = 0; // error 不能將指針類型賦給int

7.    a += b; a計算了一次, a = a + b; a計算了兩次

8.    使用++i而不用i++:

工做量少啊,後置操做要先保存操做數原來的值,以便返回未加1以前的值做爲操做的結果。

9.    .>*  取成員操做大於解引用操做

(*pstu).name = 「leiming」;

 pstu->name = 「leiming32」;

++>*  ++也大於解引用操做

*iter++ 至關於 *(iter++)

10.  t5_18:定義一個string指針的vector,輸入內容,而後輸出

       vector<string *> vec;

    string str;

    cout << "enter strings:" ;

    while (cin >> str) {

          string *pstr = new string; // 必須新分配一塊地方

                     // string *pstr = &str;不對,指向同一空間啦

          *pstr = str; // 每一個指針單獨指向一塊空間,賦值

        vec.push_back(pstr);

        if(vec.size()==4) cout <<"vec[1]:" << *vec[1] << endl;

    }

    for (vector<string *>::iterator it =vec.begin(); it != vec.end(); ++it) {

        cout <<**it << ", len: " << (**it).size()<< endl;

}

11. 三種語法:

sizeof (expr)

sizeof (type name) // 必須加括號

sizeof expr :  並無計算表達式的值

Sales_item item, *p;

sizeof item;

sizeof *p; // 並無解引用操做,  p指向一塊沒有意義 的地址

sizeof (Sales_item)

12. 逗號表達式的是其最右邊表達式的值

13.  int *p = new int; //ok 沒有初始化

int *p = new int();// okp所指的對象內容初始化爲0

 

int *p = 0;

delete p; // ok,能夠刪除空指針

 

delete p;以後,刪掉了它所指向的對象,p此時成了懸掛指針,應該p = 0;

 

若是你使用delete是未加括號,delete便假設刪除對象是單一對象。不然便假設刪除對象是個數組。delete[] 相比 delete 會多作一件事,就是在刪除這個內存塊前,析構每一個元素,特別是對於對象數組

萬一須要重載 new, delete,new[], delete[], 咱們必須所有都重載,而不能僅僅重載 new, delete, 而後但願 new[],delete[] 會調用它們

 

string*stringPtr1 = new string;
  string *stringPtr2 = newstring[100];
  ……
  delete stringPtr1;
  delete [] stringPtr2;
  若是你對着stringPtr1使用「[]」形式,其結果未定義。若是你對着stringPtr2沒有使用「[]」形式,其結果亦未定義。猶有進者,這對內建型別如int者亦未定義,即便這類型別並無destructors。

 

遊戲規則很簡單,若是你在調用new時使用了[],則你在調用delete時也使用[],若是你在調用new的時候沒有[],那麼你也不該該在調用時使用[]。

 

14. a[][5]; // 第一維可缺省 ,5不可省

15. 動態建立const對象:

const int *pci = new const int(32); // 定義的時候必須初始化,初始化後就不能夠修改  pci指向一個const對象,因此它也要是const

1)  普通指針不能指向const對象;

void reset(int *pi) //實參不能是指向const int對象的指針

 // 爲了保護所指向的值,通常地將定義爲指向const對象的指針

void reset(const int *cpi);

2)  指向const對象的指針能夠用普通指針來初始化

3)  指向const對象的指針能夠指向非const對象,但不能修改它,能夠賦給非指向const對象的指針來改;

4)  可使用普通指針初始化const指針

5)  不能用const對象來初始化非const的引用

6)  可使用const對象來初始化非const對象,反之亦然

因此const形參的時候,實參能夠是const對象也能夠是非const對象

C語言中,const形參與非const形參沒有區別,爲了支持與C的兼容,編譯器將const形參看做是普通的形參,因此const不能看做是函數重載的根據

 

const string *pcs = new conststring; // ok

內置類型的以及沒有默認構造函數的類型必須顯式初始化

 

能夠刪除const指針:

delete pci; // ok

delete pcs; // ok

16. t5_30:

vector<string> svec(10); // 十個空串元素

vector<string> **pvec = new vector<string>[10]; // error

    //右邊返回的是一個指向元素類型是vector<string>的指針

17. 算術轉換:

char, signed char, unsigned char, short, unsignedshort若是能包容在int內則轉換爲int,不然轉成unsigned int(主要針對unsiged short)

 

bool 提高爲int, false轉爲0, true轉爲1

    inta = 2 + true;

    cout<< a << endl; // 3

 

 向上提高:int->unsigned int->long->unsignedlong

signed int + unsigned int,則signed int會轉爲unsigned int

    unsignedchar b = -1; // 1000 0001,符號位不變全部位取反,再加1 : 1111 1111

    cout<< (int)b << endl; // 255

 

    inti = 32;

    constint &iref = i; // 使用非const對象初始化const對象的引用,將非const對象轉成const對象

       i= 33;

    //iref= 34; // error

    cout<< iref << endl; //ok, iref要隨着i而變 = 33

const int *cpi = &i; // 非const對象的地址(或非const指針)轉成指向相關const類型的指針

int *pi = &i;

const int *cpi2 = pi; // 可使用非const指針初始化const指針

 

 

18.  顯式轉換

1)   何時使用

       i.     強制轉換

      ii.     存在多種類型轉換時,須要選擇一種特定的類型轉換

2)   cast-name<type>(expression)

dynamic_cast

運行時識別指針或引用所指向的對象

3)   const_cast

    int*cpi3 = const_cast<int *>(cpi);// 是指針或對象

    //*cpi= 100; // error

    *cpi3= 43;

    cout<< "i=" << i << endl;

 

       const int ci = 32;

    int ival = const_cast<int>(ci);//error不能從const intint

    int &iref2 = const_cast<int&>(ci); // 能夠將const轉換掉

    iref2 = 35;

    cout << ci << endl; // 32

    cout << iref2 << endl; // 35

4)   static_cast

int ival = static_cast<int>(2.4); // 關閉警告,不關心精度丟失

 

能夠找回放在void*中的地址值

double dval = 2.33;

void *p = &dval;

double *pd = static_cast<double*>(p);

5)   reinterpret_cast

爲操做數的位模式提供較低層次的從新解釋,依賴於機器,要閱讀編譯器細節。

int *ip;

char *pc = reinterpret_cast<char*>(ip);//不能把pc當作是普通字符指針,程序員必須記住pc指向真正指向的是int

string str(pc); // compile error

6)   最後:避免使用強制類型轉換,也能寫出好程序

使用const_cast拋棄cast屬性意味着設計缺陷

有些強制轉換有危險

若是必需要用,則限制強制轉換值的做用域,記錄全部假定涉及的類型

7)   在新編譯器上寫程序,最好不要用()式的老強制轉換

19.  dval = ui * fval; // ui先轉成float再乘,結果是double

相關文章
相關標籤/搜索