C++面試筆記(1)
1. C和C++的區別
C++面向對象的三大特性
面向對象的三個基本特征:封裝、繼承、多態
1、封裝:把客觀事物封裝成抽象的類,類進行信息隱藏
關鍵字 |當前類 |包內 |子孫類| 包外
--|--|--|--|--
public |√ |√ |√ |√
protected| √| √| √| ×
friendly| √| √| ×| ×
private| √| ×| ×| ×
2、繼承:使用現有類的所有功能,並在無需編寫原來的類的情況下對這些功能進行擴展
分為父類和子類
繼承的過程,就是一般到特殊的過程
3、多態:允許將子類類型的指針賦值給父類類型的指針
實現多態有兩種方式:
(1) 覆蓋:子類重新定義父類的虛函數的做法
(2) 重載:允許存在多個同名函數,但是參數表不同
基類指針指向子類對象
作用 :
(1) 封裝可以隱藏實現的細節,使得代碼模塊化
(2) 繼承擴展已有的代碼模塊--代碼復用
(3) 實現接口的重用
C語言和C++的區別
從思想上
-- C程序的的設計首先考慮的是如何聽過一個過程,對輸入進行運算處理得到輸出,是一個結構化語言
-- C++首先考慮的是如何構造出一個對象模型,讓這個模型能夠契合與之對應的問題域。
C++ 與 C 的區別(細化)
***
2. 指針和引用的區別
淺談 C++ 中指針和引用的區別
(1) 性質區別:指針是一個變量,只不過這個變量存儲的是一個地址,指向內存的一個存儲單元;
而引用跟原來的變量實質上是一個佛那個下,只不過是一個別名
(2) 指針可以多級、引用最多一級
(3) 指針可以為空、引用不能為空
(4) 指針在初始化為可以改變,引用不能
(5) sizeof(指針)=指針本身的大小 sizeof(引用)=所指向變量的大小
***
3. 野指針和懸空指針的區別
野(wild)指針與懸空(dangling)指針
野指針是指沒有初始化的指針,野指針指向不可用的內存
懸空指針是指指針最初指向的內存已經被釋放的一種指針
***
4. const和static之間的區別
const的作用:
(1) 修飾變量,說明該變量不可以被改變
(2) 修飾指針,分為指向常量的指針和指針常量
常量指針和指針常量
int _tmain(int argc, _TCHAR* argv[]) { //定義變量 int a=1; //定義常量 const int b=2; //定義常量指針 const int *ptr1=&a; //定義指針常量,必須賦值 int* const ptr2=&a; //錯誤,不能把常量的地址賦給指針變量 int *ptr3=&b; //正確,可以把常量的地址賦給常量指針 const int* ptr4=&b; //錯誤,間接引用常量指針不可以修改內存中的數據 *ptr1=3; //正確,間接引用指針常量可以修改內存中的數據 *ptr2=4; //正確,常量指針可以指向其他變量 ptr1=&b; //錯誤,指針常量不可以指向其他變量 ptr2=&b; //常量指針常量,即不可以間接引用修改內存數據,也不可以指向別的變量 const int * const ptr5=&a; //錯誤,不可以間接引用修改內存數據 *ptr5=5; //錯誤,不可以修改指向的對象 ptr5=&b; return 0; }
(3) 常量引用,經常用於形參類型,即避免了拷貝,又避免了函數對值的修改
(4) 修飾成員函數,說明該成員函數內不能修改成員變量。
// 類
class A
{
private:
const int a; // 常對象成員,只能在初始化列表賦值
public:
// 構造函數
A() { };
A(int x) : a(x) { }; // 初始化列表
// const可用於對重載函數的區分
int getValue(); // 普通成員函數
int getValue() const; // 常成員函數,不得修改類中的任何數據成員的值
};
void function()
{
// 對象
A b; // 普通對象,可以調用全部成員函數
const A a; // 常對象,只能調用常成員函數、更新常成員變量
const A *p = &a; // 常指針
const A &q = a; // 常引用
// 指針
char greeting[] = "Hello";
char* p1 = greeting; // 指針變量,指向字符數組變量
const char* p2 = greeting; // 指針變量,指向字符數組常量
char* const p3 = greeting; // 常指針,指向字符數組變量
const char* const p4 = greeting; // 常指針,指向字符數組常量
}
// 函數
void function1(const int Var); // 傳遞過來的參數在函數內不可變
void function2(const char* Var); // 參數指針所指內容為常量
void function3(char* const Var); // 參數指針為常指針
void function4(const int& Var); // 引用參數在函數內為常量
// 函數返回值
const int function5(); // 返回一個常數
const int* function6(); // 返回一個指向常量的指針變量,使用:const int *p = function6();
int* const function7(); // 返回一個指向變量的常指針,使用:int* const p = function7();
static作用
(1) 修飾普通變量,修改變量的存儲區域和生命周期,使變量存儲在靜態區,在 main 函數運行前就分配了空間,如果有初始值就用初始值初始化它,如果沒有初始值系統用默認值初始化它。
(2) 修飾普通函數,表明函數的作用範圍,僅在定義該函數的文件內才能使用。在多人開發項目時,為了防止與他人命令函數重名,可以將函數定位為 static。
(3) 修飾成員變量,修飾成員變量使所有的對象只保存一個該變量,而且不需要生成對象就可以訪問該成員。
(4) 修飾成員函數,修飾成員函數使得不需要生成對象就可以訪問該函數,但是在 static 函數內不能訪問非靜態成員。
5 this指針
C++ this 指針
在C++中。每一個對象都能通過this指針來訪問自己的地址,this指針是所有成員函數的隱含參數。因此在成員函數內部,它可以用來指向調用對象
其中,友元函數沒有this指針,因為友員不是類的成員,只有成員函數才有this指針
#include <iostream>
using namespace std;
class Box
{
public:
// 構造函數定義
Box(double l=2.0, double b=2.0, double h=2.0)
{
cout <<"Constructor called." << endl;
length = l;
breadth = b;
height = h;
}
double Volume()
{
return length * breadth * height;
}
int compare(Box box)
{
return this->Volume() > box.Volume();
}
private:
double length; // Length of a box
double breadth; // Breadth of a box
double height; // Height of a box
};
int main(void)
{
Box Box1(3.3, 1.2, 1.5); // Declare box1
Box Box2(8.5, 6.0, 2.0); // Declare box2
if(Box1.compare(Box2))
{
cout << "Box2 is smaller than Box1" <<endl;
}
else
{
cout << "Box2 is equal to or larger than Box1" <<endl;
}
return 0;
}
6 友元函數
類的友元函數是定義在類的外部,但有權訪問類的私有和保護成員。盡管友元函數的原型在類的定義中,但是友元函數並不是成員函數
友元可以是一個函數,也可以是一個類;友元類的整個類及其所有成員都是友元
聲明函數為友元:
#include <iostream>
using namespace std;
class Box
{
double width;
public:
friend void printWidth( Box box );
void setWidth( double wid );
};
// 成員函數定義
void Box::setWidth( double wid )
{
width = wid;
}
// 請註意:printWidth() 不是任何類的成員函數
void printWidth( Box box )
{
/* 因為 printWidth() 是 Box 的友元,它可以直接訪問該類的任何成員 */
cout << "Width of box : " << box.width <<endl;
}
// 程序的主函數
int main( )
{
Box box;
// 使用成員函數設置寬度
box.setWidth(10.0);
// 使用友元函數輸出寬度
printWidth( box );
return 0;
}
7. struct與class之間的區別
C++ 中結構體與類的區別
(1) 默認的訪問控制:struct是public class是private
(2) Class還能用於定義模板參數,而struct不用於定義模板參數
struct更適合看成一個數據結構的實現體,class更適合看成是一個對象的實現體
***
8. 常見數據結構在32位和64位機器上的字節數
/ | 32位 | 64位 |
---|---|---|
char | 1 | 1 |
指針變量 | 4 | 8 |
short | 2 | 2 |
int | 4 | 4 |
uint | 4 | 4 |
float | 4 | 4 |
double | 8 | 8 |
long | 4 | 8 |
long long | 8 | 8 |
ulong long | 4 | 8 |
9.虛函數可以inline嗎?
引入inline的原因?
在 c/c++ 中,為了解決一些頻繁調用的小函數大量消耗棧空間(棧內存)的問題,特別的引入了 inline 修飾符,表示為內聯函數。
棧空間就是指放置程序的局部數據(也就是函數內數據)的內存空間。
在系統下,棧空間是有限的,假如頻繁大量的使用就會造成因棧空間不足而導致程序出錯的問題,如,函數的死循環遞歸調用的最終結果就是導致棧內存空間枯竭。
類中除了虛函數的其他函數都會自動隱式地當成內聯函數
(1) 虛函數是可以內聯的,但是當虛函數表現多態時不能內聯
(2) 內聯是在編譯器建議編譯器內聯,而虛函數的多態性在運行期,編譯器無法知道運行期調用哪個代碼,因此虛函數表現為多態性時(運行期)不可以內聯。
(3) inline virtual 唯一可以內聯的時候是:編譯器知道所調用的對象是哪個類(如 Base::who()),這只有在編譯器具有實際對象而不是對象的指針或引用時才會發生。
虛函數內聯使用:
#include <iostream>
using namespace std;
class Base
{
public:
inline virtual void who()
{
cout << "I am Base\n";
}
virtual ~Base() {}
};
class Derived : public Base
{
public:
inline void who() // 不寫inline時隱式內聯
{
cout << "I am Derived\n";
}
};
int main()
{
// 此處的虛函數 who(),是通過類(Base)的具體對象(b)來調用的,編譯期間就能確定了,所以它可以是內聯的,但最終是否內聯取決於編譯器。
Base b;
b.who();
// 此處的虛函數是通過指針調用的,呈現多態性,需要在運行時期間才能確定,所以不能為內聯。
Base *ptr = new Derived();
ptr->who();
// 因為Base有虛析構函數(virtual ~Base() {}),所以 delete 時,會先調用派生類(Derived)析構函數,再調用基類(Base)析構函數,防止內存泄漏。
delete ptr;
ptr = nullptr;
system("pause");
return 0;
}
10 volatile關鍵字
C/C++ 中 volatile 關鍵字詳解
談談 C/C++ 中的 volatile
***
C++面試筆記(1)