C++高级编程总结
C++高級編程總結
C++高級編程筆記
- 頭文件添加注釋(文件名, 作者, 函數簡介, 日期).
- 當頭文件數目較多時, 應將頭文件放在include目錄下, 源文件放在source目錄下.
- const常量有數據類型, 宏常量沒有數據類型.
- 對于重載賦值運算符, 應該用"引用傳遞"方式
- 函數入口處用assert檢查
- 內存分配方式有三種, 從靜態存儲區域分配, 從棧上分配, 從堆上分配, 靜態存儲區包括全局變量, static變量等.
- C++/C語言,沒有辦法知道指針所指的內存容量.
- 指針消亡了, 不表示所指的內存會被自動釋放。內存被釋放了, 不表示指針會消亡或者成了NULL指針.
- 指針被free或delete之后, 別忘記設置為NULL.
- malloc不調用構造函數, new自動調用構造函數, free和delete類似.
- 為什么要用重載? (1) 便于記憶。(2) 不得不, 比如說類的多個構造函數
- 不能編譯頭文件
全局變量定義時, (直接賦值), extern無作用
全局變量聲明時, extern告訴編譯器, 定義部分在其它模塊中
全局變量默認extern - 重載: 同一個類中, 或都是全局函數.
覆蓋: 分別位于派生類與基類中, 函數名與參數都相同,有virtual關鍵字,用于多態.
隱藏: 分別位于派生類與基類中, 只要同名, 且非覆蓋, 均為隱藏. - 函數參數的缺省值,只能出現在函數的聲明中.
- 操作符重載。調用時, 普通函數參數出現在圓括號內, 對于運算符, 參數出現在其左右兩側。定義時,可定義為全局函數和類的成員函數,后者比前者少了一個參數。
- 類的構造次序,先構造基類,再構造構造函數的參數,再構造成員,再構造自己。析構完全相反。
- String a(“hello”); String b=a; 其實是調用了b的拷貝構造函數,最好寫成String b(a).
- 對于一個類,編譯器默認生成4個函數,無參構造函數,拷貝構造函數,析構函數,賦值函數
- 類的析構函數,應為虛函數, 多態
- 對于非內部數據類型的輸入參數,應該將“值傳遞”的方式,改為“const 引用傳遞”,目的是提高效率。例如,將void Func(A a),改為 void Func(const A &a)。
- 引用被創建的同時,必須被初始化,一旦引用被初始化,就不能改變引用的關系。
- 對比于C語言的函數,C++增加了重載,內聯,const和virtual四種新機制,重載和內聯機制,既可以用于全局函數,也可用于類的成員函數,const與virtual機制,僅用于類的成員函數
- 賦值符號的重載,不能為友元,只能是類的成員函數
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
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;
#include
using namespace std; //使用命名空間
int main(void)
{
cout <<“hello world!”<<endl;
return 0;
}
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
#include
using namespace std;
int main(void)
{
string str(“hello”);
cout << str.length() << endl;
str.append(" world!");//在已有字符串后追加新串
cout << str << endl;
return 0;
}
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
#include<string.h>
using namespace std;
int main()
{
char *str;
str = new char[10];
strcpy(str,“hello”);
cout << str << endl;
delete []str;
return 0;
}
杜絕“野指針”
指針用來存放某個變量的地址值的一種變量
“野指針”不是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 類型標示符 &引用名 = 目標變量名;
用這種方式聲明的引用,不能通過引用,對變量的值進行修改,但目標自身任然能夠改變,這是語言的不一致性。
#include
using namespace std;
int main()
{
double dvalue = 3.14;
const int &pd = dvalue;
dvalue = 50.6;
cout << pd <<"\n"<<dvalue <<endl;
return 0;
}
函數返回一個類型的引用
注意:不允許返回的引用,對應于一個局部變量
#include
using namespace std;
int &getlnt(const int v)
{
int *p = new int(v);
return *p;
}
int main()
{
int &n = getlnt(123456789);
cout << n << endl;
int *pp = &n;
delete pp;
return 0;
}
return語句,不可返回指向“棧內存”的,“指針”,或者“引用”,該內存在函數體結束時,被自動銷毀。
C/C++函數說明
C++函數說明
C/C++中的函數說明:
四個組成部分:返回類型、函數名、形式參數表、函數體
函數的申明與實現,函數的調用與傳參(形參與實參)
函數與指針:指針函數與函數指針
C++增強了函數類型:
基礎函數:內聯函數,函數重載,模板函數(泛型編程)
成員函數:構造/析構函數,常成員函數,靜態成員函數,虛函數
內聯函數(inline)
內聯(內嵌)函數,主要解決的是程序的運行效率問題。
解決手段,在編譯階段,編譯器就會把每次調用內聯函數的地方,都替換為該函數體內的代碼。
引入原因:解決程序中,一些函數體代碼不是很大,卻有被頻繁調用的函數,調用效率問題。
解決方法: 以目標代碼的增加為代價,換取時間上的節約。
當在一個函數定義或聲明前,加上關鍵字inline,則把該函數定義為內聯函數。
#include
using namespace std;
inline int f(int x)
{
return x * x;
}
int main()
{
int x(2);
cout << f(x) << endl;
cout << f(x + 1) <<endl;
return 0;
}
使用內聯函數的注意事項
內聯函數的定義,必須出現在該函數第一次被調用前;
內聯函數不能有復雜的控制語句,如switch,goto和while。
遞歸函數不能是內聯函數。類結構中所有的在類,說明內部定義的函數,都是內聯函數。
內聯函數具有與帶參的宏定義#define,相同的作用和相似的機理,但內聯函數具有宏定義所沒有的優點,沒有缺點,消除了宏定義的不安全性。
函數重載
函數重載又稱為函數的多態性
指同一個函數名對應著多個不同的函數。
“不同”是指,這些函數的形參表,必須互不相同,或者是形參的個數不同,或者是形參是類型不同,或者是兩者都不相同,否則,將無法實現函數重載。
下面是合法的重載函數:
int funcl (int ,int);
int funcl(int );
double funcl(int,long);
double funcl(long);
#include /函數重載/
using namespace std;
int add(int,int);
double add(double,double);
int main()
{
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;
return x+y;
}
注意:
重載函數的類型,即函數的返回類型,可以相同,也可以不同。如果僅僅是返回類型不同,函數名相同、形參表也相同,則是不合法的,編譯器會報“語法錯誤”。如:
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
#include <string.h>
using namespace std;
struct Person
{
char m_name;
int m_age;
void setName(const char);
void setAge(int);
void disp();
};
void Person::setName(const char* name)
{
m_name = new char[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;
}
int main()
{
Person somebody;
somebody.setName(“DaXian666”);
somebody.setAge(23);
somebody.disp();
}
定義類來實現數據隱藏
#include
#include <string.h>
using namespace std;
class Person
{
private://數據
char m_name;
int m_age;
public://函數
void setName(const char);
void setAge(int);
void disp();
};
void Person::setName(const char*name)
{
m_name = new char[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;
}
int main()
{
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
-
切勿在頭文件中,使用using指令,或者using 聲明,否則,添加頭文件,都要用這個空間。
-
命名空間嵌套在17標準中,得到了極大的簡化
-
聲明變量的時候不指定值,變量的值取決于這塊內存的值
-
允許if中加入初始化器
if (int a = 0;a>0) {
}
std::array<int, 3> arr = { 1,1,1 };//固定大小 #include 11標準
int size=std::size(arr);
6. C++程序中內存分為堆和棧,函數的執行一般在棧上進行,用完釋放。明確的分配內存,一般是放在了堆上。(在函數中聲明的,也要釋放掉)
int* a;
*a = 0;
delete a;//delete[] a—刪除數組
a = nullptr; 刪除指針后,最好進行置空操作,防止再次的使用
7.
//make_unique在超過作用域,或者被刪除時,就會自動釋放內存資源
auto an=make_unique();
//共享指針,每次指定make_shared時,都會遞增一個引用計數,表示數據又多了一個擁有者,當超過作用域時,就遞減引用計數,直到為0時,釋放資源
auto an1 = make_shared();
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 {
}
參考鏈接:
https://www.cnblogs.com/sumaoqing123/archive/2013/02/14/2912574.html
https://blog.csdn.net/weixin_41730008/article/details/88918971
https://blog.csdn.net/dsadasjdka/article/details/108547660
總結
- 上一篇: LLVM数据流分析的理论
- 下一篇: DeepSpeed超大规模模型训练工具