search
尋找貓咪~QQ 地點 桃園市桃園區 Taoyuan , Taoyuan

C++基礎複習與總結

一、new和malloc的區別

1、new和delete配對,釋放數組需要用delete。new和delete實際上調用了malloc和free,另外調用了類的構造函數和析構函數。2、malloc和free配對,malloc返回的是void指針,需要強轉。

3、new申請的內存保存在堆中,malloc申請的內存保存在自由存儲區。

二、C++運算符

1、取模操作符:%2、邏輯否、與、或:!, &&, ||3、三元操作符:c = (a>b) ? a : b;4、按位與、或、非& AND 邏輯與 Logic AND| OR 邏輯或Logic OR~ NOT 對1取補(位反轉)Complement to one (bit inversion)5、按位移:<< SHL 左移Shift Left>> SHR 右移Shift Right

三、&: 取地址運算符、定義變數引用

&操作符用於取地址時的用法是:int* x=&y;。

然而,另外一種用法是定義變數別名,這種用法不能和取地址簡單等同。用於傳遞函數輸入參數時很好理解,但定義變數時容易引起理解錯誤,特別是和指針的區別:

從內存的角度看,指針和引用是完全不同的。指針,內存要為它分配一個存儲空間。引用,內存不分配空間的,引用只是一個別名。我認為就是在符號表裡增加一個標誌而已,對於語句int &y=x; (&x=&y)為true。實際上「引用」可以做的任何事情「指針」也都能夠做,為什麼還要「引用」這東西?答案是「用適當的工具做恰如其分的工作」。當重載某個操作符時,你應該使用引用。最普通的例子是操作符。這個操作符典型的用法是返回一個目標對象,其能被賦值。如果操作符返回一個指針,那麼后一個語句就得這樣寫:*v[5] = 10; 但是這樣會使得v看上去象是一個向量指針。因此你會選擇讓操作符返回一個引用。

引用的一些規則如下: (1)引用被創建的同時必須被初始化(指針則可以在任何時候被初始化),否則會報編譯錯誤。 (2)一旦引用被初始化,就不能改變引用的關係(指針則可以隨時改變所指的對象)。 以下示常式序中,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;(3)不能有NULL 引用,引用必須與合法的存儲單元關聯(指針則可以是NULL)。 以下的寫法將地址指向一個位置的內存,是錯誤的。結果將是不確定的(編譯器能產生一些輸出,導致任何事情都有可能發生)char *pc = 0; // 設置指針為空值 char& rc = *pc; // 讓引用指向空值(4)「sizeof 引用」得到的是所指向的變數(對象)的大小,但是當引用作為成員時,其佔用空間與指針相同(沒找到標準的規定)。

(5)引用只能指向一個實際的變數,不能指向指針或引用(int*) * p1; // p1是指針的指針(int*) & p2; // p2是指向整型指針的引用引用不能指向指針或引用!(int&) * p3; // ERROR: 不能有指向引用的指針,因為引用只是一個別名(int&) & p4; // ERROR: 不能有指向引用的引用,因為引用只是一個別名(6)指針和引用在內部的實現其實是沒多大的區別的。但使用時有些地方是要注意的。因為引用具有對象行為,這一點很重要。引用複製時會調用對象的複製函數,在涉及多態時,這地方很容易出錯。class A{...};class B:public A{...};void f(A&a1,A&a2){a1=a2;//此處調用的只有基類A的複製函數,而B部分不會被進行複製,之將導致數據的不一致(即B部分的數據沒有被複制);a1.fun;}

四、關於const

一般的const變數:

下面兩個聲明都指向一個const int類型的指針,指針所指向的內存不能被修改,但指針可以指向另一個內存:const int *p; int const *q; int類型的const指針應該這樣聲明。指針所指向的內存可以被修改,但指針不能指向另一個內存int * const r= &n;

聲明一個指向const int類型的const指針:const int * const p=&n;

const在函數聲明中的含義:

const int& SetPoint(const int& param) const第一個const:函數的返回值限定為const,即返回值不能被修改。const int a=SetPoint(...) a在此之後便不能被修改。第二個const:指函數的形參為const類型,函數體內不能被修改.第三個const:表明這個函數不會對這個類對象的數據成員(準確地說是非靜態數據成員)作任何改變。

類的const和static成員變數的初始化:

對於static成員變數,如果同時是const的,可以在類定義中初始化,否則只能在類定義外部初始化。非static的const成員變數只能在構造函數的初始化列表中初始化。(ClassName:m_1(1){};)

五、一些數據類型和變數賦值語法

1、union 中的所有被聲明的元素佔據同一段內存空間,其大小取聲明中最長的元素的大小。union 的用途之一是將一種較長的基本類型與由其它比較小的數據類型組成的結構(structure)或數組(array)聯合使用。2、long double和float變數的賦值方法:3.14159L // long double6.02e23f // float3、容易引起理解錯誤的定義語句:int* p,q; 第一眼看去,好像是p和q都是int*類型的,但事實上,只有p是一個指針,而q是一個最簡單的int型變數。同時定義兩個指針的語法是:int *p1, *p2;

4、定義一個指向int[4]數組的指針變數int (*p)[4]=RollNum;這裡,p被聲明為一個指向一個4元素(int類型)數組的指針。5、未指定size情況下,char數組的大小由初始化字元串決定:我們可以用下面兩種方法的任何一種來初始化字元串mystring:char mystring = { 'H', 'e', 'l', 'l', 'o', '/0' };char mystring = "Hello";在兩種情況下字元串或數組mystring都被定義為6個字元長(元素類型為字元char):組成Hello的5個字元加上最後的空字元('/0')。在第二種用雙引號的情況下,空字元('/0')是被自動加上的。兩種情況下sizeof應該都是6,strlen都是5。

六、常用的幾個標準C++函數

1、cout和cin的用法:cout << "xxx" << endl;cin >> "yyy";

2、常用的字元串函數:strcat //字元串拼接strcpystrncpystrcmp //字元串比較,相同返回0

七、switch-case的寫法

switch (expression) {case constant1:block of instructions 1break;case constant2:block of instructions 2break;...default:default block of instructions}

八、函數的幾個屬性和用法

1、指定函數的默認參數值int divide (int a, int b=2) {

2、什麼是函數重載(Overloaded functions)兩個不同的函數可以用同樣的名字,只要它們的參量(arguments)的原型(prototype)不同,也就是說你可以把同一個名字給多個函數,如果它們用不同數量的參數,或不同類型的參數。

3、內聯函數

inline 指令可以被放在函數聲明之前,要求該函數必須在被調用的地方以代碼形式被編譯。這相當於一個宏定義(macro)。它的好處只對短小的函數有效,這種情況下因為避免了調用函數的一些常規操作的時間(overhead),如參數堆棧操作的時間,所以編譯結果的運行代碼會更快一些。調用函數的時候並不需要寫關鍵字inline ,只有在函數聲明前需要寫。

4、將數組作為參數傳入函數,傳的是引用而不是值。void procedure (int myarray[3][4])

九、函數指針的用法

使用函數指針的幾種方法:(1)簡單調用函數指針;void (*pfunc)(int);pfunc=callback_funcname;callback_funcname(1);其中聲明函數指針原型的代碼可以在調用處寫,也可以寫成全局的。這種方法使用簡單,適用於臨時調用。(2)使用typedef調用函數指針:typedef void(*PFUNC)(int);PFUNC pfunc;pfunc=callback_funcname;callback_funcname(1);這種方法適用於多次調用,先全局定義PFUNC,再在每個調用的地方聲明臨時變數后調用。(3)C++類中調用成員函數指針(不使用typedef):void (*MyClass::pfunc)(int);pfunc=&MyClass::callback_funcname;(this->*callback_funcname)(1);和方法1類似,注意語法的不同。(4)C++類中調用成員函數指針(使用用typedef):typedef void(*PFUNC)(int); //在類中typedefPUNC pfunc;pfunc=&MyClass::callback_funcname;(this->*callback_funcname)(1);和方法2類似,注意語法的不同。

十、typedef的不常用用法

typedef的一般用法是:

typedef int UINT32;

但用來定義一個數組類型或指針函數時,比較特殊:

typedef char CARRAY[32]; //定義了一個CARRAY的類型,代表char[32]

typedef void(*PFUN)(int); //定義了一個指向指針函數的變數類型,函數原型為void xxx(int yyy);

十一、類的private/protected/public屬性

1、類的成員如果沒有指定訪問域,默認是private的。

2、標識符protected 與 private類似,它們的唯一區別在繼承時才表現出來。當定義一個子類的時候,基類的protected 成員可以被子類的其它成員所使用,然而private 成員就不可以。

3、public/protected/private繼承的區別:(1)public繼承:父類的public依然是public,protected依然是protected,private不可訪問;(2)protected繼承:父類的public稱為protected,protected稱為private;(3)private繼承:父類的所有成員全部變成private。

十二、關於空類

編譯器為一個空類提供哪些默認函數?

1、C++編譯器會提供默認的構造函數,析構函數, 拷貝構造函數和拷貝賦值操作符(請參考著名的Effective C++的第三版的第5條)當我們定義一個class而沒有明確定義構造函數的時候,編譯器會自動假設兩個重載的構造函數 (默認構造函數"default constructor" 和複製構造函數"copy constructor")。拷貝構造函數是一個只有一個參數的構造函數(原型:ClassName(ClassName &cn){};),該參數是這個class的一個對象,這個函數的功能是將被傳入的對象(object)的所有非靜態(non-static)成員變數的值都複製給自身這個object。必須注意:這兩個默認構造函數(empty construction 和 copy constructor )只有在沒有其它構造函數被明確定義的情況下才存在。一個類包含一個對賦值操作符assignation operator (=)的默認定義,該操作符用於兩個同類對象之間。這個操作符將其參數對象(符號右邊的對象) 的所有非靜態 (non-static) 數據成員複製給其左邊的對象。2、用class obj;的方式聲明一個對象,如果構造函數沒有參數,或只有默認構造函數,後面不能加,因為編譯器會誤以為這是一個沒有參數的函數聲明;3、如果任何其它有任意參數的構造函數被定義了,默認構造函數和拷貝構造函數就都不存在了。在這種情況下,如果你想要有empty construction和copy constructor ,就必需要自己定義它們。4、對基本類型,在c++裡面,為了模板template,規定他們可以使用類似於類的默認構造函數的方式(僅僅是類似的方式而已) 賦初始值0。這叫做基本類型的顯示初始化, 請參考 C++標準程序庫(The C++ Standard Library)的14頁,2.2.2 基本型別的顯示初始化,書中舉的例子就是int i1;//未初始化int i2 = int; //初始化為0

sizeof一個空類等於多少?

sizeof一個空類返回1。所謂類的實例化就是在內存中分配一塊地址,每個實例在內存中都有獨一無二的地址。同樣空類也會被實例化,所以編譯器會給空類隱含的添加一個位元組,這樣空類實例化之後就有了獨一無二的地址了。所以空類的sizeof為1。C++編譯器不允許對象為零長度。試想一個長度為0的對象在內存中怎麼存放?怎麼獲取它的地址?為了避免這種情況,C++強制給這種類插入一個預設成員,長度為1。如果有自定義的變數,變數將取代這個預設成員。

十三、繼承或多重繼承情況下構造函數的調用順序

(1)如果聲明為Derive: public Super1, public Super2{AnotherClass m_obj;}; 構造函數的調用順序是:Super1, Super2, AnotherClass, Derive.(2)如果父類有默認構造函數,或沒有參數的構造函數,不需要在子類的構造函數定義中顯式調用父類構造函數,否則需要調用。成員對象也是一樣道理。以上面的例子說明,Derive的構造函數寫法是:Derive(int i): Super1(i), Super2(i), m_obj(1){...}注意,成員對象初始化時應該指明對象名稱,而不是類名。

析構函數的調用順序應該是依次反過來的。

十四、虛函數、純虛函數和抽象類、虛析構函數

虛函數的作用和運行原理

(1)多態是面向對象編程中的核心概念,就是說一個基類類型的指針實際上可能指向的是一個子類對象。只有在運行時才能根據實際情況來決定執行哪個函數,也就是動態聯編。和動態聯編對應的是靜態聯編,也就是說在編譯時就決定了調用哪個函數。為了實現動態聯編,必須將父類的函數聲明為virtual。如果沒有聲明為virtual,可能得到的結果不是預期中的。對於析構函數而言,虛函數保證子類和父類的析構函數都會被執行。參考:http://jiamingjun03.blog.163.com/blog/static/11687677620099297435263/(2)對於包含了至少一個虛函數的類(或其父類包含虛函數),編譯器需要為這個類增加4個位元組,用來保存指向虛函數表VTABLE的指針。

純虛函數和抽象類

包含了純虛函數的類不能被直接實例化,可以稱為抽象類。定義方法:virtual void func=0;子類override一個虛函數,不一定要加virtual關鍵字。

什麼情況下需要指定析構函數為virtual?

(1)析構函數不一定需要定義為虛函數,只有當這個類要作為其他類的父類使用時,才需要定義為虛函數。如果父類的析構函數沒有定義為虛函數,則子類對象銷毀時,父類析構函數不會被調用。(2)對於一個抽象類,析構函數可以被定義為純虛的。(3)父類和子類之間的虛函數動態聯編不會因為private發生影響。(4)一個類的虛函數在它自己的構造函數和析構函數中被調用的時候,它們就變成普通函數了,不「虛」了。也就是說不能在構造函數和析構函數中讓自己「多態」。(5)在虛函數和純虛函數的定義中不能有static標識符,原因很簡單,被static修飾的函數在編譯時候要求前期bind,然而虛函數卻是動態綁定(run-time bind),而且被兩者修飾的函數生命周期(life recycle)也不一樣。

十五、多重繼承情況下如何引用父類的同名成員?

重繼承情況下,如果多個基類有同名成員,引用方法是:

pDeriveObj->BaseClass1::Member;

十六、運算符重載

語法:

Type Type::operator +(const Type &i){}

十七、友元

友元可以實現外部對private和protected成員的訪問。有兩種實現:(1)友元函數。語法:在函數聲明前加上friend。友元函數並不是類的成員函數,實現函數體或調用函數時不加ClassName::。(2)友元類。在類的聲明中加入:friend class VisitorClass;友元不會被子類繼承。

十八、模板

函數模板

實現語法:

在函數的聲明和實現前加上template<typename T> ,(typename和class等價),可以寫在一行裡面,也可以分成兩行寫,注意>後面沒有分號。如果聲明和實現分開寫,兩個地方都要寫上template<typename T>。(1)一行代碼中同時聲明並實現函數template <typename T>void func(T t1, T t2)

{...};(2)分兩行代碼聲明並實現函數template <class T>void func(T t1, T t2){...};(3)在兩個地方分別聲明和實現函數template <class T>void func(T t1, T t2);...template <class T>void func(T t1, T t2){...}

引用語法:

func<int>(5, 6);

類模板

聲明方法:

類模板可以實現在一個類中有一個通用類型的成員變數。

template <class T> class ClassName

{public:

T *m_pVariable;

};

引用方法:

ClassName<int> obj;

模板特殊化

模板特殊化可以專門為某種數據類型定義特殊的行為。類的定義必須和通用的模板類完全一致,除了用專門語法,並將T修改為專門的類型,並定義特殊行為。template<> class<int>{定義通用函數,定義特殊函數};

定義模板的默認值template <class T = char> // 有一個默認值。

模板的參數值

除了模板參數前面跟關鍵字class 或 typename 表示一個通用類型外,函數模板和類模板還可以包含其它不是代表一個類型的參數,例如代表一個常數,這些通常是基本數據類型的。

二十、類型轉換和C++高級類型轉換

基本類型強轉有兩種寫法:

int i;float f = 3.14;i = (int) f;i = int ( f );

高級類型轉換

ANSI-C++ 標準定義了4種新的類型轉換操作符: reinterpret_cast, static_cast, dynamic_cast 和const_cast。

reinterpret_cast可以將一個指針轉換為任意其它類型的指針。ClassA* pa;ClassB* pb=reinterpret_cast<ClassB*>pa;

static_cast可以執行所有能夠隱含執行的類型轉換,以及它們的反向操作(即使這種方向操作是不允許隱含執行的)。用於類的指針,也就是說,它允許將一個引申類的指針轉換為其基類類型(這是可以被隱含執行的有效轉換),同時也允許進行相反的轉換:將一個基類轉換為一個引申類類型。不會檢查被轉換的基類是否真正完全是目標類型的。Derive* pa;Super* pb;pa = static_cast<Derive*> pb; pb = static_cast<Super*> pa;static_cast除了能夠對類指針進行操作,還可以被用來進行類中明確定義的轉換,以及對基本類型的標準轉換:double d=3.14159265;int i = static_cast<int>(d);

dynamic_cast 完全被用來進行指針的操作。它可以用來進行任何可以隱含進行的轉換操作以及它們被用於多態類情況下的方向操作。然而與static_cast不同的是, dynamic_cast 會檢查后一種情況的操作是否合法,也就是說它會檢查類型轉換操作是否會返回一個被要求類型的有效的完整的對象。在不合法的情況下,如果用於指針,將返回NULL;如果用於引用,拋出異常。Derive* pa = new Derive;Super* pb = new Super;pa = dynamic_cast<Derive*> pb; //失敗,返回NULLpb = dynamic_cast<Super*> pa; //成功

const_cast類型轉換對常量const 進行設置或取消操作。class C {};const C * a = new C;C * b = const_cast<C*> (a);

typeid (object_pointer)這個操作符返回一個類型為type_info的常量對象指針,這種類型定義在標準頭函數中。type_info::name返回對象的類名。

二十一、命名空間

定義一個命名空間:

namespace ns1{...}

設置默認命名空間:

using namespace ns1;

引用其他命名空間的類型:

ns2::variable = xx;

二十二、預處理命令

#undef 完成與 #define相反的工作,它取消對傳入的參數的宏定義

#ifdef, #ifndef, #if, #endif, #else and #elif

指令#line 可以使我們對這兩點進行控制,也就是說當出錯時顯示文件中的行數以及我們希望顯示的文件名。它的格式是:#line number "filename"下面這段代碼將會產生一個錯誤,顯示為在文件"assigning variable", line 1 。#line 1 "assigning variable"int a?;

這個指令將中斷編譯過程並返回一個參數中定義的出錯信息#error

這個指令是用來對編譯器進行配置的,針對你所使用的平台和編譯器而有所不同。#pragma

二十三、預定義宏

__LINE__ 整數值,表示當前正在編譯的行在源文件中的行數。__FILE__ 字元串,表示被編譯的源文件的文件名。__DATE__ 一個格式為 "Mmm dd yyyy" 的字元串,存儲編譯開始的日期。__TIME__ 一個格式為 "hh:mm:ss" 的字元串,存儲編譯開始的時間。__cplusplus 整數值,所有C++編譯器都定義了這個常量為某個值。如果這個編譯器是完全遵守C++標準的,它的值應該等於或大於199711L,具體值取決於它遵守的是哪個版本的標準。

學習過程中遇到什麼問題或者想獲取學習資源的話,歡迎加入學習交流群

639368839,我們一起學C/C++!



熱門推薦

本文由 yidianzixun 提供 原文連結

寵物協尋 相信 終究能找到回家的路
寫了7763篇文章,獲得2次喜歡
留言回覆
回覆
精彩推薦