概述#
好的面試官不會要求你用一個你不熟悉的語言編寫程式碼。如果 C++ 列在你的履歷上,你才可能被問到這類題目。即使記不住所有 API,大多數面試官也不會太在意,但建議熟悉基本的 C++ 語法。
類別與繼承(Classes and Inheritance)#
C++ 的類別與其他語言類似,但有一些語法差異。以下範例展示基本類別實作與繼承:
#include <iostream>
using namespace std;
#define NAME_SIZE 50 // Defines a macro
class Person {
int id; // all members are private by default
char name[NAME_SIZE];
public:
void aboutMe() {
cout << "I am a person.";
}
};
class Student : public Person {
public:
void aboutMe() {
cout << "I am a student.";
}
};
int main() {
Student * p = new Student();
p->aboutMe(); // prints "I am a student."
delete p; // Important! Make sure to delete allocated memory.
return 0;
}C++ 中,所有資料成員和方法預設是 private。需要使用
public關鍵字才能對外開放存取。
建構子與解構子(Constructors and Destructors)#
建構子(Constructor) 在物件建立時自動呼叫。若未定義,編譯器會自動產生預設建構子。
初始化基本型別的兩種寫法:
// 寫法一:在建構子主體中賦值
Person(int a) {
id = a;
}
// 寫法二:使用初始化列表(Initializer List)
Person(int a) : id(a) {
...
}初始化列表(
id(a)的寫法)會在物件建立前、建構子主體執行前完成賦值。當欄位為const或類別型別時,必須使用此寫法。
解構子(Destructor) 在物件銷毀時自動呼叫,負責釋放資源。解構子不接受參數:
~Person() {
delete obj; // free any memory allocated within class
}虛擬函式(Virtual Functions)#
考慮以下情境:
// 使用 Student* 型別
Student * p = new Student();
p->aboutMe(); // 呼叫 Student::aboutMe()
// 使用 Person* 型別
Person * p = new Student();
p->aboutMe(); // 呼叫 Person::aboutMe() ← 這不是我們想要的!
當使用父類別指標指向子類別物件時,若未宣告 virtual,方法呼叫會在編譯期決定(static binding),呼叫的是父類別的實作。
使用 virtual 關鍵字可以啟用動態綁定(dynamic binding):
class Person {
...
virtual void aboutMe() {
cout << "I am a person.";
}
};
class Student : public Person {
public:
void aboutMe() {
cout << "I am a student.";
}
};純虛擬函式(Pure Virtual Function)#
若父類別的方法沒有合理的預設實作,可以將其宣告為純虛擬函式:
class Person {
int id;
char name[NAME_SIZE];
public:
virtual void aboutMe() {
cout << "I am a person." << endl;
}
virtual bool addCourse(string s) = 0; // 純虛擬函式
};宣告純虛擬函式後,Person 成為抽象類別,無法直接實例化。子類別必須提供實作。
虛擬解構子(Virtual Destructor)#
當使用父類別指標指向子類別物件,並刪除該物件時,若解構子不是 virtual,只有父類別的解構子會被呼叫,導致子類別的資源無法被清理。
有問題的版本:
class Person {
public:
~Person() {
cout << "Deleting a person." << endl;
}
};
class Student : public Person {
public:
~Student() {
cout << "Deleting a student." << endl;
}
};
int main() {
Person * p = new Student();
delete p; // 只印出 "Deleting a person.",Student 的記憶體可能未被清理
}正確做法:將父類別解構子宣告為 virtual:
class Person {
public:
virtual ~Person() {
cout << "Deleting a person." << endl;
}
};這樣 delete p 會依序輸出:
Deleting a student.
Deleting a person.預設值(Default Values)#
函式可以為參數指定預設值。所有有預設值的參數必須位於參數列表的右側:
int func(int a, int b = 3) {
x = a;
y = b;
return a + b;
}
w = func(4); // b 使用預設值 3,w = 7
z = func(4, 5); // z = 9
運算子多載(Operator Overloading)#
運算子多載讓我們可以對自訂物件使用 + 等運算子:
BookShelf BookShelf::operator+(BookShelf &other) { ... }指標與參考(Pointers and References)#
指標(Pointer)#
指標保存一個變數的記憶體位址,可以用來存取和修改該變數:
int * p = new int;
*p = 7;
int * q = p;
*p = 8;
cout << *q; // prints 8(p 和 q 指向同一記憶體位址)
指標的大小在 32 位元機器上為 32 bits,64 位元機器上為 64 bits。面試中可能會問某個資料結構佔用多少空間。
參考(Reference)#
參考是已存在物件的別名,不佔有自己的記憶體:
int a = 5;
int & b = a;
b = 7;
cout << a; // prints 7(修改 b 就是修改 a)
參考與指標的主要差異:
- 參考不能為 null
- 參考一旦初始化,不能指向另一個物件
指標算術(Pointer Arithmetic)#
int * p = new int[2];
p[0] = 0;
p[1] = 1;
p++;
cout << *p; // Outputs 1
p++ 會讓指標向前移動 sizeof(int) 個 bytes。如果指標型別不同,則跳過的 bytes 也不同。
模板(Templates)#
模板可以讓同一段程式碼適用於不同的資料型別,實現程式碼重用:
template <class T>class ShiftedList {
T* array;
int offset, size;
public:
ShiftedList(int sz) : offset(0), size(sz) {
array = new T[size];
}
~ShiftedList() {
delete [] array;
}
void shiftBy(int n) {
offset = (offset + n) % size;
}
T getAt(int i) {
return array[convertIndex(i)];
}
void setAt(T item, int i) {
array[convertIndex(i)] = item;
}
private:
int convertIndex(int i) {
int index = (i - offset) % size;
while (index < 0) index += size;
return index;
}
};面試題目列表#
| 題號 | 題目 |
|---|---|
| 12.1 | Last K Lines:用 C++ 印出輸入檔案的最後 K 行 |
| 12.2 | Reverse String:實作 void reverse(char* str) 反轉字串 |
| 12.3 | Hash Table vs. STL Map:比較 Hash Table 和 STL Map 的差異 |
| 12.4 | Virtual Functions:virtual function 在 C++ 中如何運作? |
| 12.5 | Shallow vs. Deep Copy:深拷貝與淺拷貝的差異及使用時機 |
| 12.6 | Volatile:C 中 volatile 關鍵字的意義 |
| 12.7 | Virtual Base Class:為何基底類別的解構子需要宣告為 virtual? |
| 12.8 | Copy Node:實作方法,複製含有兩個 Node 指標的資料結構 |
| 12.9 | Smart Pointer:實作一個 smart pointer 類別 |
| 12.10 | Malloc:實作對齊的 malloc,使回傳的記憶體位址能被指定的 2 的冪次整除 |
| 12.11 | 2D Alloc:用 C 實作 my2DAlloc,以最少的 malloc 呼叫分配二維陣列 |