一、指針與引用的區別
1、指針:一個變量,存儲的內容為一個地址;引用:給一個已有對象起的別名。
2、指針是一個實體,需要分配內存空間;引用知識變量別名,不需要分配內存空間。
3、可以有多級指針,不能有多級引用。
4、自增運算結果不一樣。
5、指針是間接訪問,引用是直接訪問。
6、指針可以不用初始化,引用一定要先初始化。
二、指針與數據的區別
1、含以上的區別:數組對應着一塊內存,而指針是指向一塊內存。數組的地址和空間大小在生命周期不會發生改變,內容可能會發生改變,而指針指向的內存大小可以隨時發生改變。當指針指向常量字符串時,它的內容不可以改。
2、計算容量的區別:用sizeof計算出數組的元素個數,無法計算指針所指向內存的大小。
3、數組名是常量指針,指針是變量指針。
4、對數組用&和對指針&的意義不同,此時數組名不在當成指向一個元素的常量指針來使用。
三、虛函數的數據結構
虛函數:用virtual定義的函數為虛函數。虛函數來源:基於C++的一個特性:子類能轉換成父類,例如:
CBasic *parent; CBasic *p1; CChildren *child; parent = new CBsic; child = new CChildren; p1 = new CChlldren;
如上代碼,p1為CBasic型指針,但實際對象是CChildren型,如果子類和父類有相同的函數時,是調用子類的函數還是父類的函數?基於這個問題,C++提出多態的概念:根據實際對象類型決定函數調用的具體目標,使用virtual關鍵字對多態進行支持。被virtual聲明的函數被重寫后具有多態性。
底層機制:虛函數是使用虛函數表和虛函數表指針實現的。虛函數表是一個類虛函數的地址,用於索引類本身和類虛函數,若子類重寫父類函數,則會在相應的虛函數表處替換成子類虛函數的地址。虛函數表指針存在於每一個對象中,它指向對象所對應類的虛函數地址。
構造函數是不是虛函數並無多大影響,因為在構造子類一定要先構造父類。在存在繼承並且析構函數需要用來析構資源時,析構函數一定要為虛函數,若使用父類指針指向子類,用delete析構函數時,只會調用父類析構函數,不會調用子類的析構函數,造成內存泄漏。
四、const與define的區別
1、編譯器處理方式:const:編譯時確定其zhi;define:預處理時進行替換。
2、類型檢查:const:有數據類型,編譯時進行數據檢查;define:無類型,也不做類型檢查。
3、內存空間:const:在靜態存儲區儲存,僅此一份;define:在代碼段區,每做一次替換就會進行一次拷貝。
4、define可以用來防止重復定義,const不行。
五、不用臨時變量實現兩個變量的交換
#include<iostream> using namespace std; void Switch(int *p1, int *p2) { *p1 = *p1 + *p2; *p2 = *p1 - *p2; *p1 = *p1 - *p2; } void Xor(int *p1, int *p2) { *p1 = *p1^*p2; //異或操作 *p2 = *p1^*p2; *p1 = *p1^*p2; } int main() { int a = 1, b = 2; int *p1 = &a; int *p2 = &b; count << "*p1 = " << *p1 << endl; count << "*p2 = " << *p2 << endl; Switch(p1, p2); count << "*p1 = " << *p1 << endl; count << "*p2 = " << *p2 << endl; Xor(p1, p2); count << "*p1 = " << *p1 << endl; count << "*p2 = " << *p2 << endl; system("pause"); return 0; }
方法一缺陷:相加和可能存在溢出情況。
六、函數指針與指針函數
函數指針:顧名思義,與整型指針類似,整型指針為指向整型的指針,函數指針為指向函數的指針,是指針變量,他與函數名無掛,只與參數列表和返回類型有關;
指針函數:本質是函數,返回值為一個指針。
#include<iostream> using namespace std; //求和函數 int ADD(int a, int b) { return a + b; } //求差函數 int Sub(int a, int b) { return a - b; } //求和函數 int* add(int *p1, int *p2) { int a = *p1 + *p2; int *p = &a; return p; } int main() { int a = 5, b = 3; int *p1 = &a; int *p2 = &b; //聲明一個函數指針,只能指向兩個整形參數,返回值為Int型的函數 int (*hanshuzhizhen)(int, int); hanshuzhizhen = ADD; //函數指針初始化,是函數指針指向ADD函數; count << "函數指針指向ADD函數計算結果:" << hanshuzhizhen(a, b) << endl; hanshuzhizhen = Sub; //函數指向Sub函數 count << "函數指針指向Sub函數計算結果:" << hanshuzhizhen(a, b) << endl; count << "指針函數計算結果:" << *add(p1, p2) << endl; system("pause"); return 0; }
七、一個C++源文件從文本到可執行文件經歷的過程
1、預處理:對所有的define進行宏替換;處理所有的條件編譯#idef等;處理#include指令;刪除注釋等;bao#pragma。
2、編譯:將預處理后的文件進行詞法分析、語法分析、語義分析以及優化相應的匯編文件。
3、優化:
4、匯編:將匯編文件轉換成機器能執行的代碼。
5、鏈接:包括地址和空間分配,符號決議和重定位。
八、C++11新特性
1、nullptr代替NULL,傳統C++在識別NULL有兩種情況,一種是空指針,一種是當做0,在重載時往往把應該看成指針的當做0處理。
2、類型推導:auto 和decltype(可以讓編譯器找出表達式的類型)。
3、區間迭代,使c++的for語句能向python一樣便捷。
4、初始化列表。
5、模板增強。
6、新增容器 :std::array,std::forward_list(單鏈表)。
7、正則表達式。
8、線程支持。
9、右值引用(重點)。
九、C++和C的不同
1、c語言是面向過程的程序設計,主要核心為:數據結構和算法,具有高效的特性。對於C語言程序的設計,主要是考慮如何通過一個過程,對輸入進行處理得出一個輸出。C++是面向對象的程序設計,對於C++,首先考慮的是如何構造一個對象模型,讓這個模型配合對應問題,這樣可以通過獲取對象狀態信息得到輸出。
2、C++比C語言的增強點:1、命名空間;2、實用性加強;3、register關鍵字;4、變量檢測加強;5、struct 加強。
十、malloc的原理
函數原型:void malloc(size_t n)返回值額類型為void,為動態分配得到的內存,但代大小是確定的,不允許越界使用。
malloc函數的實質體現在它有一個可以將可用內存塊連接成一個長的列表的空閑鏈表,當調用鏈表時,它沿着連接表尋找一個大到可以滿足用戶請求所需內存,將內存一分為二,將分配給用戶那塊內存傳給用戶,剩下的那塊返回連接表。
十一、內存泄漏、野指針
內存泄漏:動態申請的內存空間沒有被正常釋放,但也不能繼續被使用的情況。
野指針:指向被釋放的內存或者訪問受限的指針。造成的原因:1、指針未被初始化;2、被釋放的指針沒有被置為NULL;3、指針越界操作。
解決內存泄漏的辦法:使用智能指針。
十二、static
1、局部靜態變量:static局部變量和普通局部變量有什么區別?
答:static局部變量只被初始化一次,下一次依據上一次結果值;程序的局部變量存在於(堆棧)中,全局變量存在於(靜態區 )中,動態申請數據存在於( 堆)中。
2、全局靜態變量:static全局變量與普通的全局變量有什么區別?
答:全局變量(外部變量)的說明之前再冠以static就構成了靜態的全局變量。全局變量本身就是靜態存儲方式,靜態全局變量當然也是靜態存儲方式。 這兩者在存儲方式上並無不同。這兩者的區別雖在於非靜態全局變量的作用域是整個源程序,當一個源程序由多個源文件組成時,非靜態的全局變量在各個源文件中都是有效的。而靜態全局變量則限制了其作用域,即只在定義該變量的源文件內有效,在同一源程序的其它源文件中不能使用它。由於靜態全局變量的作用域局限於一個源文件內,只能為該源文件內的函數公用,因此可以避免在其它源文件中引起錯誤。
從以上分析可以看出,把局部變量改變為靜態變量后是改變了它的存儲方式即改變了它的生存期。把全局變量改變為靜態變量后是改變了它的作用域, 限制了它的使用范圍。static全局變量與普通的全局變量有什么區別:static全局變量只初使化一次,防止在其他文件單元中被引用;
3、靜態成員函數:static函數與普通函數有什么區別?
答:static函數與普通函數作用域不同,僅在本文件。只在當前源文件中使用的函數應該說明為內部函數(static),內部函數應該在當前源文件中說明和定義。對於可在當前源文件以外使用的函數,應該在一個頭文件中說明,要使用這些函數的源文件要包含這個頭文件,static函數與普通函數有什么區別:static函數在內存中只有一份,普通函數在每個被調用中維持一份拷貝。
十三、union和struct
1、在存儲信息時,struct可以存儲多個成員,而union每個成員會共享一個存儲空間,只能存儲最后一個成員。
2、在任何時刻,union只存放被選中的那個成員,struct每個成員都在。
3、對union的不同成員賦值,將會對其他成員重寫。
十四、new與malloc的區別
1、屬性:new為關鍵字,malloc為庫函數,需要頭文件支持。
2、參數:使用new申請內存無需指定內存大小,編譯器會自行計算,而malloc需要顯示的給出所需內存的大小。
3、返回類型:new分配成功返回的是對象類型指針,與對象嚴格匹配,無需類型轉換,故new是符合類型安全性操作符,malloc返回的是void*。
4、分配失敗:new分配失敗,拋出bad_alloc異常,malloc則是返回NULL。
5、重載。
6、內存區域:new分配的內存在自由儲存區,malloc在堆上分配內存。
十五、C++類型轉換
1、static_cast
2、dynamic_cast
3、const_cats
4、reinterpret_cast
#include<iostream> using namespace std; //static_cast<typeid> () 其中typeid可以為一般類型,也可以為指針引用 class A { public: A() :i(1), j(1) {} ~A() {} void printA() { count << "call printA() in class A" << endl; } void printSum() { count << "Sum = " << i + j << endl; } private: int i, j; }; class B :public A { public: B() :a(2), b(3) {} ~B() {} void printB() { count << "call printB() in class B" << endl; } void printSum() { count << "Sum = " << a + b << endl; } void Add() { a++; b++; } private: double a, b; } int main() { B *ptrB = new B; //創建一個B類型對象,堆區 ptrB->printSum(); //輸出和 A *ptrA = static_cast<A*>(ptrB); //將派生類轉換成父類,上行轉換 ptrA->printA(); ptrA->printSum(); ptrA = new A; //創建一個父類對象 ptrB = static_cast<B*>(ptrA); //將父類對象轉換成子類對象,下行轉換 ptrB->printB(); ptrB->printSum(); B b; //棧上創建一個B類型對象 B &rB = b; //對b的引用 rB.printSum(); A &rA = static_cast<A &>(b); //派生類轉換成基類,上行轉換 rA.printA(); rA.printSum(); A a; A &rA1 = a; rA1.printA(); B &rB1 = static_cast()<B &>(a); //將基類轉換成派生類,下行轉換 rB1.printB(); rB1.printSum(); system("pause"); return 0; }
十六、面向對象的了解
面向對象:把數據和對數據的操作方法放在一起,做成一個相互依靠的整體,稱之為對象,對同類對象抽象出共同特性,類中大多數數據只能用本類的方法進行處理。
面向對象的三大特性:封裝,繼承,多態。
封裝:將一類事物的屬性和行為抽象為一個類,使屬性私有化,行為公開化,提高數據隱蔽性,復用性高。
繼承:進一步將屬性和行為抽象為一個父類,而每一個子類擁有父類的行為和屬性,也有自己的行為和屬性。
多態:接口復用。
十七、前置和后置的區別
前置++的實現比較高效,自增之后,將*this指針直接返回即可,一定要返回this指針。
后置++的實現比較麻煩,因為要返回自增之前的對象,所以先將對象進行拷貝一份,再進行自增,最后返回那個拷貝。
十八、靜態庫和動態庫
靜態庫:
1、鏈接時將程序放進可執行的程序中。
2、可產生多個副本。
3、不依賴程序運行。
動態庫:
1、程序運行時,加載時才會到動態庫找函數。
2、多線程共享。
3、依賴程序運行。