C++進階程式設計總結C++進階程式設計筆記
1. 頭檔案添加注釋(檔案名, 作者, 函數簡介, 日期).
2. 當頭檔案數目較多時, 應将頭檔案放在include目錄下, 源檔案放在source目錄下.
3. const常量有資料類型, 宏常量沒有資料類型.
4. 對于重載指派運算符, 應該用"引用傳遞"方式
5. 函數入口處用assert檢查
6. 記憶體配置設定方式有三種, 從靜态存儲區域配置設定, 從棧上配置設定, 從堆上配置設定, 靜态存儲區包括全局變量,
static變量等.
7. C++/C語言,沒有辦法知道指針所指的記憶體容量.
8. 指針消亡了, 不表示所指的記憶體會被自動釋放。記憶體被釋放了,
不表示指針會消亡或者成了NULL指針.
9. 指針被free或delete之後, 别忘記設定為NULL.
10. malloc不調用構造函數, new自動調用構造函數,
free和delete類似.
11. 為什麼要用重載? (1) 便于記憶。(2) 不得不, 比如說類的多個構造函數
12. 不能編譯頭檔案
全局變量定義時, (直接指派), extern無作用
全局變量聲明時, extern告訴編譯器, 定義部分在其它子產品中
全局變量預設extern
13. 重載: 同一個類中, 或都是全局函數.
覆寫: 分别位于派生類與基類中, 函數名與參數都相同,有virtual關鍵字,用于多态.
隐藏: 分别位于派生類與基類中, 隻要同名, 且非覆寫, 均為隐藏.
14. 函數參數的預設值,隻能出現在函數的聲明中.
15. 操作符重載。調用時, 普通函數參數出現在圓括号内, 對于運算符, 參數出現在其左右兩側。定義時,可定義為全局函數和類的成員函數,後者比前者少了一個參數。
16. 類的構造次序,先構造基類,再構造構造函數的參數,再構造成員,再構造自己。析構完全相反。
17. String a("hello"); String b=a; 其實是調用了b的拷貝構造函數,最好寫成String b(a).
18. 對于一個類,編譯器預設生成4個函數,無參構造函數,拷貝構造函數,析構函數,指派函數
19. 類的析構函數,應為虛函數, 多态
20. 對于非内部資料類型的輸入參數,應該将“值傳遞”的方式,改為“const 引用傳遞”,目的是提高效率。例如,将void Func(A a),改為 void Func(const A &a)。
21. 引用被建立的同時,必須被初始化,一旦引用被初始化,就不能改變引用的關系。
22. 對比于C語言的函數,C++增加了重載,内聯,const和virtual四種新機制,重載和内聯機制,既可以用于全局函數,也可用于類的成員函數,const與virtual機制,僅用于類的成員函數
23. 指派符号的重載,不能為友元,隻能是類的成員函數
C++進階程式設計
C++程式設計學習
概述
面向對象的方法簡介
對象----存在即合理
抽象性–物以類聚
封裝----事物的封閉性
繼承----事物的相似性
多态----事物的多樣性
面向對象方法(類與對象)
C++概述
1.什麼是C++?
C to C++:
* C語言的超集
* 面向對象程式設計
* 可移植性,不犧牲性能和底層功能
C++ compiler:将C++代碼翻譯為C代碼
2.C++适合?
* 算法
* 應用開發
* C/C++伺服器
C++設計原則
C++設計成使用靜态型别機制、與C同樣高效,可移植的,多用途程式設計語言。
C++設計成直接的和廣泛的,支援多種程式設計風格(程式化程式設計、資料抽象化、面向對象程 序設計、泛型程式設計)。
C++設計成給程式設計者,更多的選擇,即使可能導緻程式設計者選擇錯誤。
C++設計成盡可能與C相容,提供一個從C到C++的平滑過渡。
C++避免平台限定,或沒有普遍用途的特性。
C++不使用會帶來額外開銷的特性。
C++設計成無需複雜的程式設計環境。
UNIX鐵律: K.I.S.S-Keep It Simple,Stupid!
&參考
C++大學教程(推薦)
C++Primer
C++程式設計思想
&開發環境
Visual Studio 201X Community
Code::Block
Qt Creator
C++的第一個程式
第一個C++程式:
#include<iostream>
int main(void)
{
std::cout<<"hello world!"<<std::endl;
return 0;
}
流的概念及用途
C++的I/O是以位元組流的形式實作的,流(stream)實際上就是一個位元組序列。
輸入流: 在輸入操作中,位元組從輸入裝置(如鍵盤、磁盤、網絡連接配接等)流向記憶體;
輸出流: 在輸出操作中,位元組從記憶體流向輸出裝置(如顯示器、列印機、磁盤、網絡連接配接等);
這裡“流”,試圖說明字元,随着時間順序生成或消耗的。
輸人/輸出系統的任務,實際上就是以一種穩定、可靠的方式,在裝置與記憶體之間傳輸資料。
C++并沒有直接定義,進行輸入輸出的任何語句,這些功能是由标準IO庫完成。
命名空間(namespace)
實際上就是,一個由程式設計者命名的記憶體區域,程式設計者,可以根據需要指定一些有名字的空間域,把一些全局實體,分别放在各個命名空間中,進而與其它全局實體分隔開來。
命名空間是ANSIC++引入的,可以由使用者命名的作用域,用來處理程式中,常見的同名沖突。
std::cout std::cin std::endl
+使用命名空間:
using std::cout;
using namespace std;
using namespace std; //使用命名空間
cout<<"hello world!"<<endl;
C++程式的執行過程
1.C++主要有三個編譯階段:預處理、轉譯成目标代碼和連結(最後的兩個階段,一般才視為真正的“編譯”)。
2.C++預處理指令和C基本相同
C/C++的字元串比較
0.C語言字元串練習 char st[100];
0.C++語言字元串練習 string str;
1.檢測字元串長度 int len = strlen(st);
1.檢測字元串長度 int len = str.length();
2.字元串比較 strcmp(st1, st2);
2.字元串比較 str1.compare(str2);
3.在已有字元串後,追加新串 strcat(st1, st2); strncat(st1,st2,n);
3.在已有字元串後,追加新串 str1 += str2; str1.append(str2);
4.字元串拷貝 strcpy(st1,st2); strncpy(st1,st2, n);
4.字元串拷貝 str2 = str1; str2 = str1.substr();
5.字元串查找 where = strchr(st, ch)
5.字元串查找 where = str1.find(str2);
#include<string>
stringstr("hello");
cout <<str.length() << endl;
str.append("world!");//在已有字元串後追加新串
cout << str<< endl;
C/C++資料類型與變量
C/C++變量
程式運作過程中,值能否發生改變,分為**常量和變量**
從變量作用域的大小考慮:全局變量,局部變量
全局變量:定義在所有的函數體之外,在程式開始運作時,配置設定存儲空間,在程式結束時,釋放存儲空間
函數中定義的變量,稱為局部變量(Local Variable)
從變量的生命周期考慮: 靜态生存周期和動态生存周期
動态存儲變量:變量僅在需要的時候,配置設定和占用記憶體
靜态存儲變量:變量在程式運作過程中,占用固定的記憶體
從變量的在記憶體中位置考慮:普通變量與指針變量
動态記憶體配置設定
所謂動态記憶體配置設定,指在程式運作期間,根據實際需要随時申請記憶體,在不需要時釋放
new/delete是C++的運算符
用于申請動态記憶體和釋放記憶體
new運算符的文法格式為:指針=new 資料類型;
• int *p=new int;
• int *p=new int[30];
delete運算符的功能,用來删除是用new建立的對象,或一般類型的指針,其格式如下:delete<指針名>
使用delete,也可以用來删除使用new建立的對象數組,其使用格式如下:delete [] 指針名
#include<string.h>
int main()
char *str;
str = new char[10];
strcpy(str,"hello");
delete []str;
杜絕“野指針”
指針用來存放某個變量的位址值的一種變量
“野指針”不是NULL指針,指向“垃圾”記憶體的指針,“野指針”的危險之處,在于if語句不起作用。
任何指針變量剛被建立時,不會自動成為NULL指針,預設值是随機的。是以,指針變量在建立的同時,應當被初始化,要不将指針設定為NULL,要麼指向合法的記憶體。
char *p = NULL;
char * str = (char *)malloc(100);
指針p被free或者delete之後,應設定為NULL。
指針操作超越了變量的作用範圍
程式不能傳回指向棧記憶體的指針或引用。
數組與指針
-任何一個數組的名字是一個常量指針,值是該數組的首元素的位址值
-在C++中引用一個數組元素,有兩種等價的方法:
-下标法:如a[j]或p[j]形式,比較直覺
-指針法:如*(a+j)或 *(p+j)形式,比較高效
-數組名作為函數形參時,在函數體内,失去了本身的内涵,僅僅是一個指針。
-指向數組的指針,另外一種變量類型(在linux或win32平台下,長度為4),僅僅意味着數組的存放位址;
數組指針與指針數組
總結
new運算符根據對象的類型,自動決定其大小,不使用sizeof運算符,malloc要指定配置設定存儲空間的大小;
new傳回指向此類型的指針,不用進行強制指針類型轉換。malloc傳回指向void*類型的指針。
如果在申請動态記憶體時,找不到足夠大的記憶體塊,malloc和new将傳回NULL指針,宣告記憶體申請失敗
用free或delete釋放記憶體之後,沒有将指針設定為NULL。導緻産生“野指針”。防止使用指針值為NULL的記憶體。
動态記憶體的申請與釋放必須配對,防止記憶體洩露。
引用與函數傳參
什麼是引用?
引用就是某一變量(目标)的一個别名,對引用的操作與對變量直接操作完全一樣。
引用(&)在此是起标示作用,不是求位址運算,定義格式
<類型>&<引用名>(<變量名>);
<類型>&<引用名>=(<變量名>);
int a = 3;
int &m = a; //定義引用并初始化
int n = m; //将引用賦給變量
int *p = &m;
m = m + 5; // a = 8, 對引用的操作,就是對被引用者的操作
引用詳解
初始化與指派
定義引用時,必須初始化;
可以将一個引用賦予給某個變量;
引用聲明完畢後,相當于目标變量名有兩個名稱,即該目标原名稱和引用名,不能再把該引用名,作為其它變量名的别名。
指針和引用的差別
指針通過位址,間接通路某個變量
引用通過别名,直接通路某個變量
引用一般用作函數的參數,或函數的傳回值
如果既要利用引用提高使用效率,又要保護傳遞給函數的資料,不在函數中被改變,就應當使用常引用。
引用的用途
C語言中沒有引用,C++中才有引用,引用的主要用途,就是在函數傳參和傳回值上。
使用引用作為傳遞函數的參數,在記憶體中,沒有産生實參的副本,直接對實參操作。
如果輸入參數,以值傳遞的方式傳遞對象,宜改用“const &”方式來傳遞,這樣可以省去臨時對象的構造和析構過程,提高效率。
如果既要利用引用,調高使用效率,又要保護傳遞給函數的資料,不在函數中被改變,就應當使用常引用。
如果函數的傳回值是一個對象,有些場合,用“引用傳遞”替換“值傳遞”,可以提高效率,有些場合不可以。
常引用
聲明一個引用,不是新定義一個變量,隻是該引用名是目标變量的一個别名,本身不是一種資料類型,引用不占存儲單元。對引用取位址,就是對目标變量取位址
非const引用,隻能綁定到該引用同類型的變量。const引用,可以綁定到不同,但相關類型的對象, 或着綁定到右值。
常引用的聲明方式:
const 類型标示符 &引用名 = 目标變量名;
用這種方式聲明的引用,不能通過引用,對變量的值進行修改,但目标自身任然能夠改變,這是語言的不一緻性。
double dvalue = 3.14;
const int &pd =dvalue;
dvalue = 50.6;
cout << pd<<"\n"<<dvalue <<endl;
函數傳回一個類型的引用
注意:不允許傳回的引用,對應于一個局部變量
int &getlnt(const int v)
int *p = new int(v);
return *p;
int &n =getlnt(123456789);
cout << n<< endl;
int *pp = &n;
delete pp;
return語句,不可傳回指向“棧記憶體”的,“指針”,或者“引用”,該記憶體在函數體結束時,被自動銷毀。
C/C++函數說明
C++函數說明
C/C++中的函數說明:
四個組成部分:傳回類型、函數名、形式參數表、函數體
函數的申明與實作,函數的調用與傳參(形參與實參)
函數與指針:指針函數與函數指針
C++增強了函數類型:
基礎函數:内聯函數,函數重載,模闆函數(泛型程式設計)
成員函數:構造/析構函數,常成員函數,靜态成員函數,虛函數
内聯函數(inline)
内聯(内嵌)函數,主要解決的是程式的運作效率問題。
解決手段,在編譯階段,編譯器就會把每次調用内聯函數的地方,都替換為該函數體内的代碼。
引入原因:解決程式中,一些函數體代碼不是很大,卻有被頻繁調用的函數,調用效率問題。
解決方法:以目标代碼的增加為代價,換取時間上的節約。
當在一個函數定義或聲明前,加上關鍵字inline,則把該函數定義為内聯函數。
inline int f(int x)
return x * x;
int x(2);
cout << f(x)<< endl;
cout << f(x +1) <<endl;
使用内聯函數的注意事項
内聯函數的定義,必須出現在該函數第一次被調用前;
内聯函數不能有複雜的控制語句,如switch,goto和while。
遞歸函數不能是内聯函數。類結構中所有的在類,說明内部定義的函數,都是内聯函數。
内聯函數具有與帶參的宏定義#define,相同的作用和相似的機理,但内聯函數具有宏定義所沒有的優點,沒有缺點,消除了宏定義的不安全性。
函數重載
函數重載又稱為函數的多态性
指同一個函數名對應着多個不同的函數。
“不同”是指,這些函數的形參表,必須互不相同,或者是形參的個數不同,或者是形參是類型不同,或者是兩者都不相同,否則,将無法實作函數重載。
下面是合法的重載函數:
int funcl (int ,int);
int funcl(int );
double funcl(int,long);
double funcl(long);
#include <iostream>/*函數重載*/
int add(int,int);
double add(double,double);
cout<<add(10,20)<<endl;
cout<<add(10.0,20.5)<<endl;
int add(int x,int y)
cout<<"int"<<endl;
return x+y;
double add(double x,double y)
cout<<"double"<<endl;
注意:
重載函數的類型,即函數的傳回類型,可以相同,也可以不同。如果僅僅是傳回類型不同,函數名相同、形參表也相同,則是不合法的,編譯器會報“文法錯誤”。如:
int funcl (int a,int b);
double funcl (int a,int b);
除形參名外都相同的情況,編譯器不認為是重載函數,隻認為是對同一個函數原型的多次聲明。
在調用一個重載函數funcl()時,編譯器必須判斷函數名funcl,到底是指哪個函數。通過編譯器,根據實參的個數和類型,對所有funcl()函數的形參一一進行比較,調用一個最比對的函數。
帶預設參數值的函數
在C++語言中調用函數時,通常要為函數的每個形參,給定對應的實參。若沒有給出實參,按指定的預設值進行工作。
當一個函數既有定義,又有聲明時,形參的預設值,必須在聲明中指定,不能放在定義中指定。隻有當函數沒有聲明時,才可以在函數定義中指定形參的預設值。
預設值的定義,必須遵守從右到左的順序,如果某個形參沒有預設值,則左邊的參數就不能有預設值。如:
void funcl(int a, double b=4.5, int c=3);//合法
void funcl(int a=1, double b, int c=3);//不合法
在進行函數調用時,實參與形參按從左到右的順序進行比對,當實參的數目少于形參時,如果對應位置形參,沒有設定預設值,就會産生編譯錯誤;如果設定了預設值,編譯器将沒有對應實參的形參取預設值。
注意
形參的預設值,可以是全局常量、全局變量、表達式、函數調用,但不能為局部變量。例如,
下例不合法:
void funcl()
int k;
void g(int x=k);//k為局部變量
使用預設參數時,主要滿足函數重載條件;
void fun (int x, int y=0);
void fun(int x);
此時函數fun不能進行重載,因為編譯器不能唯一确定,調用哪個函數(fun(3)或fun(3,0)均可)。
##封裝性
封裝是面向對象的特征之一,對象和類概念的主要特性。
封裝,也就是把客觀事物封裝成抽象的類,類可以把自己的資料和方法,隻讓可信的類,或者對象操作,對不可信的進行資訊隐藏。
具備封裝性(Encapsulation)的面向對象程式設計,隐藏了某一方法的具體執行步驟,取而代之的是通過消息傳遞機制傳送消息。
封裝
關鍵字:public、protected、prviate
• 修飾成員變量和成員函數;
• 繼承時使用
破壞封裝:關鍵字friend
• 友元類和友元函數
封裝好的結構體
#include <iostream>
#include <string.h>
struct Person
char *m_name;
int m_age;
void setName(constchar*);
void setAge(int);
void disp();
};
void Person::setName(const char* name)
m_name = newchar[100];
strcpy(m_name, name);
void Person::setAge(int age)/*作用域=作用域名+作用域符*/
m_age = age;
void Person::disp()
cout <<"Name:" << m_name << endl;
cout <<"Age:" << m_age << endl;
Person somebody;
somebody.setName("DaXian666");
somebody.setAge(23);
somebody.disp();
定義類來實作資料隐藏
class Person
private://資料
public://函數
void Person::setName(const char*name)
void Person::setAge(int age)
cout << "Name:" <<m_name <<endl;
Person p;
p.setName("DaXian666");
p.setAge(23);
p.disp();
如何實作資料隐藏
引入class類型
對資料成員進行保護
增加存取範圍
• 私有成員private
• 保護成員protected
• 公共成員public
在C++中,class與struct的差別:
struct的預設作用域為public
class的預設作用域為private
類定義格式的構成
說明部分:說明類中的成員,包含資料成員的說明和成員函數的說明;
實作部分:對成員函數的定義
類的一般定義格式
類與對象
C++是為了解決編寫大程式過程中的困難而産生的。
對象:客觀世界中任何一個事物,都可以看成一個對象( object )。
對象組成:
資料——描述對象的屬性
函數——行為(操作代碼),根據外界給的資訊進行相應操作的代碼,外界給的資訊進行相應操作的代碼。
具有相同的屬性和行為的對象抽象為類(class )
類是對象的抽象
對象則是類的特例
類中的權限
一般的做法:将需要被外界調用的成員函數指定為public,類的對外接口。
并非要求把所有成員函數都指定為public
有的函數不是準備為外界調用的,支援其它函數的操作,就應該指定為private 。
這種函數稱為其它成員的工具函數(utility function),類外使用者不能調用。
私有的成員函數隻能被本類中的其它成員函數所調用,不能被類外調用。
成員函數可以通路本類中任何成員(包括私有的和公用的),可以引用在本作用域中有效的資料。
C++ 标準庫
#pragma once
//上下等價
#ifndef BitFlip_H
#define BitFlip_H
//the content of BitFlip_H header file
#endif // !BitFlip
1. 切勿在頭檔案中,使用using指令,或者using 聲明,否則,添加頭檔案,都要用這個空間。
2. 命名空間嵌套在17标準中,得到了極大的簡化
3. 聲明變量的時候不指定值,變量的值取決于這塊記憶體的值
4. 允許if中加入初始化器
if (int a = 0;a>0) {
}
5.
std::array<int, 3> arr = { 1,1,1 };//固定大小#include<array> 11标準
intsize=std::size(arr);
6. C++程式中記憶體分為堆和棧,函數的執行一般在棧上進行,用完釋放。明确的配置設定記憶體,一般是放在了堆上。(在函數中聲明的,也要釋放掉)
int* a;
*a = 0;
delete a;//delete[]a---删除數組
a = nullptr; 删除指針後,最好進行置空操作,防止再次的使用
7.
//make_unique在超過作用域,或者被删除時,就會自動釋放記憶體資源
autoan=make_unique<BitFlip>();
//共享指針,每次指定make_shared時,都會遞增一個引用計數,表示資料又多了一個擁有者,當超過作用域時,就遞減引用計數,直到為0時,釋放資源
auto an1 =make_shared<BitFlip>();
8.const多種用法(如果需要給函數傳遞對象,最好按const引用傳遞,這樣可以防止多餘的複制。如果需要改變對象,則需要傳遞非const引用)
//保護參數
void get(const string* a) {
*a ="ddd";//不通過
const int a = 1; //保證代碼不會改變該變量的值
//變量x和引用變量re,指向同一個值,改變一個,都會變
int x = 1;
int& re = x;
9.auto 去除了const限定和引用 因為auto建立一個副本。除非用auto &
//第一,const函數傳回值(即指針)的内容不能被修改,該傳回值隻能被賦給加const修飾的同類型指針
//第二,任何不會修改資料成員的函數,都應該聲明為const類型。如果在編寫const成員函數時,不慎修改了資料成員,或者調用了其它非const成員函數,編譯器将指出錯誤,無疑會提高程式的健壯性
const int& Get() const {