C++學習筆記

2020-08-11 22:19:17

C++ 第一天*********
1.簡介:
80年代 本賈尼、
83年正式命名 、
92年微軟制定了C++標準、IBM
98年ANSI C++ ISO C++98
03年C++03
11年ISO c++0x
c和c++的關係 c是c++的基礎。c++對型別檢查比c要嚴格。c++是強型別的語言。
c++擴充套件了c
物件導向 (一類的方式 分析和組織程式碼 )
運算子過載 (一種函數的特殊表現形式)
異常 (一種新的錯誤處理方式)
泛型程式設計 (型別通用程式設計)
1.3 c++ 的課程安排
c語言到c++的過渡
二、 第一個c++程式
2.1 原始碼的後綴名 .c .cc .C .cxx .c++ 最好是.cpp
2.2 c++ 程式中的標頭檔案的問題
c++ 不再以.h 爲標頭檔案的結尾。
/user/inlcude/c++/4.6
#include
#include
#include
可以使用c的標準標頭檔案 (不推薦)
#include <stdio.h>
#include <string.h>
推薦這樣使用
#include
#include
非標準的(系統的)不能使用去尾加頭的方式,
#include <pthread.h>
2.3輸入輸出 cout <<
cin >>

2.4 編譯器
gcc 要加 -lstdc++
最好使用 g++ gcc 和g++幾乎一樣。
g++ -o -S -E -l -L -O1 -g -c -I

3.1 爲什麼有名稱空間 namespace 可以對程式進行邏輯劃分 避免命名衝突。
3.2 如何來定義名稱空間。
namespace 空間名{
變數定義;
函數宣告;
函數實現;
}
同一個名稱空間可以出現多次。

1 #include
2 using namespace std;
3 namespace IBM{
4 int age;
5 void show(){
6 cout<<「IBM age is」<<age<<endl;
7 }
8 }
9 namespace tarena{
10 int age;
11 void show();
12 }
13 namespace tarena{
14 void show(){
15 cout<<「tarena age is」<< age<<endl;
16 }
17 }
18 int main(){
19
20 return 0;
21 }

3.3如何使用名稱空間.
3.3.1 在數據前加名稱空間名::(作用域運算子)
1、--------------------------------------------
19 int main(){
20 IBM::age=51;
21 IBM::show();
22 tarena::age=15;
23 tarena::show();
24 return 0;
25 }

3.3.2 使用using 申明
using 空間名::數據;
把名稱空間下的數據在當前宣告 這樣就可以在當前的作用域直接使用。
使用using可能會產出衝突,回到第一種解決方案。

using IBM::age;

21 using IBM::show;
22 //using tarena::age;
23 //using tarena::show;
24 int main(){
25 age=52;
26 show();
27 cout<<age<<endl;
28 tarena::age=10;
29 tarena::show();
30
31 return 0;
32 }

3.3.3使用名稱空間指令
using namespace 空間名;
fg: using namespace IBM;
using namespace tarena;

oracle10g
oracle10g
sqlplus ‘/as sysdba’
start

3.4 無名名稱空間。
如果一個名稱空間,沒有定義在任何名稱空間中,則會自動放入無名名稱空間。
::數據 ;
namespace {
數據
/不能跨檔案存取/
}

1 #include
2 using namespace std;
3 int age=100;
4 namespace {
5 double salary=10000;
6 }
7
8 int main(){
9 cout << age <<":"<< age << endl;
10 cout << ::age << endl;
11 cout << salary << 「:」<< ::salary << endl;
12 return 0;
13 }

3.5 名稱空間巢狀

1 #include
2 using namespace std;
3 namespace ns1{
4 int count=1;
5 namespace ns2{
6 int count =10;
7 namespace ns3{
8 double salary=100;
9 void show(){
10 cout << salary << endl;
11 cout << ns1::count<< endl;
12 cout << ns1::ns2::count<<endl;
13 }
14 }
15 }
16 }
namespace ns4=ns1::ns2::ns3;
17 int main(){
18 cout<< ns1::ns2::count<<endl;
19 ns1::ns2::ns3::show();
ns4::show();
20 return 0;
21 }

四 c++中的結構 聯合 列舉
4.1 定義結構體的語法和c完全相同。
區別在定義變數時,可以省略struct 關鍵字
c++中的結構體可以定義函數(c語言結構體中不能定義函數。可以定義函數指針)
這些函數稱爲成員函數。
4.2 聯合體
共用記憶體
突破編譯器的限制。
定義 聯合體的語法 和c完全相同。
在定義變數時 可以省略union 關鍵字
C++ 支援匿名聯合體
union {
int x ;
char data[4];
};

1 #include
2 using namespace std;
3 int main(){
4 union{
5 int x;
6 char date[4];
7 };
8 /*48 代表0 , A 65, a 97, */
9 x=0x31323334;
10 x=0x41424344;//小端序 低位在前 高位在後
11 cout<<date[0]<<date[1]<<date[2]<<date[3]<<endl;
12 for(int i=0;i<=3;i++)
13 cout<<date[i];
14 cout <<endl;
15 }

4.3 列舉
定義列舉型別 和 C 完全相同
定義變數的 可以省略 enum 關鍵字
列舉值 能賦值給整數變數,整數不能賦值給列舉。(c中允許整數賦值給列舉)
這裏體現了C++的強型別。

1 #include
2 using namespace std;
3 enum Direction{D_UP,D_DOWN,D_RIGT,D_LEFT};
4 int main(void){
5 Direction dire=D_DOWN;
6 int var_v=dire;
7 cout << var_v <<endl;
8 dire=D_RIGT;// 改變向右
9 return 0;
10 }


五 C++ 中的bool 型別
c99 #include <stdbool.h>
c++ 中直接支援布爾型別 true false
bool 可以賦值任何值,但下面 下麪的四個值表示假
0 ‘\0’ null false 四大假 其他都是真

1 #include
2 using namespace std;
3 int main(){
4 bool flag=true;
5 cout<<flag<<endl;
6 if(flag){
7 cout <<"flag is true "<<endl;
8 }
9 return 0;
10 }

六.c++中的符號替換。
a&&b a and b

1 %:include
2 using namespace std;
3 int main() <%
4 int age=100;
5 cout<<bitand age<<endl;
6 %>

七、c++中函數
C++ 中函數 無參代表沒有任何型別的參數。預設的void。
c++呼叫前必須前置宣告 不支援c語言中的隱式宣告。(extren int gg 宣告)
c++中 函數預設不在返回int,函數必須設定返回值,如果沒有返回值型別
則設定成void。(main函數除外)

1 #include
2 using namespace std;
3 int show(/void/){//必須加函數的型別
4 cout<<「there is show()」<<endl;
5 }// 註釋中不能巢狀註釋
6 int main(){
7 show();// show(1); 錯誤
8 show();
9 return 0;
10 }

八、函數過載(overload)
8.1 概念
在同一作用域中函數名相同,參數列表不同的函數構成過載關係。
參數列表不同的含義: 參數的型別不同,參數的個數不同 ,參數的順序不同 。
8.2 舉例

1 #include
2 using namespace std;
3 int add(int x,int y){
4
5 cout<<「add(int x,int y)」<<endl;
6 return x+y;
7 }
8 double add(int x,double y){
9 cout <<「add(int x,double y)」<<endl;
10 return x+y;
11 }
12 double add(double x,double y){
13 cout<<「add(double x,double y)」<<endl;
14 return x+y;
15
16 }
17 double add(double x,int y){
18 cout<<「add(doube x,int y)」<<endl;
19 return x+y;
20 }
21 int main(){
22 cout<<add(1,1.1)<<endl;;
23 cout<<add(1.1,1.1)<<endl;
24 cout<<add(2.1,1)<<endl;
25 cout<<add(1,2)<<endl;

//五月七日第二天*

8.2用函數指針呼叫這些函數,函數指針會根據函數的型別來確定函數地址。
double add(int x,double y)
int add(int x,int y)
double add(double x,int y)

double(*pfunc)(int x,double y);

    double (*padd)(int x,double y);

29 padd=add;//根據函數指針的型別來呼叫
30 padd(1,1.2);

8.4 過載的原理:
c的函數在編譯時,編譯器只考慮函數的名字。
C++的函數 進行編譯時,生成的函數名 不但要考慮函數名,還要考慮參數列表。

-----------------跨編譯器呼叫 cfun.c 和callfunc.cpp-------------
vi cfun.c
1 #include <stdio.h>
2 int getmax(int x,int y){
3 printf(「you call function!\n」);
4 return x>y?x:y;
5 }

vi callfun.cpp

1 #include
2 using namespace std;
3 extern 「C」 int getmax(int x,int y); // extern 「C」 按照C的方式編譯
4 int main(){
5
6 cout << getmax(100,200)<<endl;
7
8 }

1 #include
2
3 using namespace std;
4 extern 「C」 int getmax(int x,int y);
5 extern 「C」 void memcpy(void dext,const void* src,size_t size);
6 int main(){ //extern 「C」 按照C的方式編譯
7
8 cout << getmax(100,200)<<endl;
9 int x=100;
10 int y=10000;
11 memcpy(&x,&y,4);
12 }

九、
9.1 概念
一個函數的參數只有型別 沒有形參名 這個參數稱之爲啞元。
9.2 作用
void show(void)
void encode(int key);
void decode(int ) //這個參數稱之爲啞元 向前相容

函數區分.
Date date;
date++;
Date& operator++(){
/這個函數代表前++/
}
date++;
Date& operator++(int){
/這個函數代表後++/
}
十、函數參數的預設值
10.1 如果一個函數的參數指定了預設值,則呼叫這個函數,不給這個參數賦值就使用預設值,如果這個參數賦值則代替掉預設值。

int getmax(int x,int y=1223,int z=100);//一旦出現預設值,右邊必須都出現預設值。
getmax(200);
getmax(1);
getmax(1,2);
優點:減少函數的個數,方便呼叫者呼叫.
10.3 設計一個函數 列印整數陣列 預設列印 前五個數據 可以指定數據的分割分號,預設以逗號分割。

1 #include
2 using namespace std;
3 void func(int arr[],int size, char a=’,’){
4 int i=0;
5 for(i=0;i<size-1;i++)
6 cout<<arr[i]<<a;
7 cout<<arr[i];
8 cout<<endl;
9
10 }
11 int main(){
12 int arr[]={1,2,3,4,5,6,7,8,9};
13 func(arr,9);
14 return 0;
15 }

參數的預設值 不要和過載構成衝突。

十一:行內函式
11.1 行內函式在函數的呼叫的位置,直接把函數的程式碼複製過去。
11.2 inline void getma(int x,int y){}
行內函式在呼叫時,省了開棧和清理棧的時間開銷,提高了程式碼的效率,但是會造成了
程式碼的體積變大,記憶體消耗大。(空間換時間)
函數體積大 稀少呼叫 不用內聯
遞回函數無法實現內聯。
函數體積小,頻繁呼叫 適合內聯
內聯只是給編譯器的一個建議,建議成功則是則是內聯形式呼叫,內聯不成功則是普通形式函數呼叫。

十二: C++中的動態記憶體分配
12.1 c中的動態記憶體分配
malloc calloc relloc free
C++ 中使用的
new delete new[] delete[]
12.2 語法
型別指針名 /* 申請一個大小的記憶體 */
型別 * 指針名=new 型別;
型別 * 指針名=new 型別(值);

/釋放指針對應的記憶體/
delete 指針名;

1 #include
2 using namespace std;
3 int main(){
4 int *pi=new int ;
5 cout <<*pi<<endl;
6 int *p = new int(4);
7 cout <<*p<<endl;
int *pi1=new int;
8 *pi1=1000;
9 cout<<*pi1<<endl;
10 delete pi;// 使的記憶體變爲自由記憶體,能夠被其他的new 分配。
11 pi=NULL; // 數據變沒變 是由編譯器決定。
12 delete p; p=NULL;
13 delete pi1; p=NULL;
14 }

12.3寫一個結構體Date 型別(年 月 日 ) 然後給這塊記憶體賦值 並輸出這塊記憶體中的值,然後釋放。

1 #include
#include
2 using namespace std;
3 struct Date{
4 int year;
5 int month;
6 int date;
7 void show(){
8 //cout<<year<<"-"<<month<<"-"<<date<<endl;
cout<<setfill(‘0’)<<setw(4)<<year<<setw(2)<<month<<setw(2)<<day<<endl;
9 }
10
11 };
12 int main(){
13 Date *pdate=new Date(); //Date *pdate=new Date; 都可以
14 pdate->year=1990;
15 pdate->month=9;
16 pdate->date=10;
17 pdate->show();
18
19 }

12.4 使用new[] 和 delete[] 申請和釋放多個物件所佔的記憶體。
型別名 * 指針名 = new 型別名[n];
delete[] 指針名;

申請5個整數的記憶體空間 然後操作這塊記憶體,最後釋放。

1 #include
2 using namespace std;
3 int main(){
4 int *pa=new int[5];
5 pa[1]=1000;
6 pa[2]=200;
7 pa[4]=400;
8 pa[3]=300;
9 pa[0]=500;
10 for(int i=0;i<5;i++)
11 cout<<pa[i]<<" ";
delete[] pa;
12 cout <<endl;
13 }

12.5 定位記憶體分配。 不用delete釋放。(瞭解)
14 /定位記憶體分配/
15 char cdata[100]; //棧區
16 int pia=new(cdata)int[25];//使得指向棧的記憶體(在已有的記憶體上分配)
17 cout<<(void
)cdata<<endl;
18 cout<<pia<<endl;

十三、參照 reference
13.1 概念:
參照:就是別名。
13.2 語法:
int a=100;
int& int型的參照;
int& ra=a;參照必須初始化;
參照一旦初始化,參照的物件終生不能修改。

1 #include
2 using namespace std;
3 int main(){
4 int a=100;
5 /定義一個a 的參照/
6 int& ra=a;//參照必須初始化
7 ra =1000;
8 cout<<ra<<endl;
9 }

13.3參照傳遞作爲函數傳遞(參照傳遞)
值傳遞
swap(int x,int y,)
grap(int *x,int y);
參照傳遞 :
swap (int&x ,int&y);

注意:
如果函數內部不對參數進行修改 儘量對參照型別的參數加const。

值傳遞(交換x,y)

1 #include
2 using namespace std;
3 void swap(int& x,int& y){
4 int temp=x;
5 x=y;
6 y=temp;
7
8 }
9 int main(){
10 int x=1000;
11 int y=10;
12 swap(x,y);
13 cout<<x<<":"<<y<<endl;
14
15 }

-------------------不能常數參照,除非加const -----------
1 #include
2 using namespace std;
3 void printNum(const int& x){
4 cout<<x<<endl;
5 }
6 int main(){
7 int x=100;
8 printNum(99);
9
10 return 0;
11 }

13.4 參照作爲函數的返回值
函數的返回值一般作爲右值。
int x=getmax(a,b);
getmax(a,b)=100;
如果希望函數的返回值作爲左值,可以使用參照型別。
不能 返回區域性變數的參照。
可以返回 全域性 堆 函數參數 static
成員函數中返回成員函數變數

1 #include
2 using namespace std;
3 int& getmax(int& x,int& y){
4 return x>y?x:y;
5 }
6 int main(){
7 int y=1;
8 int x=2;
9 cout<<getmax(x,y)<<endl; //值爲x,y中的最大值
10 getmax(x,y)=100; //前面對整個函數進行裡參照
11 cout<<getmax(x,y)<<endl; //值爲100
12 cout<<x<<endl; // x=100
13 return 0;
14 }

13.5 參照如何來實現的。
參照是用指針來實現的。
int a=100;
const int*pa=&a;

大體下: int* const newra=&a;

13.6 指針 和 參照的區別和聯繫??

十四 c++中型別轉換運算子。
static_cast<型別>(變數);
在某一方向上可以做隱式型別轉換。轉換可以使用靜態轉換。
int *pi
void *vp=pi;

1 #include
2 using namespace std;
3 int main(){
4 int var_i=100;
5 int pi=&var_i;
6 void vp=pi;
7 int
newpi=static_cast<int
>(vp);
8 }

dynamic_cast<型別>(變數)
具有多型性的父子類之間 轉換使用
const_cast<型別>(變數)
去除const修飾的轉換。
reinterpret_cast<型別> 重新解釋/最接近強制型別轉換方式/

1 #include
2 using namespace std;
3 int main(){
4 //volatile const int a=199
5 const int a=199;//指針const
6 //int *pa=&a; //會報錯
7 int pa=const_cast<int>(&a);
8 *pa=123;
9 cout<<a<<endl;//C++中有const優化(不去記憶體中讀值),c中結果爲123
10 // cout<<a<<endl;//用volatile可以改變*pa中的值
11
12 }

十五
C++之父給程式設計師的建議
********************************5月8日 *********第三天
十五.c++ 之父給c程式設計師的建議
c++程式設計中 儘量少使用 宏
const,enum 定義常數
使用inline 替代帶參宏
使用namespace 避免命名衝突
變數隨時用隨時定義 並保證初始化
儘量使用 new delete 運算子去分配和釋放記憶體
少於malloc free
儘量少用c風格的字串 使用c++中的string
儘量不要做強制型別轉換,非要強轉 也要使用
c++ 提供的轉換運算子。
逐步建立物件導向的思想
(以類的方式來組織程式碼)
-----------------------------------物件導向程式設計------------------------
一.什麼是物件?
一切皆物件
一個程式就是有一組物件組成的一個整體,程式的
功能通過物件和物件之間發送訊息完成。
每個物件都有一個型別。
同一個型別都能接收相同的訊息。

現實世界 抽象 計算機世界
Person
張三(英雄) 英雄(打人)
age
power

李四

具體 ----- 抽象
具體的人 學生
具體的人 諮詢
具體的人 老師
二.如何描述一組物件
抽取共同(主要)特徵 汽車
name (銷售) (維修)
age 品牌 型號
gender 價格 車主
address 顏色
油耗
變數
抽取共同的功能
函數
三.一組學生的共同特徵 和 功能
學號 吃飯
姓名 睡覺
年齡 玩
學校 學習
班級
四.如何描述 這組物件的型別
4.1 使用結構體描述
使用結構體 描述一個時間 提取時間的特徵
和時間的功能。
特徵:
小時 int hour;
分鐘 int min;
秒 int sec;
功能:
時間的顯示
設定時間
走秒 (呼叫一次 秒數加1)
執行(一秒鐘 顯示一次 不斷回圈)

4.2 使用類class描述符 型別
class 中預設的許可權是私有的,而struct中預設是公開的。
許可權 pubblic: 數據可以在類內和類外存取。/修飾到下一個修飾出現位置/
private:數據只可以在類內存取。
proctect : 類內 和 子類 中都可以使用。

5.1 什麼是建構函式。
是一個特殊的函數,和型別名相同。
沒有返回值型別。能保證建立一個物件時 自動呼叫一次。
建構函式目的是:初始化物件
如果一個型別 不提供建構函式,則系統自動提供一個無參的建構函式。但一旦人爲提供 建構函式則系統提供的無參構造自動消失。

2
3 #include
4 #include
5 #include <unistd.h>
6 #include
7 using namespace std;
8 class Time{
9 int hour;
10 int min;
11 int sec ;
12 public:
13 Time(){
14 hour=0;
15 min=0;
16 sec=0;
17 cout<<「Time()」<<「0」<<endl;
18 }
19 Time(int x,int y,int z){
20 cout<<「Time(int ,int,int)」<<x<<endl;
21 }
22 };
23 int main(){
24 Time *a= new Time;
25 cout<<"--------------------"<<endl;
Time * b= new Time(); //呼叫Time()
Time * c= new Time(1,1,1);// 呼叫Time(int,int,int)
}

5.2 一個物件建立的過程
根據對物件的大小,分配記憶體
如果型別成員的變數是基本型別,則什麼都不做,如果是類型別的成員 則去構建他。
呼叫這個型別的構建函數.

5.3 初始化參數列表
當型別中有 const成員 或者參照型別成員時 需要在建構函式呼叫之前賦值。
在建構函式參數列表之後 實現之前的位置 是初始化參數列表。

1 #include
2 using namespace std;
3 class A{
4 const int a ;
5 int &c;
6 int b;
7 public:
8 /3/ A(int oc):a(123),b(100),c(oc){
9 //2 A(int oc):a(123),b(100),c(*new int(oc)){
10 //1 A(int& oc):a(123),b(100),c(oc){ // 後面改爲;A a(變數)
11 //a=123; //會報錯
12 b=46;//更改在建構函式之前的賦值
13 }
14 };
15 int main(){
16 A a(111);
17 return 0;
18 }

class A {
public:
A();/這裏是初始化參數列表/
};
六、實際開發中 類的標頭檔案和 實現檔案時分離的 標頭檔案中是對型別的定義
class 型別名{
成員變數的定義;
成員函數的宣告;
建構函式宣告;
};
實現的檔案
對成員函數 和 建構函式進行實現。
如果函數有參數的預設值 在標頭檔案中指定
實現檔案中不能確定。 《g++ -c xx.cpp 之後一定要記得xx.o 否者偵錯會出錯》

1 #ifndef MYTIME_H
2 #define MYTIME_H
3 class mytime{
4 private:
5 int hour;
6 int min;
7 int sec;
8 public://重名 可以初始化來解決
9 mytime(int hour=0,int min=0,int sec=0);
10 void show();
11 void dida();
12 void run();
13 }
14 };
15 #endif

1 #include
2 #include 「mytime.h」
3 #include
4 using namespace std;
5 //重名 可以初始化來解決i**/
6 mytime::mytime(int hour ,int min ,int sec )
7 :hour(hour),min(min),sec(sec){//初始化參數列表
/// 也可以用
mytime::mytime(int hour ,int min ,int sec ){
this->year=year;
this->min=min;
this->day=day;
}
/
8
9 }
10 void mytime::show(){
11 cout<<setfill(‘0’)<<setw(2)<<hour<<":"<<setw(2)<<min<<":"<<setw(2) <<sec<<’\r’<<flush;
12
13 }
14 void mytime::dida(){
15 if(60==++sec){
16 sec=0;
17 if(60==++min){
18 min=0;
19 }
20 if(24==++hour){
21 hour=0;
22 }
23 }
24
25 }
26 void mytime::run(){
27 while(1){
28 show();
29 sleep(1);
30 dida();
31 }
32 }

1 #include 「mytime.h」
2 #include
3 using namespace std;
4 int main(){
5 mytime mytime1;
6 mytime1.run();
7 }

/五月九日第四天
1.指向當前物件的指針:this指針
在構建函數中 this 代表指向正在被構建的物件
在成員函數中 this 代表呼叫這個成員的物件中。

this->year;
1.2 this指針的使用:
可以使用函數的參數和名字 和成員變數重名時 做一個區分。
可以作爲函數的返回值
可以作爲函數的參數、

1 #include
2 using namespace std;
3 class Date;
4 Date& showDate(Date& date) ;
5 class Date{
6 int year;
7 int month;
8 int day;
9 public:
10 Date(int year=2013,int month=1,int day=2){
11 this->year=year;
12 this->month=month;
13 this->day=day;
14 }
15 /提供一個函數返回當前得到日期/
16 Date& addSelfDay(){//呼叫this就加一天
17 day++;
18 return *this;
19 }
20 void show(){
21 showDate(*this);
22 }
23 /提供一個私有的成員存取介面/
24 int getYear(){
25 return year;
}
27 int getMonth(){
28 return month;
29 }
30 };
31 /在這裏提供一個全域性函數 列印DATE型別的參數的值 並且返回這個物件的本身,還要
求在DATE的成員函數SHSOW 使用這個全域性函數
/
32 Date& showDate(Date& date){
33 cout<<date.getYear()<<"-"<<date.getMonth()<<endl;
34 return date;
35 }
36 int main(){
37 Date date;
38 date.show();
}

二、const 物件和 const函數
2.1 A a
const A aa;
const A {
public:
void show(){
/普通成員函數/
}
void show() const{
/* 這是const函數*/
}
};
2.2 const 物件只能呼叫const函數。
const 函數和非const函數可以形成過載。
非const物件優先呼叫 非const函數。
如果沒有相應的非const 函數則呼叫const函數。

1 #include
2 using namespace std;
3 class A{
4 public:
5 void show()const{
6 cout<<「this is const show」<<endl;
7 }
8 void show(){
9 cout<<「this is no const show」<<endl;
10 }
11 A(){}// A 就可以不用初始化
12 };
13 int main(){
14 const A a;
15 a.show();
16 A aa;
17 aa.show();
18 }

如果const函數中只能去讀成員變數,如果需要修改成員變數 加一個mutable可以對成員進行修改。

1 #include
2 using namespace std;
3 class A{
4 mutable int m;//使得後面的const 能夠修改
5 public:
6 void show()const{
7 m=1000;
8 cout<<「this is const show」<<m<<endl;
9 }
10 void show(){
11 m=20;
12 cout<<「this is no const show」<<m<<endl;
13 }
14 A(){}// A 就可以不用初始化
15 };
16 int main(){
17 const A a;
18 a.show();
19 A aa;
20 aa.show();
21 }

三、解構函式
3.1 概念
一個特殊的函數 和類名相同 但函數前有一個~ 不能有任何的參數,沒有返回值型別。
這種函數可以在物件銷燬前自動呼叫一次。
如果想多次呼叫解構函式,理論上是可以的。
3.2 舉例:

1 #include
2 using namespace std;
3 class A{
4 public:
5 A(){
6 cout<<「A()」<<endl;
7 }
8 ~A(){
9 cout<<"~A()"<<endl;
10 }
11 };
12 void foo(){
13 A *a=new A();//物件離開作用域就銷燬
14 A aa;
15 delete a;//銷燬a時呼叫解構
16 cout<<"------------"<<endl;
17 /申請三個物件的堆記憶體 在釋放掉這這三個物件的記憶體/
18 A *pa=new A[3];
19 delete[] pa;
20 }
21 int main(){
22 foo();
23 cout<<「end mian」<<endl;
24 }

3.3 什麼時候要自定義的解構函式
物件銷燬時 釋放記憶體
一般有動態記憶體
3.4 在建構函式中 或者初始化參數中爲 指針型別的成員變數 分配動態記憶體。
在解構函式中 釋放掉 動態分配記憶體。

1 #include
2 #include
3 using namespace std;
4 struct Date{
5 int year;
6 int month;
7 int day;
8 Date(){
9 cout<<「Date()」<<endl;
10 }
11 ~Date(){
12 cout<<"~Date()"<<endl;
13 }
14 };
15 class Person{
16 string name;
17 Date *date;//垃圾分配
18 public:
19 Person():date(new Date()){//分配動態記憶體
20 cout<<「person」<<endl;
21 }
22 ~Person(){
23 cout<<"~person"<<endl;
24 delete date;
25 }
};
27 int main(){
28 Person *p=new Person();
29 delete p;
30
31 }

四 拷貝建構函式
4.1 拷貝建構函式是一個特殊的建構函式 是用一個已經存的物件 去建立另外一個物件。
class A{
public:
A(){
}
/拷貝函數/
A(const A& a){//const 利於提高相容性
}
};
4.2 拷貝建構函式呼叫時機
使用同類型的物件 建立另外一個物件
A a;
A b=a;// A b; b=a; 這是叫賦值
把一個物件傳賦值給函數的形參;
showa(A,a);
把一個物件作爲函數的返回值。
A geta(A ,a){return a;}
4.3 什麼時候需要自定義拷貝建構函式、
希望自定義物件的拷貝過程。
需要讓物件有自己獨立的記憶體。
預設拷貝建構函式 是把原物件的數據進行逐子節拷貝。

1 #include
2 #include
3 #include
4 using namespace std;
5 struct Date{
6 int year;
7 int month;
8 int day;
9 Date(){
10 cout<<「Date()」<<endl;
11 }
12 ~Date(){
13 cout<<"~Date()"<<endl;
14 }
15 };
16 class Person{
17 string name;
18 Date date;//垃圾分配
19 public:
20 Person():date(new Date()){
21 cout<<「person」<<endl;
22 }
23 /拷貝建構函式 讓物件獨立/
24 Person(const Person& p){
25 name=p.name;
26 /申請新空間/
27 date= new Date();
28 memcpy(date,p.date,sizeof(Date)); //記憶體拷貝 先要確定記憶體大小
29 }
30 ~Person(){
31 cout<<"~person"<<endl;
32 delete date;
33 }
34 };
35 void foo(){
36 /
Person *p=new Person();
37 Person p2=p;
38 Person p3=p2;
39 delete p;
/
40 Person p1;
41 Person p2=p1;
42 }
43 int main(){
44 foo();
45 }

4.4 其他發生拷貝的情況。
作爲函數參數

作爲函數的返回值

1 #include
2 using namespace std;
3 class A{
4 public :
5 A(){
6 cout<<「A()」<<endl;
7 }
8 A(const A& a){
9 cout<<「A(const)」<<endl;
10 }
11 ~A(){
12 cout<<"~A()"<<endl;
13 }
14 };
15 const A getA(const A& a){
16 return a;
17 }
18 A get2A(){
19 // A a; return a //編譯器優化
20 /編譯器優化,使用匿名物件,簡化程式設計//* return Date(2014,5,1)*/
21 return A();
22 }
23 void shoA(const A& a){
24 }
25 int main(){
26 const A a;
27 // shoA(a1);
28 // getA(a1);
29 // getA(a)=a;
30 get2A();
31 }

五、靜態函數 和 靜態成員函數(一般用於共用)
5.1 靜態函數是不需要物件就可以直接呼叫成員函數。
5.2 靜態函數是不需要通過物件就能直接呼叫的成員函數。
5.3 靜態函數受類名作用域的影響
5.4 靜態函數只能存取靜態函數 不能直接存取非靜態成員。
5.5 普通函數的成員函數中 普通編譯器負責傳入一個指針this,而靜態函數沒有傳入指針this。
5.6 靜態成員變數 必須在類外初始化。
class A{
static int a;
};
靜態變數的型別 型別名::變數名;
如果靜態成員變數是基本型別 則賦值成0
如果是自定義型別 則自動呼叫 無參構造。
int A::a;

1 #include
2 using namespace std;
3 class A{
4 public:
5 void show(){ //需要通過物件才能 纔能存取
6
7 }
8 static void staticshow(){ //不需要通過物件就能直接存取,受類名作用域的影響
9
10 }
11 };
12 int main(){
13 A a;
14 a.show();
15 A::staticshow();
16 }


1 #include
2 using namespace std
3 class A{
4 int a;
5 public:
6 static int b;
7 public:
8 void show(){
9 cout<<「a=」<<a
10 }
11 //public:
12 static void stat
13 // cout<<「a=」<
14 }
15 static void show2
16 cout<<「a=」<<myt
17 }
18 };
19 int A::b=100; //靜態成員變數 必須在類外初始化。
20 int main(){
21 A a;
22 a.show();
23 A::staticshow();
24 A::show2(&a);
25 cout<<A::b<<endl;}

/單例模式 寫一個類只允許在一個進程中建立出一個物件
許可權 建構函式 拷貝函數 靜態成員函數 靜態函數 參照
/

1 #include
2 using namespace std;
3 /餓漢式/
4 class Sigleton{
5 private :
6 Sigleton(){} // 封裝複製
7 Sigleton(const Sigleton& s){} // 封裝拷貝
8 static Sigleton sig;
9 public :
10 static Sigleton& getInstance(){
11 return sig;
12 }
13 };
14 Sigleton Sigleton::sig;//可以呼叫私有的建構函式
15 int main(){
16 Sigleton& s1= Sigleton::getInstance();
17 Sigleton& s2= Sigleton::getInstance();
18 cout<<&s1<<endl;
19 cout<<&s2<<endl;
20 }

第五天五月十二日****
1.1 指向成員變數的指針。
struct Date{
int year;
int month;
int day;
};
int Date::*pm; //成員指針
成員變數的型別 類名::*指針名;
pm= &Date::year;//取成員變數的地址
1.1.2 呼叫 (必須通過物件)
(1) Date date;
date.*pm; //取值
(2) Date *date2=new Date();
date2->*pm;//取值

1 #include
2 using namespace std;
3 struct Date {
4 int year;
5 int month;
6 int day;
7 };
8 int main(){
9 Date date={2014,5,12};
10 /定義一個成員指針/
11 int Date::*pm;
12 pm=&Date::day;
13 cout<<date.*pm<<endl;
14 pm=&Date::year;
15 cout<<date.*pm<<endl;
16 }

1.1.3 成員變數指針的本質: 是這個成員變數在物件中的地址偏移量。

3 struct Date {
4 int year;
5 int month;
6 int day;
7 };
8 int main(){
9 Date date={2014,5,12};
10 /定義一個成員指針/
11 union {
12 int Date::*pm;
13 int *mypm;
14 };
15 //int Date::*pm;
16 pm=&Date::day;
17 cout<<date.*pm<<endl;
18 pm=&Date::year;
19 cout<<date.*pm<<endl;
20 cout<<mypm<<endl;
21 pm=&Date::day;
22 cout<<date.*pm<<endl;
23 cout<<mypm<<endl;// 輸出地址偏移量
24
25 }

1.2 成員函數 指針
struct Date{
int year;
int month;
int day;
int getyear(){
return month;
}
int getMonth(){
return month;
}
};
int (Date::*pmfun)();
pmfun=&Date::getMonth;
(date.*pmfun)();
(指針物件->*成員函數指針名)();

成員函數的指針就是在程式碼區中的絕對地址。

int (*p)[5][4]=new int[3][4][5];
int (*pa)[7]=new int[4][7];

1 #include
2 using namespace std;
3 struct Date{
4 int year;
5 int month;
6 int day;
7 int getyear(){
8 cout<<「getyear()…」<<endl;
9 }
10 int getMonth(){
11 cout<<「getMont()」<<endl;
12 }
13 void setMonth(int m){
14 month=m;
15 cout<<「m=」<<m<<endl;
16 }
17 };
18 int main(){
19 Date date={2014,5,12};
20
24 int (Date::*pmfun)();//定義
25 pmfun=&Date::getMonth;//取地址
26 (date.*pmfun)();//呼叫函數
27 cout<<"--------"<<endl;
28 void (Date::*psetfun)(int m);
29 psetfun=&Date::setMonth;
30 (date.*psetfun)(6);
31 }

二、運算子過載
2.1 本質
函數的特殊表現形式。
2.2 寫一個類 表達分數
特徵:
int x ;
int y;
功能:
顯示分數
加法
減法

2.3 Fraction fa(1,2);
Fraction fa(1,2);
當編譯器 翻譯到fa+fb時;
先去Franction型別中 找一個成員函數叫operator+(const Fraction&fb)如果沒有這個形式
的成員函數 則去全域性找一個函數operator+(const Fraction&a,const Fraction&b);
a#b operator#(b)
operator#(a,b)
2.4 寫一個成員函數operator- 負責兩個分數相減 寫一個成員函數相乘operator*

2.5 存取許可權的解決
爲private的成員 提供存取介面 可以把函數 宣告爲友元函數
友元函數:(獲得存取私有成員的存取許可權,全域性函數)
成員函數:
存取類私有成員的權利、
作用域位於類外、受存取許可權控制
必須通過物件存取
普通成員函數具有三個特徵:
靜態函數 只具有前兩個。
有元函數只有一個特徵.

1
2 #include
3 using namespace std;
4 class Fraction{
5 private:
6 int x;
7 int y;
8 public:
9 Fraction(int x=0,int y=1){
10 this->x=x;
11 this->y=y;
12 }
13 void show(){
14 cout<<x<<"/"<<y<<endl;
15 }
16 /成員函數的兩個分數相加/
17 Fraction operator+(const Fraction&b){
18 cout<<「operator+(const Fraction&b)」<<endl;
19 return Fraction(this->xb.y+this->yb.x,this->yb.y);
20 }
21 Fraction operator-(const Fraction& fb){
22 return Fraction(this->x
fb.y-this->yfb.x,this->yfb.y);
23 }
}
24 };
25
26 /設計一個全域性函數表達兩個分數相加/
27 //Fraction /Add_Fraction/operator+(const Fraction& a,const Fraction& b){// ??
28 /* Fraction temp;
29 temp.x=a.xb.y+a.yb.x;
30 temp.y=a.yb.y;/
31 /* return Fraction(a.xb.y+a.yb.x,a.yb.y);
32 }
/
33 int main(){
34 Fraction fa(1,3);
35 fa.show();
36 Fraction fb(2,3);
37 fb.show();
38 // Fraction tmp=Add_Fraction(fa,fb);
39 Fraction tmp=fa+fb;
40 tmp.show();
41 Fraction temp=fa-fb;
42 temp.show();
43
44 }

2.5 包裝一個int型別,Integer型別

1 #include <iostream>

2 using namespace std;
3 class Integer{
4 int data;
5 public:
6 Integer(int data=0):data(data){
7
8 }
9 void show(){
10 cout<<data<<endl;
11 }
12 friend Integer operator+(const Integer& a,const Integer&b);
13 friend void operator+=(Integer& a ,const Integer& b);
14
15 };
16 /在全域性提供兩個整數相加的函數operator/
17 Integer operator+(const Integer& a,const Integer& b){
18 return Integer(b.data+a.data);
19 }
20 /在全域性提供一個函數實現+=的實現/
21 void operator+=(Integer& a ,const Integer& b){
22 a.data+=b.data;
23 }
24 int main(){
25 Integer a(1001);
26 a.show();
27 Integer b(1222);
28 b.show();
29 Integer c=a+b;
30 c.show();
31 a+=b;
32 a.show();
33 }

2 #include
3 using namespace std;
4 class Integer{
5 int data;
6 public:
7 Integer(int data=0){
8 this->data=data;
9 }
10 void show(){
11 cout<<data<<endl;
12 }
13 Integer& operator+(const Integer& b){ //兩種方式:
14 // return Integer(b.data+a.data);
15 this->data+=b.data;
16 return this
17 }
/
************************//參照幾乎與一級指針一樣
7 Integer operator + (const Integer& a){
8 return Integer(data+a.data);
9 }

*************************/
18 Integer& operator+=(const Integer& b){
19 this.data+=b.data;
20 return *this;
21 }
22
23 };
26 int main(){
27 Integer a(1001);
28 a.show();
29 Integer b(1222);
30 b.show();
31 Integer c=a+b;
32 c.show();
33 a+=b;
34 a.show();
35 }

2.7 特殊運算子號
cout<<b;
先去cout對應的型別ostream 中找一個成員函數 operator<<(b)
operator<<(ostream& os,b);
cin>>b

2 #include
3 using namespace std;
4 class Integer{
5 public:
6 int data;
7 Integer(int data=0){
8 this->data=data;
9 }
10 void show(){
11 cout<<data<<endl;
12 }
13 Integer& operator+(const Integer& b){
14 // return Integer(b.data+a.data);
15 this->data+=b.data;
16 return *this;
17 }
18 Integer& operator+=(const Integer& b){
19 this->data+=b.data;
20 return this;
21 }
22 friend ostream& operator <<(ostream& os,const Integer& i);
23 friend istream& operator <<(istream& is, Integer& j);
24 };
25 /輸出Integer/
26 ostream& operator <<(ostream& os,const Integer& i){//ostream 實現連續參照
27 return os<<i.data;
28 }
29 /輸入Integer/
30 istream& operator >>(istream& is,Integer& j){ //不能加const
31 return is>>j.data;
32 }
33 int main(){
34 Integer a(1001);
35 a.show();
36 Integer b(1222);
37 b.show();
38 Integer c=a+b; // cout<<(a+b)<<endl;//a+b被當作const型別
/
39 c.show();
40 a+=b;
41 cout<<a<<endl;
42 cout<<「please input e」<<endl;
43 Integer e;
44 cin>>e;
45 cout<<e;
46 }

2.8 寫一個Integer 類封裝整數 使用成員函數實現+ - * /
然後提供輸入輸出符 運算子實現比較兩個整數是否相等的運算子實現==
在實現一個運算子一個-=

1 #include
2 using namespace std;
3 class Integer{
4 int data;
5 public :
6 Integer(int data=0):data(data){}
7 Integer operator + (const Integer& a){
8 return Integer(data+a.data);
9 }
10 Integer operator-(const Integer& a){
11 return Integer(data-a.data);
12 }
13 Integer operator (const Integer& a){
14 return Integer(data
a.data);
15 }
16 double operator /(const Integer& a){
17 return this->data1.0/a.data1.0;
18 }
19 /判斷兩個整數是否相等/
20 bool operator (const Integer& a){
21 /* return data
a.data;/ return this==&a;//地址相等
22 }
23 void show(){
24 cout<<data<<endl;
25 }
26 };
27 int main(){
28 Integer a(11);
29 Integer b(23);
30 Integer c=a+b;
31 c.show();
32 c=a-b;
33 c.show();
34 c=a
b;
35 c.show();
36 double e;
37 e=a/b;
38 cout<<e<<endl;
39 cout<<(a==b)<<endl;
40 // cout<<(a+b)<<endl;//a+b被當作const型別*/
41 }

2.9 一元運算子的過載

  • ! ++ –
    Integer a;
    -a;
    #a(去成員函數中找一個成員函數);
    叫 operator#() 如果找不到就去全域性找一個 operator#(a);

2 #include
3 using namespace std;
4 class Integer{
5 int data;
6 public :
7 Integer(int data=0):data(data){}
8 Integer operator + (const Integer& a){
9 return Integer(data+a.data);
10 }
11 Integer operator - (const Integer& a){
12 return Integer(data-a.data);
13 }
14 /寫一個負號運算子函數/
15 const Integer operator-(){
16 return Integer(-data);
17 }
18 /寫一個!運算子/
19 const Integer operator !() const{//加const 可以求!!a
20 return Integer(!data);
21 }
22 /寫一個++ 運算子函數/
24 ++data;//data++;
25 return this;
26 }
27 /用啞元區分 是後++/
28 Integer operator++(int){
29 return Integer(data++);
30 }
31 /判斷兩個整數是否相等/
32 bool operator ==(const Integer& a){
33 /
return dataa.data;*/ return this&a;//地址相等
34 }
35 void show(){
36 cout<<data<<endl;
37 }
38 };
39 int main(){
40 Integer a(10);
41 Integer e=-a;
42 e.show();
43 Integer q=!!a;//!a是const型別
44 q.show();
45 Integer h=++++a;
46 h.show();
47 Integer g=a++;
48 g.show();
49 g=a++;
50 g.show();
51 Integer b(4);
52 Integer c=a-b;
53 c.show();
54 }
55


三 指針和參照 的區別和聯繫
聯繫:
參照本身是指針 指針的大小大部分效果 都可以使用參照來實現
指針作爲函數的實參
myswap(intx,inty);
myswap(int& x,int& y);
二級指針作爲函數的參數 就是希望在函數內部修改 指針的指向。
changerPtr(int** ptr);
changerPtr(int*& ptr);
用指針作爲函數的返回值
自增效果
區別:
1、參照定義時,必須是初始化
指針沒有這個要求
2.參照一旦初始化,就不能改變參照的物件。
指針可以更改指向
3.指針是一個實體變數 大小永遠是4
參照是一個別名 大小和參照物件有關
4.有指針的指針
沒有參照的參照
int **pi;
int *& pi;
5.有指針的參照
沒有參照的指針
int & pa;
int &
pa;//error
6.有指針的陣列
沒有參照的陣列
int *data[3];
int& data[3]={a,b,c};//error
7.有陣列的參照
int data[3]={1,2,3};
int (&data)[3];

1 #include
2 using namespace std;
3 void showArr(int(&rdata)[4]){
4 cout<<sizeof(rdata)<<endl;
5 }
6 int main(){
7 int data[3]={9,5,5};
8 cout<<sizeof(data)<<endl;
9 }

/五月十三號第六天*******
一、運算子的限制
1.不能衝載的運算子
:: . .* sizeof() typeid ?:
typedid 取得記憶體資訊
2. 不能發明運算子 只能修改運算子過載
$ #
3. 不能對基本型別的數據進行過載 至少有一種是類型別
4.不要修改運算子的特性。
5. 只能過載成 成員的運算子
= 最好是成員運算子 += -= /= %=
[] -> *
()
二 只能是成員的運算子
2.1 = []
寫一個自定義陣列

string 瞭解庫成員

1 #include
2 #include
3 #include
4 using namespace std;
5 int main(){
6 string abc(「hello」);
7 string bcd(「hello」);
8 if(abc==bcd){
9 cout<<「abc equals bcd」<<endl;
10 }
11 cout<<abc[100]<<endl;//不報錯
12 try {
13 cout<<abc.at(9)<<endl;
14 cout<<「app continue」<<endl;
15 }catch(out_of_range& e){
16 cout<<e.what()<<endl;
17 }
18 abc =「test string」;
19 cout<<abc<<endl;
20 abc=abc+bcd;
21 cout<<abc<<endl;
22 cout<<abc.length()<<endl;
23 /* c 和c++之間的轉換*/
24 const char* mycstr=abc.c_str();
25 cout<<mycstr<<endl;
26 /c轉換爲C++/
27 // string (const char *str);
28 }

2.1 = []
寫一個自定義陣列 Array

2.2 ()
單參型別 允許吧這個單參型別 轉換成建構函式 所對應的型別。
防止這種型別的轉換 可以使用 explicit。

91 Array c=100;// 單參構造型別有關,允許把單參型別轉化爲建構函式對應的型別
92 //預設爲建構函式的單參的值 要避免
93 Array cc(100);

  1. ()運算子 也可以做型別轉換
    轉換成的型別 operator 轉換成的型別(){}
  2. () 函數物件
    可以象函數一樣 使用一個物件
    返回值的型別 operator()(參數型別 形參名,參數型別 形參名){}

1 #include
2 using namespace std;
3 class Product{
4 int count;
5 double price;
6 public:
7 Product(int count=1,double price=0.0):price(price),count(count){
8 }
9 operator int (){
10 return count;
11 }
12 operator double () {
13 return price;
14 }
15 /h函數物件的實現/
16 double operator () (int count=1,double price=0){
17 this->count=count;
18 this->price =price;
19 return count*price ;
20 }
21 };
22 int main(){
23 Product p(10,100.85);
24 int c=(int)p;
25 double pr=(double)p;
26 cout<<c<<"-"<<pr<<endl;
27 cout<<p(10,100.6)<<endl;
28 }

五月十四第七天**
new 比 malloc多做三件事:
如果成員是類型別 自動建立成員
自動處理型別轉換
自動呼叫這個類別建構函式
delete 比free 多做了一件事 :多呼叫了一次解構、

/new 運算子的過載/
void* operator new(size_t size){
cout<<「this is my new」<<endl;
return malloc(size);
}
static void operator delete(voidp){
cout<<「this is delete」<<endl;
free§;
}
-----------物件導向-------------
物件導向的四大特徵:(2,3,4是三大)
1.抽象
2.封裝
3.繼承
4.多型
二、封裝
該隱藏的私用化,該公開的公有化。
優點:便於分工和協同開發
加密
const char code(const char
msg);
解密
const char* code(const char*msg,int key);
阻止不必要的擴充套件
class A {
public:
list getData();
private:
void fa();
void fb();
void fc();
};

二、繼承
3.1 程式碼和數據的複用。
在已有的程式碼和數據上 擴充套件
3.2 語法
class A{};
class B:public A{};
三 另一種程式碼複用關係
組合

2 #include
3 #include
4 using namespace std;
5 class Animal{
6 public:
7 string name;
8 int age;
9 void show(){
10 cout<<「this is show」<<endl;
11 cout<<name<<":"<<age<<endl;
12 }
13 };
14 class Dog{
15 public:
16 Animal an; //組合
17 void dogfun(){
18 cout<<「dog kanjia」<<endl;
19 }
20 };
21 class cat:public Animal{ //繼承
22 string color;
23 public:
24 void catfun(){
25 cout<<「抓老鼠」<<endl;
26 }
27 };
28 int main(){
29 Animal animal;
30 animal.name=「xiaoqiang」;
31 animal.age=2;
32 animal.show();
33 Dog dog;
34 dog.an.name=「xianghuang」;
35 dog.an.age=3;
36 dog.an.show();
37 dog.dogfun();
38 }

3.4 繼承的方式
公開的方式 class A :public B{};
保護繼承 class A :protected B {};
私有繼承 class A :private B{};
3.5 公開繼承下 父類別數據到子類之後的許可權的變化:
在公開繼承下父類別的公開的數據 到子類之後是公開的。
父類別中的保護的數據到子類是保護的。
父類別中私有的數據到子類中的隱藏的。

1 #include
2 using namespace std;
3 class A{
4 public:
5 void showa(){
6 cout<<「showa()」<<endl;
7 }
8 protected:
9 void showb(){
10 cout<<「showb()」<<endl;
11 }
12 private:
13 void showc(){
14 cout<<「showc」<<endl;
15 }
16 public:/私有成員在父類別中開一個介面/
17 void showcc(){
18 showc();
19 }
20 };
21 /公開繼承/
22 class B :public A{
23 /公開介面/
24 public:
25 void show(){//保護型別只有父類別和子類能夠存取
26 showb();
27 }
28 /提供一個私有成員的存取介面/
29 public:
30 void getshowc(){
31 showcc();//呼叫父類別的介面函數
32 }
33 };
34 int main(){
35 B b;
36 b.showa();
37 b.show();
38 b.getshowc();
39 //b.showb();保護成員只能在子類 或者父類別中存取
40 }

3.6 保護成員保護繼承
在保護繼承下:父類別的公開數據 到子類是保護的

      父類別保護的成員 到子類是保護的
         父類別私有的成員   到子類是隱藏的

1 #include
2 using namespace std;
3 class A{
4 public:
5 int a ;
6 protected:
7 int b;
8 private:
9 int c;
10
11 };
12 class B:protected A {
13 public:
14 void set_ma(int oa){
15 a=oa;
16 }
17 int get_ma(){
18 return a;
19 }
20 };
21 int main(){
22 B ob;
23 ob.set_ma(100);
24 cout<<ob.get_ma()<<endl;
25 }

3.6 私有繼承
在私有繼承下:父類別的公開數據 到子類是私有的

      父類別保護的成員 到子類是私有的
         父類別私有的成員   到子類是隱藏的

所謂的繼承方式 就是能夠給子類最大的存取許可權。
實際的存取許可權可能小於等於這個繼承方式。私有的數據到子類中一定是隱藏的

1 #include
2 using namespace std;
3 class A {
4 public:
5 void showa(){
6 cout<<「showa」<<endl;
7 }
8 protected:
9 void showb(){
10 cout<<「showb」<<endl;
11 }
12 private:
13 void showc(){
14 cout<<「showc」<<endl;
15 }
16 public:
17 void showcc(){
18 showc();
19 }
20 };
21 class B:private A{
22 public:
23 void show_a(){
24 showa();
25 }
26 void show_b(){
27 showb();
28 }
29 void show_c(){
30 showcc();
31 }
32 };
33 int main(){
34 B b;
35 b.show_a();
36 b.show_b();
37 b.show_c();
38 }
39

4.1 繼承中建構函式和解構函式的呼叫
構建子類物件時,一定會呼叫父類別的建構函式。
解構函式的呼叫順序和建構函式的呼叫順序相反。
A()
B()
~B()
~A()
字類預設呼叫父類別的建構函式。
當然也可以指定呼叫哪個解構函式,在初始化參數列表中指定。

1 #include
2 using namespace std;
3 class A {
4 public:
5 A(){
6 cout<<「A()」<<endl;
7 }
8 A(int x){
9 cout<<「A(int)」<<endl;
10 }
11 ~A(){
12 cout<<"~A()"<<endl;
13 }
14 };
15 class B:public A{
16 public:
17 B():A(100){//指定呼叫A 的建構函式
18 cout<<「B()」<<endl;
19 }
20 ~B(){
21 cout<<"~B()"<<endl;
22 }
23 };
24 class C : public B{
25 public:
26 C(){
27 cout<<「C()」<<endl;
28 }
29 ~C(){
30 cout<<「C()」<<endl;
31 }
32 };
33 void foo(){
34 C c;
35 }
36 int main(){
37 foo();
38 }

3 #include
4 #include <stdio.h>
5 using namespace std;
6 class Person{
7 int age;
8 int pa;
9 public:
10 Person(int age=0):age(age) {
11 pa =new int(123);
12 }
13 ~Person(){
14 cout<<「pa=」<<pa<<endl;
15 delete pa;
16 }
17 Person(const Person& p){
18 age=p.age;
19 pa=new int(
(p.pa));
20 }
21 Person& operator=(const Person& p){
22 if(this!=&p){
23 age=p.age;
24 pa=(p.pa);
25 }
26 return *this;
27 }
28 };
29 class Emp:public Person{
30 public:
31 int *ea;
32 Emp(int age=0):Person(age) {
33 ea=new int(356);
34 }
35 ~Emp(){
36 cout<<「ea=」<<ea<<endl;
37 }
38 /沒有提供賦值運算子 就預設呼叫父類別的/
39 Emp& operator =(const Emp& e){
40 if(this!=&e){
41 Person::operator=(e);
42 memcpy(e.ea,ea,sizeof(ea));
43 }
44 return *this;
45 }
46 /子類的拷貝建構函式/
47 Emp(const Emp& e):Person(e){
48 /處理自己的堆記憶體/
49 ea=new int(4);
50 memcpy(e.ea,ea,sizeof(ea));//memcpy 要保證記憶體連續
52 }
53 };
54 int main(){
55 // Person p;
56 // Person p2(21);
57 // person p3(12); //爲什麼可以通過
58 // Person p3=p2;// 會崩潰
59 // Person p3;
60 // p3=p2;
61 Emp p(11);
62 Emp p1;
63 p1=p;
64 Emp p2(23);
65 Emp p3=p2;
66
67
68 return 0;
69 }

六 名字隱藏(name hide)
子類繼承了父類別數據後 如果提供了和父類同名的數據,則會把父類別的數據隱藏 -----------------------
1 #include
2 using namespace std;
3 class A{
4 public:
5 void show(){
6 cout<<「this is A show」<<endl;
7 }
8
9 };
10 class B:public A{
11 public:
12 void show(){
13 this->A::show();
14 cout<<「this is b show」<<endl;
15 }
16 };
17 int main(){
18 B b;
19 b.show();
20 b.A::show();
21 }

*********************五月十五 *********第七天

一 多繼承;
一個類可以有多個父類別 構建這個物件的順序 和這個類 繼承類的順序相關。
如果繼承的數據 不產生衝突可以使用 父類別名的作用域區分。使用名字隱藏機制 機製 也可以解決。
1.2 多繼承的優化
把父類別中的 共同成員函數 抽取出來
double price ;
double getPrice(){}
放入到更高層的類中。
然後使用虛繼承 繼承最高層的類 對直接子類而言 和普通繼承的區別而言 只是多出了一個指針 維護虛繼承 關係
對子類而言 不再從父類別中拷貝 而是最高層類的子類沒有區別
但要付出維護虛繼承的代價。
1.3 虛擬函式
虛擬函式 是在普通成員函數上加了 virtual 修飾
一個型別有虛擬函式 則編譯器 提供一個虛擬函式表 虛擬函式表中的元素就是函數的地址。這個指針存入物件的前四個位元組。

1 #include
2 using namespace std;
3 class Animal{
4 public:
5 virtual void show(){//多了一個指針,指向表格的首地址(虛擬函式放在一個表中)
6 cout<<「show」<<endl;
7 }
8 virtual void run(){
9 cout<<「run()」<<endl;
10 }
11 };
12 class Dog:public Animal{
13 public:
14 void run(){//虛擬函式的重寫
15 cout<<「four feet run」<<endl;
16 }
17
18 };
19 int main(){
20 cout<<sizeof(Animal)<<endl;
21 cout<<sizeof(Dog)<<endl;
22 Dog dog;
23 dog.run();
24 }


三.虛擬函式的重寫(over write)
虛擬函式的重寫:是針對虛擬函式 重寫時要求函數名相同 參數列表相同 返回值相同。
名字隱藏: 在子類中 出現和父類同名的數據、
函數過載(over load):同一個作用域中函數名相同 參數列表不同。

四 多型 (條件 :指針(參照) 虛擬函式 覆蓋 )
4.1 概念
當父類別物件的指針(或參照)指向,子類物件時呼叫父類別中的提供的虛擬函式 表現將會是子類中函數覆蓋實現。
4.2 多型的基礎是繼承 虛擬函式是多型的關鍵 函數覆蓋是必備條件

4.3 寫一個 動物類 Animal 有一個虛擬函式 run 還有一個虛擬函式 fun
還有一個非虛擬函式show
寫一個 子類Cat 覆蓋run 和fun
再寫一個子類Fish 覆蓋run 和fun show
然後使用Animal

1 #include
2 using namespace std;
3 class Animal{
4 public:
5 virtual void run(){
6 cout<<"this is animal run "<<endl;
7 }
8 virtual void fun(){
9 cout<<「this is animal fun」<<endl;
10 }
11 void show(){
12 cout<<「this is animal show」<<endl;
13 }
14 };
15 class Cat : public Animal{
16 public:
17 virtual void run(){
18 cout<<"this is cat run "<<endl;
19 }
20 virtual void fun(){
21 cout<<「this is cat fun」<<endl;
22 }
23 void show(){
24 cout<<「this is cat show」<<endl;
25 }
26 };
27 class Fish : public Animal{
28 public:
29 virtual void run(){
30 cout<<"this is fish run "<<endl;
31 }
32 virtual void fun(){
33 cout<<「this is fish fun」<<endl;
34 }
35 void show(){
36 cout<<「this is fish show」<<endl;
37 }
38 };
39 /如何使用多型/
40 void showAnimal(Animal* animal){
41 animal->run();
42 animal->fun();
43 animal->show();
44 }
45 int main(){
46 Cat cat;
47 Animal animal;
48 showAnimal(&animal);
49 showAnimal(&cat);
50 Fish fish;
51 showAnimal(&fish);
52 return 0;
53 }

結果:
this is animal run
this is animal fun
this is animal show
this is cat run
this is cat fun
this is animal show
this is fish run
this is fish fun
this is animal show

4.4 多型的使用 是作爲函數的參數,
函數參數作用:可以做到型別的通用。
可以在函數內部根據實際的物件型別
做出相應的動作(虛擬函式)
函數的返回值:

45 /物件製造工廠/
46 Animal* getAnimal(string animal){
47 if(「cat」==animal){
48 return new Cat();
49 }else if(「fish」==animal){
50 return new Fish();
51 }
52 }

4.5 多型的原理:
虛擬函式表指針
虛擬函式表
每一個型別都有自己型別對應的虛擬函式表,從型別的物件共用虛擬函式表。
子類和父類別的虛擬函式表各有各的。

2 #include
3 using namespace std;
4 class Animal{
5 public:
6 virtual void run(){
7 cout<<「animal run」<<endl;
8 }
9 virtual void fun(){
10 cout<<「animal fun」<<endl;
11 }
12 void show(){
13 cout<<「animal show」<<endl;
14 }
15
16 };
17 class Cat: public Animal{
18 public:
19 virtual void run(){
20 cout<<「cat run」<<endl;
21 }
22 virtual void fun(){
23 cout<<「Cat fun」<<endl;
24 }
25 void show(){
26 cout<<「Cat show」<<endl;
27 }
28
29 };
30 class Dog:public Animal{
31 public:
32 virtual void fun(){
33 cout<<「Dog fun」<<endl;
34 }
35 void show(){
36 cout<<「Dog show」<<endl;
37 }
38 };
39 int main(){
40 Cat cat;
41 Dog dog;
42 cat.fun();
43 dog.fun();
44 Animal* pa=&cat; //將cat的訊息複製給dog
45 pa->fun();
46 pa=&dog;
47 /cat前四個子節複製dog/
48 memcpy(&dog,&cat,4);
49 pa->fun();
50 }----------------
結果:
Cat fun
Dog fun
Cat fun
Cat fun

4.6 型別通用,方便了程式設計,損失了物件的一些個性、但有時 我們需要識別出物件真正的型別 進而恢復物件的個性。
具有多型性的父子類之間:
dynamic_cast<型別>(物件)
必須具有 虛擬函式 轉換成功就返回一個合法的地址 轉換失敗就返回NULL


1 #include
2 using namespace std;
3 class Animal{
4 public:
5 virtual void run(){
6 cout<<「animal run」<<endl;
7 }
8 void fun(){
9 cout<<「animal fun」<<endl;
10 }
11
12 };
13 class Dog:public Animal{
14 public:
15 void run(){
16 cout<<「dog run」<<endl;
17 }
18 void fun(){
19 cout<<「dog fun」<<endl;
20 }
21 };
22
23 class Cat:public Animal{
24 public:
25 void run(){
26 cout<<「cat run」<<endl;
27 }
28 void fun(){
29 cout<<「cat fun」<<endl;
30 }
31 };
32 void showAnimal(Animal* animal){
33 animal->run();
34 /恢復傳入的物件的個性/
35 if(dynamic_cast<Cat*>(animal)){
36 ((Cat*)animal)->fun();
37 }
38 if(dynamic_cast<Dog*>(animal)){
39 ((Dog*)animal)->fun();
40 }
41 }
42 int main(){
43 Animal* pa=new Dog();
44 showAnimal(pa);
45 /* Dogdog=dynamic_cast<Dog>(pa);//轉換成功就返回一個合法的地址 //轉換失敗就返回NULL
46 cout<<「dog dog=」<<dog<<endl;
47 Cat* cat=dynamic_cast<Cat*>(pa);
48 cout<<「cat cat=」<<cat<<endl;
49 */
50 }

沒有恢復: 恢復之後(個性)
dog run dog run
animal fun dog fun

型別識別技術:

#include
typeid_info typeid (物件|型別)
typeid 獲取一個物件 或者型別的 資訊
name() 得到型別的名字
== 判斷兩個型別的type_info 是否一致就是同一型別
!= 判斷兩個型別的type_info 是否不等
如果 一個父類別物件的指針 指向型別物件時,一定要確保父類別型中的虛擬函式,如果沒有虛擬函式就會指向的物件 識別成父類別類物件。//******//

43 if(typeid(animal)==typeid(Cat)){
44 ((Cat
)animal)->fun();
45 }
46 if(typeid(animal)==typeid(Dog)){
47 ((Dog
)animal)->fun();
48 }

五 虛解構函式
當父類別物件的指針指向子類物件時 如果釋放物件空間 則會呼叫父類別解構函式
不會呼叫子類的解構函式。
當父類別型中出現 解構函式中 則應該在父類別的解構函式加virtual修飾。否則記憶體釋放不徹底。

1 #include
2 using namespace std;
3 class Animal{
4 public:
5 Animal(){
6 cout<<「Animal」<<endl;
7 }
8 virtual ~Animal(){
9 cout<<"~Animal"<<endl;
10 }
11 };
12 class Dog:public Animal{
13 public:
14 Dog(){
15 cout<<「Dog」<<endl;
16 }
17 ~Dog(){
18 cout<<"~Dog"<<endl;
19 }
20 };
21 int main(){
22 Animal*pa=new Dog();
23 delete pa;//子類中解構沒有呼叫 用虛解構解決
24 return 0;

六 抽象類
一個類中有純虛擬函式時,則這個類就是抽象類。
抽象類不能被範例化,除此之外,與其他類沒有任何區別。
class A{
public:
virtual void show()=0;//純虛擬函式
};
如果子類沒喲實現純虛擬函式 則子類稱爲抽象類。

1 #include
2 using namespace std;
3 class Animal{
4 public:
5 virtual void show()=0;
6 Animal(){
7 }
8 ~Animal(){
9 }
10 void run(){
11 }
12 };
13 class Dog:public Animal{
14 public:
15 void show(){}
16 };
17 int main(){
18 // Animal a;
19 Dog dog;
20 }

純抽象類:除了構造和解構之外,其他所有的函數都是純虛擬函式。

第九天五月十六***********
一、異常
1.1 爲什麼要有異常 和例外處理。
之前的表達錯誤 都是用返回值
C++ 可以使用異常表達程式狀態。
1.2 如何表達異常
通過throw 值;
throw 物件;
當異常發生時: 程式的預設處理是程式終止。

1.3 如何捕獲異常
在可能出現異常的程式碼上加 try
try{
foo()
}catch(型別的參照(指針) 變數名){

}catch(型別的參照(指針) 變數名){
}

2 #include
3 #include
4 #include
5 using namespace std;
6 void foo()throw(int,double,const char*){
7 srand(time(NULL));
8 int st=rand()%4;
9 if(0st){
10 cout<< 「正常」<<endl;
11 }else if(1
st){
12 throw 1;
13 }else if(2==st){
14 throw 2.5;
15 }else {
16 throw 「其他型號」;
17 }
18 cout<<「foo over」<<endl;
19 }
20
21 int main(){
22 try{
23 foo();}
24 catch(int e){
25 cout<<「app continue」<<endl;
26 }catch(double e){
27 cout<<「doble」<<endl;}
28 catch(const char* e){
29 cout<<「const char」<<endl;
30 }
31 cout<<「app continue」<<endl;
32
33 return 0;

1.4 異常在函數上的說明:
/這個函數可能拋出任何異常/
foo();
/函數呼叫時指定拋出int double const char異常*/
foo()throw(int,double,const char*){}

//如: void foo()throw(int,double,const char*){}
/不拋出任何異常/
void foo() throw();
1.5


1 #include
2 #include
3 using namespace std;
4 int main(){
5 int pi=(int)malloc(0xffffffff);
6 cout<<pi<<endl;
7 try{
8 int* newp=new int[0xffffffff];
9 cout<<newp<<endl;
10 }catch(bad_alloc& a){
11 cout<<a.what()<<endl;
12 }catch(…){
13 cout<<「have exception」<<endl;
14 }
15 }

結果:
0
St9bad_alloc

1.5 系統預定義異常
都是exception 的子類
在呼叫系統時, 或者 每個系統類的成員 函數時 可能會觸發系統定義的異常。則拋出哪個異常就處理哪個異常。
#include

1 #include
2 #include
3 #include
4 using namespace std;
5 int main(){
6 int pi=(int)malloc(0xffffffff);
7 cout<<pi<<endl;
8 try{
9 // int* newp=new int[0xffffffff];
10 // cout<<newp<<endl;
11 string abc=「hello」;
12 char c=abc.at(10);
13 }catch(bad_alloc& a){
14 cout<<a.what()<<endl;
15 }catch(out_of_range& e){
16 cout<<e.what()<<endl;
17 } catch(…){
18 cout<<「have exception」<<endl;
19 }
20 }

1.6 使用者自定義異常
定義異常(專案詳細設計已經設計好)
定義一個類表達異常。
根據條件 拋出異常
根據條件拋出異常 在函數宣告上指定拋出那些異常。
捕獲異常
在掉用函數的程式碼中加try{}catch(){}
處理異常
根據異常的型別做處理。

1 #ifndef DMSEXCEPTION_H
2 #define DMSEXCEPTION_H
3 #include
4 #include
5 using namespace std;
6 class DmsException{
7 string msg;
8 public:
9 DmsException(string msg=""){
10 this->msg=msg;
11 }
12 virtual const char*what()const throw(){
13 return msg.c_str();
14 }
15 };
16 class DmsClientException:public DmsException{
17 public:
18 DmsClientException(string msg=「dms client exception」):
19 DmsException(msg){
20
21 }
22 };
23 #endif
-----------------------------------------------------~
1 #include
2 #include
3 #include
4 #include 「dmsexception.h」
5 using namespace std;
6 void sendData()throw(DmsClientException,DmsException){
7 srand(time(NULL));
8 int rd=rand()%3;
9 if(rd0){
10 cout<<「rend success」<<endl;
11 }else if(1
rd){
12 throw DmsClientException();
13 }else if(2==rd)
14 {
15 throw DmsClientException(「dms exception:」);
16 }
17 }
18 int main(){
19 try{
20 sendData();
21 }catch(DmsClientException& a){
22 cout<<a.what()<<endl;
23 }catch(DmsException& e){
24 //如果這層處理不了 可以繼續拋
25 throw;
26 }
27 }

二 IO
2.1 IO流
cout clog cerr cin

1 #include
2 using namespace std;
3 int main(){
4 cout<<「hello」<<flush;
5 clog<<「world」;
6 cerr<<「abc123」;
7 }

使用IO流中的標頭檔案。cin cout —#include
istream ostream
字串流-------#include
istringstream ostringtream
檔案流---------#include
ifstream ofstream
無格式化的輸入輸出 fstream

1 #include
2 using namespace std;
3 int main(){
4 int c=cin.get(); //返回EOF
5 cout.put©;
6 char var_c;
7 cin.get(var_c);//當流物件變爲空就讀完了
8 cout.put(var_c);
9 return 0;
10 }

1 #include
2 #include
3 using namespace std;
4 int main(){
5 ifstream ifs(「io.cpp」);
6 int c;
7 cout<<ifs<<endl;
8 /* while((c=ifs.get())!=EOF){
9 cout.put©;}
10 ifs.close();*/
11 char cc;
12 while(ifs.get(cc)){
13 cout.put(cc);
14 }
15 ifs.close();
16 }

1 #include
2 using namespace std;
3 int main(){
4 char data2[10];
5 cin.getline(data2,10,’=’);// ‘=‘終止符,或超過10 才結束
6 cout<<data2<<endl;
7 char data[10];
8 cin.getline(data,10);
9 cout<<data<<endl;
10 /當流出錯是就拒絕IO/
11 cout<<cin<<endl;
12 /修正流,把流復位 .clear()/
13 if(!cin){
14 cin.clear();
15 /清理緩衝區/
16 cin.ignore(100,’\n’);//知道出現’\n’
17 }
18 cout<<cin<<endl;
19 int age=0;
20 cin>>age;
21 cout<<「age=」<<age<<endl;
22 }

ifstream ifs(「text.txt」);
ifstream ifs2;
ifstream.open()

1 #include
2 using namespace std;
3 int main(){
4 char c;
5 //cin.get©;
6 c=cin.peek();
7 cout<<c<<endl;
8 // cin.putback©;// get+ putback =peek*/
9 int age;
10 cin>>age;
11 cout<<age<<endl;
12 }

字串中常見的操作:
c中 常見字串
c++中字串操作
string abc(" ./a.out ")
abc+=「a」;
abc+=「b」;
istring ostring 讀取/輸出的數位來自字串
建構函式 無參即可 使用參數string 構造 使用C風格字串構造
運算子 << >>
如何把流物件 變成string

1 #include
2 #include
3 using namespace std;
4 int main(){
5 string abc("./a.out");
6 abc=abc+" a 「;
7 abc= abc+」 ab 「;
8 cout<<abc<<endl;
9 int age =34;
10 string name=「test」;
11 double salary=123.88;
12 ostringstream ofs;
13 ofs<<age;
14 ofs<<」:"<<name<<":"<<salary;
15 string mystr=ofs.str();//拼接字串
16 cout<<mystr;
17 }

1 #include
2 #include
3 using namespace std;
4 struct Date{
5 int year;
6 int month;
7 int day;
8 /過載這個物件的 輸出運算子/
9 friend ostream& operator<<(ostream& os, const Date& date);
10
11 };
12 ostream& operator<<(ostream& os,const Date& date){
13 return os<<date.year<<"-"<<date.month<<"-"<<date.day;
14 }
15 int main(){
16 string abc("./a.out");
17 abc=abc+" a 「;
18 abc= abc+」 ab 「;
19 cout<<abc<<endl;
20 int age =34;
21 string name=「test」;
22 double salary=123.88;
23 ostringstream ofs;
24 ofs<<age;
25 ofs<<」:"<<name<<":"<<salary;
26 string mystr=ofs.str();//拼接字串
27 cout<<mystr<<endl;
28 istringstream ifs(「55 openlab 8888」);
29 ifs>>age>>name>>salary;
30 cout<<" age= 「<<age<<」 name="<<name<<" salary="<<salary<<endl;
31 ostringstream ofs2;
32 Date date={2014,5,23};
33 ofs2<<date;
34 cout<<ofs2.str()<<endl;
35 }

2.5檔案的操作:
read
write
#include
istream& read( char* buffer, streamsize num );

#include
ostream& write( const char* buffer, streamsize num );
gccount() 得到實際讀取的位元組數

1 #include
2 #include
3 #include
4 using namespace std;
5 class Dog{
6 public:
7 // string name;//這是底層操作 記憶體中
8 char name[30];
9 int age;
10 Dog(const charname,int age){
11 strcpy(this->name,name);
12 this->age=age;
13 }
14 void show(){
15 cout<<name<<":"<<age<<endl;
16 }
17 };
int main(){
19 cout<<sizeof(Dog)<<endl;
20 Dog dog(「test」,8);
21 dog.show();
22 /以物件爲單位 寫如檔案/
23 ofstream ofs(「dog.dat」);
24 ofs.write((const char
)&dog,sizeof dog);
25 ofs.close();
26 /讀出檔案/
27 Dog dog2(「t」,1);
28 ifstream ifs(「dog.dat」);
29 if(!ifs){
30 cout<<「open failed」<<endl;
31 return -1;
32 }
33 ifs.read((char*)&dog2,sizeof(dog2));
34 dog2.show();
35 cout<<ifs.gcount()<<endl;//讀取的位元組數
36 ifs.close();
37 }

寫一個程式 讀取一個檔案 然後把讀取的每個字元和一個隨機的字元(0-255)做互斥或運算之後的數據寫入一個檔案。
再提供函數 傳入一個0-255之內的字元 用來解密。

1 #include
2 #include <stdlib.h>
3 #include
4 #include
5 #include
6 using namespace std;
7 /對檔案中的每個位元組互斥或/
8 void _xor(const charsrc,const chardes, char key){
9 ifstream ifs(src);
10 ofstream ofs1(des);
11 if(!ifs){
12 cout<<「open檔案失敗」<<endl;
13 return ;
14 }
15 if(!ofs1){
16 return ;
17 }
18 char data[100];
19 int i;
20 while(1){
21 ifs.read(data,100);
22 int num=ifs.gcount();
23 for( i=0;i<num;i++){
24 data[i]=data[i]^key;
25 }
26 ofs1.write(data,num);
27 if(num<100)
28 break;
29 }
30
31 ifs.close();
32 ofs1.close();
33 }
34 void encode(const charsrc,const chardes){
35 srand(time(NULL));
36 int key=rand()%256;
37 cout<<「加密key=」<<key<<endl;
38 _xor(src,des,key);
39 }
40 void decode(const charsrc,const chardesc,unsigned char key){
41 _xor(src,desc,key);
42 ifstream of(desc);
43 if(!of){
44 cout<<「of open file failed」<<endl;
45 return ;
46 }
47 char data[100];
48 while(1){
49 of.read(data,100);
50 cout<<data<<endl;
51 int num=of.gcount();
52 if(num<100)
53 break;
54 }
55 }
56 int main(int agrc,char**argv){
57 if(agrc<3){
58 cout<<「使用錯誤 應如下」<<endl;
59 cout<<「a.out 原始檔 目標檔案」<<endl;
60 cout<<「a.out 原始檔 目標檔案 金鑰」<<endl;
61 }
62 if(agrc==3){
63 cout<<「加密檔案」<<endl;
64 encode(argv[1],argv[2]);
65 }
66 if(agrc>3){
67 cout<<「解密檔案」<<endl;
68 decode(argv[1],argv[2],atoi(argv[3]));
69 }
70 }

C++中四種強制型別轉換的區別 2013-10-19 22:35:47
分類: C/C++

使用標準C++的類別型轉換符:static_cast、dynamic_cast、reinterpret_cast和const_cast。

1、static_cast
用法:static_cast (expression)
該運算子把expression轉換爲type-id型別,但沒有執行時型別檢查來保證轉換的安全性。它主要有如下幾種用法:
(1)用於類層次結構中基礎類別和派生類之間指針或參照的轉換
進行上行轉換(把派生類的指針或參照轉換成基礎類別表示)是安全的
進行下行轉換(把基礎類別的指針或參照轉換爲派生類表示),由於沒有動態型別檢查,所以是不安全的
(2)用於基本數據型別之間的轉換,如把int轉換成char。這種轉換的安全也要開發人員來保證
(3)把空指針轉換成目標型別的空指針
(4)把任何型別的表達式轉換爲void型別
注意:static_cast不能轉換掉expression的const、volitale或者__unaligned屬性。

2、dynamic_cast
用法:dynamic_cast (expression)
該運算子把expression轉換成type_id型別的物件。type_id必須是類的指針、參照或者void*;
如果type_id是類指針型別,那麼expression也必須是一個指針,如果type_id是一個參照,那麼expression也必須是一個參照。
dynamic_cast主要用於類層次間的上行轉換和下行轉換,還可以用於類之間的交叉轉換。
在類層次間進行上行轉換時,dynamic_cast和static_cast的效果是一樣的;
在進行下行轉換時,dynamic_cast具有型別檢查的功能,比static_cast更安全。
class B
{
public:

  int m_iNum;
   virtual void foo();

};

class D:public B
{
public:
char *m_szName[100];
};

void func(B *pb)
{
D *pd1 = static_cast<D *>(pb);
D *pd2 = dynamic_cast<D *>(pb);
}
在上面的程式碼段中,如果pb指向一個D型別的物件,pd1和pd2是一樣的,並且對這兩個指針執行D型別的任何操作都是安全的;
但是,如果pb指向的是一個B型別的物件,那麼pd1將是一個指向該物件的指針,對它進行D型別的操作將是不安全的(如存取m_szName),
而pd2將是一個空指針。
另外要注意:B要有虛擬函式,否則會編譯出錯;static_cast則沒有這個限制。
這是由於執行時型別檢查需要執行時型別資訊,而這個資訊儲存在類的虛擬函式表(
關於虛擬函式表的概念,詳細可見)中,只有定義了虛擬函式的類纔有虛擬函式表,
沒有定義虛擬函式的類是沒有虛擬函式表的。

另外,dynamic_cast還支援交叉轉換,如下所示。

class A
{
public:
int m_iNum;
virtual void f(){}
};

class B:public A
{

};

class D:public A
{

};

void foo()
{
B *pb = new B;
pb->m_iNum = 100;
D *pd1 = static_cast<D *>(pb); //compile error
D *pd2 = dynamic_cast<D *>(pb); //pd2 is NULL
delete pb;
}
在函數foo中,使用static_cast進行轉換是不被允許的,將在編譯時出錯,而使用dynamic_cast轉換則是允許的,結果是空指針。

3、reinterpret_cast
用法:reinterpret_cast (expression)
type-id必須是一個指針、參照、算術型別、函數指針或者成員指針。
它可以把一個指針轉換成一個整數,也可以把一個整數轉換成一個指針(先把一個指針轉換成一個整數,
在把該整數轉換成原型別的指針,還可以得到原先的指針值)。
該運算子的用法比較多。
(static_cast .與. reinterpret_cast比較,見下面 下麪 )
該運算子平臺移植性比價差。

4、const_cast
用法:const_cast (expression)
該運算子用來修改型別的const或volatile屬性。除了const 或volatile修飾之外, type_id和expression的型別是一樣的。
常數指針被轉化成非常數指針,並且仍然指向原來的物件;
常數參照被轉換成非常數參照,並且仍然指向原來的物件;常數物件被轉換成非常數物件。
volatile和const型別,舉例如下所示。
class B
{
public:
int m_iNum;
}

void foo()
{
const B b1;
b1.m_iNum = 100; //comile error
B b2 = const_cast(b1);
b2. m_iNum = 200; //fine
}
上面的程式碼編譯時會報錯,因爲b1是一個常數物件,不能對它進行改變;
使用const_cast把它轉換成一個非常數物件,就可以對它的數據成員任意改變。注意:b1和b2是兩個不同的物件。

5、比較
(1)dynamic_cast vs static_cast
class B
{

};

class D : public B
{

};

void f(B* pb)
{
D* pd1 = dynamic_cast<D*>(pb);
D* pd2 = static_cast<D*>(pb);
}
If pb really points to an object of type D, then pd1 and pd2 will get the same value. They will also get the same value if pb == 0.
If pb points to an object of type B and not to the complete D class, then dynamic_cast will know enough to return zero. However, static_cast relies on the programmer’s assertion that pb points to an object of type D and simply returns a pointer to that supposed D object.

即dynamic_cast可用於繼承體系中的向下轉型,即將基礎類別指針轉換爲派生類指針,比static_cast更嚴格更安全。dynamic_cast在執行效率上比static_cast要差一些,但static_cast在更寬上範圍內可以完成對映,這種不加限制的對映伴隨着不安全性。static_cast覆蓋的變換型別除類層次的靜態導航以外,還包括無對映變換、窄化變換(這種變換會導致物件切片,丟失資訊)、用VOID*的強制變換、隱式型別變換等...

(2)static_cast vs reinterpret_cast
reinterpret_cast是爲了對映到一個完全不同類型的意思,這個關鍵詞在我們需要把型別對映回原有型別時用到它。我們對映到的型別僅僅是爲了故弄玄虛和其他目的,這是所有對映中最危險的。(這句話是C++程式設計思想中的原話)
static_cast 和 reinterpret_cast 操作符修改了運算元型別。它們不是互逆的; static_cast 在編譯時使用型別資訊執行轉換,在轉換執行必要的檢測(諸如指針越界計算, 型別檢查). 其運算元相對是安全的。另一方面;reinterpret_cast 僅僅是重新解釋了給出的物件的位元模型而沒有進行二進制轉換, 例子如下:

int n=9; double d=static_cast < double > (n);
上面的例子中, 我們將一個變數從 int 轉換到 double。 這些型別的二進制表達式是不同的。 要將整數 9 轉換到 雙精度整數 9,static_cast 需要正確地爲雙精度整數 d 補足位元位。其結果爲 9.0。
而reinterpret_cast 的行爲卻不同:
int n=9;
double d=reinterpret_cast<double & > (n);
這次, 結果有所不同. 在進行計算以後, d 包含無用值. 這是因爲 reinterpret_cast 僅僅是複製 n 的位元位到 d, 沒有進行必要的分析.
因此, 你需要謹慎使用 reinterpret_cast.

引自:http://www.cppblog.com/lapcca/archive/2010/11/30/135081.aspx

補充:
(1)static_cast:在功能上基本上與C風格的型別轉換一樣強大,含義也一樣。它有功能上的限制。例如,你不能用static_cast像用C風格轉換一樣把struct轉換成int型別或者把double型別轉換成指針型別。另外,static_cast不能從表達式中去除const屬性,因爲另一個新的型別轉換符const_cast有這樣的功能。
可以靜態決議出型別的轉換可能性,即使是在繼承體系中,即使包括了多重繼承和虛繼承,只要可以進行靜態決議就可以轉換成功
(2)const_cast:用於型別轉換掉表達式的const或volatile屬性。通過使用const_cast,你向人們和編譯器強調你通過型別轉換想做的只是改變一些東西的constness或者volatieness屬性。這個含義被編譯器所約束。如果你試圖使用const_cast來完成修改constness或者volatileness屬性之外的事情,你的型別轉換將被拒絕。
(3)dynamic_cast:它被用於安全地沿着類的繼承關係向下進行型別轉換。這就是說,你能用dynamic_cast把指向基礎類別的指針或參照轉換成指向其派生類或其兄弟類的指針或參照,而且你能知道轉換是否成功。失敗的轉換將返回空指針(當對指針進行型別轉換時)或者拋出異常(當對參照進行型別轉換時)。
(4)reinterpret_cast:使用這個操作符的型別轉換,其轉換結果幾乎都是執行期定義。因此,使用reinterpret_cast的程式碼很難移植。reinterpret_casts的最普通的用途就是在函數指針型別之間進行轉換。
舉例如下:

#include
using namespace std;

class A
{
public:
virtual void foo()
{
}
};

class B
{
public:
virtual void foo()
{
}
};

class C : public A , public B
{
public:
virtual void foo() { }
};

void bar1(A *pa)
{
B pc = dynamic_cast<B>(pa);
}

void bar2(A *pa)
{
B pc = static_cast<B>(pa); //error
}

void bar3()
{
C c;
A pa = &c;
B pb = static_cast<B>(static_cast<C
>(pa));
}

int main()
{
return 0;
}
A、bar1無法通過編譯
B、bar2無法通過編譯
C、bar3無法通過編譯
D、bar1可以正常執行,但是採用了錯誤的cast方法
解答:
選B。
dynamic_cast是在執行時遍歷繼承樹,所以,在編譯時不會報錯。但是因爲A和B沒啥關係,所以執行時報錯(所以A和D都是錯誤的)。
static_cast:編譯器隱式執行的任何型別轉換都可由它顯示完成。其中對於:(1)基本型別。如可以將int轉換爲double(編譯器會執行隱式轉換),但是不能將int用它轉換到double(沒有此隱式轉換)。(2)對於使用者自定義型別,如果兩個類無關,則會出錯(所以B正確),如果存在繼承關係,則可以在基礎類別和派生類之間進行任何轉型,在編譯期間不會出錯。所以bar3可以通過編譯(C選項是錯誤的)。