1.建構函式初始化
Animal::Animal():aget(1),maxLevel(30)
{
}
2.除非友元,否則不允許使用者訪問類中的私人資料成員。
friend void fun(Animal& a);
void fun(Animal& a)
{
cout<<a.name<<endl;
}
3.如果參數是指標,且僅作輸入用,則應在類型前加const,以防止該指標在函數體內被意外修改。
例如:
void StringCopy(char *strDestination,const char *strSource);
如果輸入參數以值傳遞的方式傳遞對象,則宜改用“const &”方式來傳遞,這樣可以省去臨時對象的構造和析構過程,從而提高效率。
4.函數的傳回值是一個對象
如果函數的傳回值是一個對象,有些場合用“引用傳遞”替換“值傳遞”可以提高效率。而有些場合只能用“值傳遞”而不能用“引用傳遞”,否則會出錯。
例如:
class String
{…
// 賦值函數
String & operate=(const String &other);
// 相加函數,如果沒有friend修飾則只許有一個右側參數
friend String operate+( const String &s1, const String &s2);
private:
char *m_data;
}
String的賦值函數operate = 的實現如下:
String & String::operate=(const String &other)
{
if (this == &other)
return *this;
delete m_data;
m_data = new char[strlen(other.data)+1];
strcpy(m_data, other.data);
return *this; // 返回的是 *this的引用,無需拷貝過程
}
對於賦值函數,應當用“引用傳遞”的方式返回String對象。如果用“值傳遞”的方式,雖然功能仍然正確,但由於return語句要把 *this拷貝到儲存傳回值的外部儲存單元之中,增加了不必要的開銷,降低了賦值函數的效率。例如:
String a,b,c;
…
a = b; // 如果用“值傳遞”,將產生一次 *this 拷貝
a = b = c; // 如果用“值傳遞”,將產生兩次 *this 拷貝
String的相加函數operate + 的實現如下:
String operate+(const String &s1, const String &s2)
{
String temp;
delete temp.data; // temp.data是僅含‘\0’的字串
temp.data = new char[strlen(s1.data) + strlen(s2.data) +1];
strcpy(temp.data, s1.data);
strcat(temp.data, s2.data);
return temp;
}
對於相加函數,應當用“值傳遞”的方式返回String對象。如果改用“引用傳遞”,那麼函數傳回值是一個指向局部對象temp的“引用”。由於temp在函數結束時被自動銷毀,將導致返回的“引用”無效。例如:
c = a + b;
此時 a + b 並不返回期望值,c什麼也得不到,流下了隱患。
5.引用與指標的比較
引用是C++中的概念,初學者容易把引用和指標混淆一起。一下程式中,n是m的一個引用(reference),m是被引用物(referent)。
int m;
int &n = m;
n相當於m的別名(綽號),對n的任何操作就是對m的操作。例如有人名叫王小毛,他的綽號是“三毛”。說“三毛”怎麼怎麼的,其實就是對王小毛說三道四。所以n既不是m的拷貝,也不是指向m的指標,其實n就是m它自己。
引用的一些規則如下:
(1)引用被建立的同時必須被初始化(指標則可以在任何時候被初始化)。
(2)不能有NULL引用,引用必須與合法的儲存單元關聯(指標則可以是NULL)。
(3)一旦引用被初始化,就不能改變引用的關係(指標則可以隨時改變所指的對象)。
以下樣本程式中,k被初始化為i的引用。語句k = j並不能將k修改成為j的引用,只是把k的值改變成為6。由於k是i的引用,所以i的值也變成了6。
int i = 5;
int j = 6;
int &k = i;
k = j; // k和i的值都變成了6;
上面的程式看起來象在玩文字遊戲,沒有體現出引用的價值。引用的主要功能是傳遞函數的參數和傳回值。C++語言中,函數的參數和傳回值的傳遞方式有三種:值傳遞、指標傳遞和引用傳遞。
以下是“值傳遞”的樣本程式。由於Func1函數體內的x是外部變數n的一份拷貝,改變x的值不會影響n, 所以n的值仍然是0。
void Func1(int x)
{
x = x + 10;
}
…
int n = 0;
Func1(n);
cout << “n = ” << n << endl; // n = 0
以下是“指標傳遞”的樣本程式。由於Func2函數體內的x是指向外部變數n的指標,改變該指標的內容將導致n的值改變,所以n的值成為10。
void Func2(int *x)
{
(* x) = (* x) + 10;
}
…
int n = 0;
Func2(&n);
cout << “n = ” << n << endl; // n = 10
以下是“引用傳遞”的樣本程式。由於Func3函數體內的x是外部變數n的引用,x和n是同一個東西,改變x等於改變n,所以n的值成為10。
void Func3(int &x)
{
x = x + 10;
}
…
int n = 0;
Func3(n);
cout << “n = ” << n << endl; // n = 10
對比上述三個樣本程式,會發現“引用傳遞”的性質象“指標傳遞”,而書寫方式象“值傳遞”。實際上“引用”可以做的任何事情“指標”也都能夠做,為什麼還要“引用”這東西?
答案是“用適當的工具做恰如其分的工作”。
指標能夠毫無約束地操作記憶體中的如何東西,儘管指標功能強大,但是非常危險。就象一把刀,它可以用來砍樹、裁紙、修指甲、理髮等等,誰敢這樣用?
如果的確只需要借用一下某個對象的“別名”,那麼就用“引用”,而不要用“指標”,以免發生意外。比如說,某人需要一份證明,本來在檔案上蓋上公章的印子就行了,如果把取公章的鑰匙交給他,那麼他就獲得了不該有的權利。
6. && 左右兩邊均為true 時為true , || 左右兩邊一個為true 即為 true.
false 在算術運算式中為0, true 為1.
7. 算術運算優先順序
邏輯運算子號 NOT
算術運算子(*,/,%)
算術運算子(+,-)
相對關聯運算子 (== ,!=)
邏輯運算子號 AND
邏輯運算子號 OR
賦值運算子
8. continue 終止迴圈的current iteration
string word;
const int min_size = 4;
while (cin >>word)
{
if(word.size()<min_size)
//結束此次迭代
continue;
//程式執行到此,則輸入長度都大於或等於 min_size 個字元
process_text(word);
}
9. const int seq_size = 18;
#include<vector>
vector<int>pell_seq(seq_size) // pell_sql 定義為一個vector object,可儲存18個int元素,每個元素初值為0.
for(int ix= 2;ix< seq_size; ++ix)
pell_sql[ix] = pell_seq[ix-2] + 2*pell_seq[ix-1];
// vector 初始化
vector<int>elem_seq(sql_size);
elem_sql[0] = 1;
elem_sql[1] = 2;
或:
利用一個已初始化的array 作為 vector 的初值
int elem_vals[seq_size] = {
1,2,3
};
vector<int>elem_seq(elem_vals,elem_vals+seq_size);
9.初始化:
int ival(1024) 直接初始化 更靈活且效率更高
int ival = 1024 複製初始化
10.extern
extern int i;聲明不是定義
extern int i=65535 ;定義
程式中變數可以聲明多次,但只能定義一次。
11:
引用:
引用必須用與該引用同類型的對象初始化.
int ival = 1024;
int &refVal = ival ; //ok:refVal refers to ival
int &refVal2 ; // error:a reference must be initialized
int &refVal3 = 10 ; // error: initalizer must be an object
const 引用: 指向const對象的引用:
const int ival = 1024 ;
const int &refVal = ival ; //ok:both reference and object are const;
int &ival = ival ;//error:non const reference to a const object;
可以讀取但不能修改refVal;
const 引用可以初始化為不同類型的對象或者初始化為右值。
int i= 42;
const int &r = 42 ; // legar for const references only
cont int &re = r+i;
double dval = 3.14 ;
const int &ri = dval;
編譯器會轉成如下形式:
int temp = dval;
const int &ri = dval;
//非const 引用只能綁定到與該引用同類型的對象。
const 引用則可以綁定到不同但相關的類型的對象或綁定到右值。
12: 枚舉
枚舉成員值可以是不唯一的:
enum Points{point2d = 2, point2w, point3d = 3, point3w};
point2w預設初始化,為3. point3w 為4.
枚舉對象的初始化或賦值,只能通過其枚舉成員或同一枚舉類型的其他對象進行。
Points pt3d = point3d; // ok:point3d is a points enumerator;
Points pt2w = 3 ; // error: pt2w initialized whit int
pt2w = polygon ; // error: polygon is not a Points enumerator
pt2w = pt3d ;// ok: both are objects of points enum type
把3賦給Points 對象是非法的,即使3與一個points枚舉成員相關聯。
13: 類類型
用class 和struct 關鍵字定義類的唯一差別在於預設存取層級,預設情況下,
struct 的成員為public,而class的成員為private;
14: 標頭檔用於聲明而不是用於定義
extern int ival = 10; // initializer, so it's a definition
double fica_rate ; // no extern ,so it's a definition
因為標頭檔包含在多個源檔案中,所以不應該含有變數或函數的定義。
15: 避免多重包含
#ifndef XXX_H
#define XXX_H
#endif
16: string
string 對象的定義和初始化
string s1; //預設建構函式,s1為空白串
string s2(s1); //將s2 初始化為s1的一個副本。
string s3("value"); //將s3初始化為一個字串字面值副本
string s4(n,'c') ; //將s4初始化為字元'c'的n個副本
警告: 字串字面值與標準庫string 類型不是同一種類型.
*和字串字面值的串連:
當進行string 對象和字串字面值混合串連操作時,+操作符的左右運算元必須至少一個是string類型。 string s1 = "hello" ;
string s2 = "world";
string s3 = s1+","; //ok:adding a string and literal
string s4 = "hello"+"," ; //error: no string operand
string s5 = s1+ "," +" world"; //ok:each+ has string operand
string s6 = "hello"+","+s2; //error:cant add string literals ;
*從string 對象擷取字元
string str("some string");
for(string::size_type ix = 0 ;ix != str.size(); ++ix)
cout << str[ix] << endl;
* 下標操作可用作左值
for(string::size_type ix = 0; ix != str.size(); ++ix)
str[ix] = '*';
*cctype 標頭檔中的字元處理
isalnum(c)字母或數字
isalpha(c) 字母
iscntrl(c) 控制字元
isdigit(c) 數字
isgraph(c) 不是空格,但可以列印
islower(c) 小寫字母
isprint(c) 可列印的字元
ispunct(c) 標點符號
isspace(c) 空白字元
isupper(c) 大寫字母
isxdigit(c) 十六進值
tolower(c) 大寫字母,則返回其小寫字母,否則直接返回c
toupper(c) 小寫字母,則返回其大寫字母,否則直接返回c
// 給定對象中標點符號的個數
string s("hello word!!!");
string::size_type punct_cnt = 0;
for(string::size_type index=0; index != s.size();++index)
if(ispunct(s[index]))
++punct_cnt;
cout << punct_cnt;
17: 標準庫vector 類型
vector(容器)是同一種類型的對象的集合,每個對象都有一個對應的整數索引值。
vector不是一種資料類型,而只是一個類模板,可以來定義和初始化vector對象。
幾種初始化vector對象的方式
vector<T> v1; vector保持類型為T的對象,預設建構函式v1為空白。
vector<T> v2(v1) ; v2 是v1的一個副本。
vector<T> v3(n,i); v3包含n個值為i的元素.
vector<T> v4(n) ; v4含有值初始化的元素的n個副本。
1.建立確定個數的元素。
如果複製,兩個vector必須儲存同一種元素類型
vector<int> ivec1 ; // ivec holds objects of type int
vector<int> ivec2(ivec1);//ok:copy elements of ivec1 into ivec2
vector<string> svec(ivec1); //error:svec holds strings, not ints
vector<int> ivec4(10,-1); // 10 elements ,each initialized to -1
vector<string> svec(10,"hi!"); // strings, each initialized to "hi"
2.值初始化
vector<int> fvec(10); // 10 elements ,each initialized to 0
vector<string> svec(10);// 10 elements, each an empty string
vector 對象的操作
1.vector 對象的size
vector<int>::size_type //ok
vector::size_type // erro
size_type:必須指出該類型是在那定義的.
2. 向vector 添加元素
string word;
vector<string> text;
while( cin >> word)
{
text.push_back(word);
}
3. vector 的下標操作
for(vector<int>::size_type ix = 0;ix != ivec.size(); ++ix)
ivec[ix] = 0;
4. 下標操作不添加元素
vector<int> ivec;
for( vector<int>::size_type ix = 0 ; ix != 10 ; ++ix)
ivec[ix] = ix;
下標操作只能擷取已存在的元素
正確寫法:
for( vector<int>::size_type ix = 0 ; ix != 10 ; ++ix)
ivec.push_back(ix);
警告:只能對確知已存在的元素進行下標操作
vector<int> ivec; //empty vector
cout<< ivec[0] //error: ivec has no elements !
vector<int> ivec2(10); //vector with 10 elements
cout<< ivec[10] ; //error: ivec has elements 0...9
18. 迭代器簡介
1. 容器的iterator 類型
vector<int>::iterator iter;
2. begin 和end 操作
vector<int>::iterator iter = ivec.begin();
3. 解引用運算
迭代器類型可以使用解引用操作符(*操作符)來訪問迭代器所指向的元素。
*iter = 0;
4. 樣本
下標操作法:
for( vector<int>::size_type ix = 0 ; ix != ivec.size() ; ++ix)
ivec[ix] = 0;
迭代器:
for(vector<int>::iterator iter = ivec.begin(); iter !=ivec.end();++iter)
*iter = 0; 5. const_iterator
5.唯讀容器內元素。
for(vector<string>::const_iterator iter = text.begin(); iter != text.end(); ++iter)
cout<< *iter <<endl;
6. 不要把const_iterator 對象與const 的iterator 對象混淆起來,聲明一個const 迭代器時,必須初始化迭代器,一但初始化,就不能改變它的值。
vector<int> nums(10) ;// nums is nonconst;
const vector<int>::iterator cit = nums.begin();
*cit = 1; // ok: cit can change its underlying element
++cit; // error: can't change the value of cit
7. const_iterator 對象可以用於const vector 或非const vector.
const vector<int> nines(10,9); // cannot change elements in nines
// error: cit2 could change the element it refers to and nines is const
const vector<int>::iterator cit2 = nines.begin();
//ok: it can't change an element value , so it can be used with a const vector<int>
vector<int>::const_iterator it = nines.begin();
*it = 10 ; // error:*it is const ;
++it; // ok: it isn't const so we can change its value
註解:
//an iterator that cannot write elements
vector<int>::const_iterator
// an iterator whose value cannot change
const vector<int>::iterator
8. 迭代器的算術運算
1. iter + n
iter - n
位置在所指元素之前或之後(+,-)n個元素的位置。
2. iter1 - iter2 difference_type 的signed 類型的值.
定位於 vector 的中間元素
vector<int>::iterator mid = vi.begin()+ vi.size()/2
任何改變vector長度的操作都會使已存在的迭代器失效,列如,在調用push_back 之後,就不能再信指向vector的迭代器的值了。
19. 標準庫 bitset 類型
處理二進值位的有序集。
#include<bitset>
using std::bitset;
1.定義和初始化
bitset<32> bitvec ; // 32bits ,all zero
bitset<n> b ; // b有n位,每位都為0
bitset<n> b(u) ; //b是unsigned long 型u的一個副本
bitset<n> b(s); //b是string對象s中含有的位串的副本
bitset<n> b(s,pos,n) //b是s中從位置pos開始的n個位的副本
2.用unsigned 值初始化bitset對象
當用unsigned long 值作為bitset對象的初始值時,該值將轉化為二進值的位元模式。而bitset對象中的位集作為這種模式的副本.如果bitset長度大於unsigned long 值的二進位位,則其餘高階位將置為0,如果小於,則只用低階位,超過bitset的高階位將被丟棄。
十六進位0Xffff 表示為二進位就是十六個1和十六個0(每個0xf可表示為1111),可以用0xffff初始化bitset對象。
//bitvec1 is smaller than the initializer
bitset<16> bitvec1(0xffff); // bits 0...15 are set to 1
//bitvec2 same size as initializer
bitset<32> bitvec2(0xfffff); // bits 0...15 are set to ; 16...31 are 0
// on a 32-bit machine ,bits 0 to 31 initialized from 0xffff
bitset<128> bitvec3(0xfffff);//bits 32 through 127 initialize to zero
3.用string 對象初始化bitset對象
從string讀入位集的順序從右向左。
string strval("1100");
bitset<32> bitvec4(strval);
bitvec4 的位元模式中第2和3的位置為1,其餘為0。如果string 對象的字元個數小於bitset類型長度,則高階位將置為0 .
string str("111111000011110000");
bitset<32> bitvec5(str,5,4);// 4 bits starting at str[5],1100
bitset<32> bitvec6(str,str.size()-4);// use last 4 characters
4. bitset 對象上的操作
b.any() 是否存在置為1的二進位
b.none() 不存在置為1的二進位位嗎?
b.count() 置為1的二進位位的個數
b.size() 二進位的個數
b[pos] 訪問b中在pos處的二進位位
b.test[pos] b中在pos處的二進位位是否為1
b.set() 所有二進位位都設定為1
b.set(pos) 把b中在pos 處二進位置為1
b.reset() 把b中所有二進位都置為0
b.reset(pos) 把b中在pos 處的二進位位置為0
b.flip() 二進位逐位取反
b.flip(pos) pos 處取反
b.to_ulong() 用b中同樣的二進位返回一個unsingned long 值
os << b 把b中的位集輸出到 os 流
數組和指標