C++檔案
例:從檔案income. in中讀入收入直到檔案結束,
並將收入和稅金輸出到檔案tax. out。
#include<iostream>using namespace std;const int cutoff = 6000;const float rate1 = 0.3;const float rate2 = 0.6;int main(){ ifstream infile; ofstream outfile; int income,tax; infile.open("income.in") outfile.open("tax.out") while( infile>>income){ if( income<cutoff) tax = rate1 * income; else tax = rate2 * income; outfile<< "Income = "<< income << "greenbacks\n" << "Tax = " << tax << "greenbacks\n"; } infile.close(); outfile.close(); return 0;}
檢查檔案是否成功開啟
ifstream infile;infile.open("scores.dat")if (infile) //...
#include <iostream>#include <fstream>#include <iomanip>using namespace std;int main(){ ifstream infile; ofstream outfile; infile.open("in.txt"); outfile.open("out.txt"); int num1,num2,num3=0; if(infile && outfile) { while(infile >> num1 >> num2 >> num3){ outfile << setw(2)<< num1<<" "<<num2<<" "<<num3<<" "<<num1+num2+num3<<endl; } } infile.close(); outfile.close(); return 0;}
常量
C++中的const變數能在任何常數可以出現的地方使用,例如數組的大小、case標號中的運算式。
const int Size = 100;float a[Size];
bool data type
C++新增bool類型,取值true 或false。用來表示真假。
所有的關係操作符、相等操作符和邏輯操作符現在都
產生bool類型的結果值,而不是int型。
在需要bool類型的地方,整數和指標運算式仍然是允
許的
預設情況下,bool運算式輸出時真值輸出1,假值輸出0.
操作符boolalpha可用來將bool運算式輸出或輸入為false 或true的形式。
操作符noboolalpha可用來將bool運算式輸出或輸入0或1的形式。
bool flag;flag = (3<5);cout<<flag<<'\n';cout<<boolalpha<<flag<<'\n';1true
Structure
C++中的結構體和C語言結構體不同。定義結構體變數時可以不加struct關鍵字
struct Point{ double x,y;};Point p1,p2;
C++中的結構體除了包含資料成員,還可以包含函數。
struct Point{ double x,y; void setVal(double,double);};p.x = 3.14159;p.y = 0.0;p.setVal(4.11,-13.090);
在C++中,類和結構的唯一區別是預設情況下,結構中的所有東西都是Public而類中的所有東西都是Private的. string 類型
C++提供string類型來替代C語言中以null為結尾的char數組。
使用string類型必須包含標頭檔string
有了string類型,程式員不再需要關心儲存的分配,也無需處理複雜的null結束字元,這些操作將由系統自動處理。
執行個體:
#include<string>using namespace std;string s1;string s2="Bravo";string s3=s2;string s4(10,'x');
變數s1,已經定義但沒有進行初始化, 預設值為空白串
變數s2的初始值是C風格的字串“Bravo”
變數s3用s2初始化,因此s2和s3都代表字串Bravo
變數s4的初始化為10個x。
轉換為C風格的字串:利用函數c_str返回一個指向char類型的數組的指標
執行個體:
存放輸入檔案名稱的變數filename的資料類型是string
調用ifstream的open函數時,需要一個C風格的字串
string filename = "infile.dat";ifstream infile;infile.open( filename.c_str() );
求字串長度,使用函數length
string s = "Ed Wood";cout << "Length = " << s.length() <<'\n';
輸出為Length = 7.
string的輸入輸出
<<用來輸出string類型的字串
string s1;string s2 = "Bravo";string s3 = s2;string s4(10, 'x');cout<<s1<<'\n' <<s2<<'\n' <<s3<<'\n' <<s4<<'\n';輸出為BravoBravoxxxxxxxxxx
用來輸入string類型的字串,其預設的動作是忽略空格,然後讀取儲存字元直到檔案結束或遇到另外一個空格。任何空格都不儲存。
string s;cout << "Enter a string:";cin >>s;輸入Ed Wood
則s的內容為Ed。
注意:在定義後,s實際上長度為0。在讀入字串Ed後,它的長度為2。系統自動提供了充足的儲存空間來儲存這個長度為2的字串。
函數getline:用來讀入一整行到string類型的變數中去。第一個參數是輸入資料流,第二個參數是string類型的變數。
該函數從輸入資料流中讀入字元,然後將它們儲存到string變數中,直到出現以下情況為止:
讀入了檔案結束標誌。
都到了一個新行,該新行將從流中移除,但沒有儲存到變數中。
到達字串的最大長度允許值。
如果getline沒有讀入字元,它將返回false,該條件可用於判斷檔案是否結束以終止應用程式
執行個體:
#include<iostream>#include<fstream>#include<string>using namespace std;int main(){ string buff; ifstream infile; ofstream outfile; cout<<"Input file name:"; cin>>buff; infile.open(buff.c_str()); cout<<"Output file name:"; cin>>buff; outfile.open(buff.c_str()); while(getline(infile,buff)) outfile<<buff<<"\n\n"; infile.close(); outfile.close(); return 0;}
輸出資訊的行距是輸入資訊行距的兩倍。
賦值:操作符=可用來進行string類型字串的賦值
操作符左邊必須是一個string類型的字串,右邊可以是一個string字串,也可以是C風格的字串或僅僅是一個char字元。
字串的串連:操作符+和+=可用來進行字串的串連。
操作符+
string + string
string + “xxxxx” 或 “xxxxx” + string
string + ‘x’ 或 ‘x’ + string
而+=,則左邊必須是string字串,右邊可以是一個
string字串、C風格的字串或一個char字元。
–string += string
–string += “xxxx”
–string += ‘x’
函數
應用:通過&來標記,用來為儲存空間提供別名
int x;int &ref = x;//分配了一個int單元,它擁有兩個名字:x和refx=3;或ref=3;都將3存到int單元
C++預設的調用方式和C語言一樣,都是傳值調用。如果用&指定一個函數參數為引用參數,則為引用調用,引用參數將實際的實參傳給函數,而不是實參的一個拷貝。
#include<iostream>using namespace std;void swap(int &,int &);int main(){ int i=7,j=-3; swap(i,j); cout<<"i="<<i<<'\n' <<"j="<<j<<'\n'; return 0;}void swap(int &a,int &b){ int t; t = a; a = b; b = t;}
函數原型 void swap(int &,int &)指定swap的參數是通過引用傳遞的。
在swap被調用後,swap函數體中的a和b直接對應main函數中的i和j的儲存空間。函數swap並不是對i,j的拷貝進行操作,而是直接操作i和j本身。
函數重載:函數名相同,參數個數或參數類型不一樣。
重載函數通常用來對具有相似行為而資料類型不同的操作提供一個通用的名稱。編譯器通過將實參類型與同名函數的參數表進行匹配,以決定應該調用哪個函數。
#include<iostream>#include<iomanip>using namespace std;void print(int a);void print(double a);int main(){ int x = 8; double y = 8; print(x); print(y); return 0;}void print(int a){ cout<<a<<'\n';}void print(double a){ cout<<showpoint<<a<<'\n';}
函數簽名:C++要求重載的函數具有不同的簽名。
函數簽名包括:函數名。參數的個數、資料的類型和順序。
為保證函數的唯一性,函數必須擁有獨一無二的簽名。
傳回值類型不是函數簽名的一部分,所以函數不能通過傳回值類型加以區分。 new和delete操作符
new new[] delete 和 delete[]操作符用於動態分配和釋放儲存空間(例如程式啟動並執行時候)
操作符new分配一個空間;
new[]分配一個數組;
delete釋放由new分配的單一空間;
delete[]釋放由new[]分配的數組;
與C語言中的函數malloc calloc 和free不同
new、new[] delete delete[]是內建的操作符,而malloc calloc free是庫函數。new和delete是關鍵字
new操作符根據請求分配的類型推斷傳回型別和需要分配的位元組數。
給定聲明
int *int_ptr;
通常使用如下方式為int_ptr分配儲存空間;
int_ptr = new int;
如果分配成功,則int_ptr指向所分配的儲存單元。
delete操作符用於釋放由new分配的儲存空間。如果int_ptr指向一個由new分配的單一int單元,則可以這樣釋放它
delete int_ptr;
new[]操作符用於動態分配一個數組
int *int_ptr;int_ptr = new int[100];//請求分配100個int類型單元
如果分配成功,則int_ptr指向第一個int單元的地址
delete[]操作符用於釋放由new[]分配的儲存空間。如果int_ptr指向一個由new[]分配的int數組單元,則我們可以這樣釋放它:
delete [] int_ptr;
類
C++中,一個類就是一種資料類型。
標準C++定義了一些內建類,例如string
通過建立自己的類,程式員可以對C++語言進行擴充。
通過類聲明可以建立一個類,而且可將這個類當作資料類型來使用。
類和對象
類聲明:描述了封裝在該類中的資料成員和成員函數。
class Human{ ///...data members and methods go here};
class是個關鍵字,Human稱為類標籤
通過類聲明建立一個資料類型,類標籤是該資料類型的標識符或名字。
類聲明中花括弧後的分號不可少。
對象定義:從物件導向程式設計角度看,C++中以一個類作為資料類型定義的變數就是對象。
Human maryLeakey;// 如下語句定義了Human的一個對象maryLeakey
對象數組 Human latvians[365000];
C++的資訊隱藏機制
三個關鍵字:
private:可用來隱藏類的資料成員和成員函數
public:用來暴露類的資料成員和成員函數
protected
物件導向設計的靈魂就是使用private隱藏類的實現,使用public暴露類的介面。
定義一個Person類
介面:包含兩個公有成員函數setAge和getAge
實現:一個unsigned 類型的資料成員age
class Person{ public: void setAge(unsigned n); unsigned getAge() const; private: unsigned age;};
private成員和public成員可以在類聲明中交叉出現。
Person類的客戶(指Person類的對象的使用者)可通過調用公有成員函數setAge和getAge來請求Person類提供服務
Person類的客戶不能訪問屬於類實現部分的私人資料成員age
成員選擇符
class Person{ public: void setAge(unsigned n); unsigned getAge() const; private: unsigned age;};int main(){ Person boxer; boxer.setAge(27); //...remainder of main's body
對象的使用者只能訪問類的公有成員(資料成員或成員函數)
類範圍
類的私人成員僅能由類的成員函數訪問,即具有類範圍性質。
類的公有成員擁有公有範圍性質,可以在類之外進行訪問。
在C++中,用關鍵字class聲明的類,其類成員在預設情況下作為私人成員處理,具有類範圍性質。
關鍵字class和struct的區別
使用class關鍵字或struct關鍵字都可以建立類
如果使用class關鍵字,類成員在預設狀態下是私人的。
而是用struct關鍵字,類成員在預設狀態下是公有的。 類成員函數定義
在類聲明之外定義、在類聲明之中進行定義(inline)
class Person{public: void setAge(unsigned n); unsigned getAge() const;private: unsigned age;};//define Person's setAgevoid Person::setAge(unsigned n){ age = n;}//define Person's getAgeunsigned Person::getAge() const{ return age;}
在類聲明之外進行定義,為避免重名,在定義成員函數時使用了域解析符::
在類聲明之中進行定義(inline)
class Person{public: void setAge(unsigned n){ age = n;} unsigned getAge() const{return age;}private: unsigned age;};
通過在進行成員函式宣告的時候使用inline關鍵字,可將原本定義在類聲明之外的成員函數強制變成內嵌函式。
class Person{public: inline void setAge(unsigned n); inline unsigned getAge() const;private: unsigned age;};//define Person's setAgevoid Person::setAge(unsigned n){ age = n;}//define Person's getAgeunsigned Person::getAge() const{ return age;}
在程式中使用類
關鍵步驟:類聲明,對象定義,客戶服務請求
#include<iostream>using namespace std;class Person{public: void setAge(unsigned n){age = n;} unsigned getAge() const{ return age;}private: unsigned age;};int main(){ Person p; //create a single person Person stooges[3]; //create an array of Persons p.setAge(12); //set the stooges' age stooges[0].setAge(45); stooges[1].setAge(46); stooges[2].setAge(44); //print four ages cout<<p.getAge()<<'\n'; for(int i=0;i<3;i++) cout << stooges[i].getAge() << '\n'; return 0;}
在程式中使用類
通常將類聲明放到.h中,這樣在使用時通過#include將類聲明包含進來。
如可將Person類的聲明放到person.h檔案中
通常將成員函數的定義放到.cpp中
一般不要將成員函數定義放在.h中,因為標頭檔通過#include被多個不同的檔案所包含的話可能出現函數重複定義錯
執行個體程式:堆棧類
問題:建立一個支援int型的壓入和彈出操作的堆棧類。
公有成員:
對stack對象進行初始化。
檢查stack為空白,或已滿。
將整數壓入到stack中。
從stack裡彈出整數。
不移出任何元素,將stack的內容輸出到標準輸出。
私人成員:
一個用於列印錯誤資訊的私人成員函數。
三個私人資料成員(top、資料數組、dummy_val)
#include<iostream>using namespace std;class Stack{public: enum{ MaxStack = 5}; void init(){ top = -1;} void push(int n) { if(isFull()){ errMsg("Full stack. Can't push."); return; } arr[++top] = n; } int pop() { if( isEmpty()){ errMsg("Empty stack. Popping dummy value."); return dummy_val; } return arr[top--]; } bool isEmpty(){ return top<0;} bool isFull() { return top>=MaxStack -1;} void dump() { cout<<"Stack contents, top to bottom:\n"; for(int i=top;i>=0;i--) cout<<'\t'<<arr[i]<<'\n'; }private: void errMsg(const char* msg) const{ cerr<< "\n*** Stack operation failure:"<<msg<<'\n'; } int top; int arr[MaxStack]; int dummy_val;};int main(){ Stack s1; s1.init(); s1.push(9); s1.push(4); s1.dump(); // 4 9 cout << "Popping"<<s1.pop()<<'\n'; s1.dump(); //9 s1.push(8); s1.dump(); // 8 9 s1.pop();s1.pop(); s1.dump();//empty s1.pop();//still empty s1.dump(); //ditto s1.push(3); s1.push(5); s1.dump();//5 3 //push two too manny to test for(unsigned i = 0;i<Stack::MaxStack;i++) s1.push(1); s1.dump(); //1 1 1 5 3 return 0;}
效率和健壯性
- 通過引用來傳遞和返回對象
- const型別參數的對象引用
- const成員函數
- 對成員函數進行重載以便處理兩種類型的字串
通過引用來傳遞和返回對象
對象可以採用傳值方式或引用方式進行對象的傳遞和返回。一般來說應該採用引用方式進行對象的傳遞和返回,而不要採用傳值的方式來進行。因為通過傳值方式來傳遞和返回對象時會降低效率並將面臨對象間的拷貝操作,從而使資料增大,浪費記憶體。
從效率上看,傳遞一個指向對象的指標可收到與引用方式相同的效果,但引用方式的文法要簡練得多。
#include<iostream>using namespace std;class C{public: void set(int n){num = n;} int get() const{return num;}private: int num;};void f(C&);C& g();int main(){ C c1,c2; f(c1); // pass by reference c2 = g(); // return by reference cout<< c2.get() <<'\n'; return 0;}void f(C& c){ c.set(-999); cout<<c.get()<<'\n';}C& g(){ static C c3;// NB:static, not auto c3.set(123); return c3;}output:-999123
const型別參數的對象引用
通常,如果一個對象通過引用方式傳到函數f中,而函數f又不會通過修改對象的資料成員的值改變該對象的狀態,那麼,最好將f的參數標記為const,可以預防對參數的誤寫,同時有些編譯器還可對這種情況進行一些最佳化。
如下例:將函數setName的string型別參數n標記為const,表明setName不會改變n,只是將n賦值給資料成員name。
class C{public: void setName(const string& n) {name = n;} // ... other public membersprivate: string name;};
const成員函數
如果一個成員函數不需要直接或間接(通過調用其它的成員函數來改變其對象狀態)地改變該函數所屬對象的任何資料成員,那麼最好將這個成員函數標記為const。
如下例,由於get成員函數不需要改變類C的任何資料成員,因此將get成員函數標記為const。
class C{public: void set(int n) {num = n;} int get() const {return num;}private: int num;};
const 成員函數
定義一個const成員函數時,const關鍵字出現在參數列表與其函數體之間。
由於get成員函數不更改任何資料成員,因此這種類型的函數被稱為唯讀函數。將成員函數標記為const可以預防對該函數所屬對象的資料成員的誤寫,同時有些編譯器還可對這種情況進行一些最佳化。
一個const成員函數僅能調用其它const成員函數,因為const成員函數不允許直接或間接地改變對象的狀態,而調用非const成員函數可能會間接改變對象的狀態
class C{public: void m1(int x) const{ m2(x); //*** error: m2 not const } void m2(int x) { dm = x;}private: int dm;};
const成員函數
const關鍵字三種不同用法樣本:
- 在成員函數set中,因為set不該變string型別參數n,n被標為const。
- 成員函數get返回資料成員name的一個const型引用,此處的const表明誰也不能通過這個引用來修改資料成員name的值。
- 成員函數get本身被標記為const,因為get不會改變類C唯一的資料成員name的值。
class C{public: void set( const string& n) {name = n;} const string& get() const{ return name;}private: string name;};
const返回
- 某函數如果採用const返回,則其傳回值只能賦給一個const類型的局部變數。
- 如果該const傳回值是一個類的指標或者引用的話,則不能用該指標或引用調用該類的non-const成員函數,因為這些函數可能會改變該類的資料成員的值。
class Foo{public: /* * Modifies m_widget and the user may modify the reurned widget. */ Widget *widget(); /* * Does not modify m_widget but the user may modify the returned widget. */ Widget *widget() const; /* * Modifies m_widget, but the user may not modify the returned widget. */ const Widget *cWidget(); /* * Does not modify m_widget and the user may not modify the returned widget. */ const Widget *cWidget() const;private: Widget *m_widget;};int main(){ Foo f; Widget *w1 = f.widge