1. 程式人生 > >C++面試筆記(1)

C++面試筆記(1)

成對 存儲 rtu 面試 死循環 文件 繼承 無法 let

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)