關於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常量在函數外定義默認是文件級,別人不可訪問。要想成爲程序級(被其餘文件訪問)必需要加extern。A.pp中extern 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 long型u的一個副本
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}; // error,vector沒有這樣的初始化
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)
實參指針不能是空指針,即指向0或NULL的指針。且指向以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();// ok,p所指的對象內容初始化爲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 int轉int
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