C++基礎知識總結
1
面試C++程式設計師的時候一般都是3板斧,先是基礎問答,然後一頓虛擬函式、虛擬函式表、純虛擬函式、抽象類、虛擬函式和解構函式、虛擬函式和建構函式。接著拷貝建構函式、操作符過載、下面是STL,最後是智慧指標。
都能挺過去那基本知識這關應該算是過了,下面就是專案背景和演算法了。
1,C++和C相比最大的特點
1)面向物件:封裝,繼承,多型。
2)引入引用代替指標。
3)const /inline/template替代巨集常量。
4)namespace解決重名的問題。
5)STL提供高效的資料結構和演算法
2,你知道虛擬函式嗎
答案:實現多型所必須,父類型別的指標指向子類的例項,執行的時候會執行之類中定義的函式。
3,解構函式可以是虛擬函式嗎?
答案: 如果有子類的話,解構函式必須是虛擬函式。否則析構子類型別的指標時,解構函式有可能不會被呼叫到。
4,多型的實現。
答案:簡而言之編譯器根據虛擬函式表找到恰當的虛擬函式。對於一個父類的物件指標型別變數,如果給他賦父類物件的指標,那麼他就呼叫父類中的函式,如果給他賦子類物件的指標,他就呼叫子類中的函式。函式執行之前查表。
5,虛擬函式表是針對類還是針對物件的?
答案:虛擬函式表是針對類的,一個類的所有物件的虛擬函式表都一樣。
6,純虛擬函式和虛擬函式有什麼區別
答案:純虛擬函式就是定義了一個虛擬函式但並沒有實現,原型後面加"=0"。包含純虛擬函式的類都是抽象類,不能生成例項。
7,建構函式可以是虛擬函式嗎?
答案:每個物件的虛擬函式表指標是在建構函式中初始化的,因為建構函式沒執行完,所以虛擬函式表指標還沒初始化好,建構函式的虛擬函式不起作用。
8,建構函式中可以呼叫虛擬函式嗎?
答案:就算呼叫虛擬函式也不起作用,呼叫虛擬函式同調用一般的成員函式一樣。
9,解構函式中可以呼叫虛擬函式嗎?
答案:解構函式中呼叫虛擬函式也不起作用,呼叫虛擬函式同調用一般的成員函式一樣。解構函式的順序是先派生類後基類,有可能內容已經被析構沒了,所以虛擬函式不起作用。
10,虛繼承和虛基類?
答案:虛繼承是為了解決多重繼承出現菱形繼承時出現的問題。例如:類B、C分別繼承了類A。類D多重繼承類B和C的時候,類A中的資料就會在類D中存在多份。通過宣告繼承關係的時候加上virtual關鍵字可以實現虛繼承。
2
實現一個自己的String類是一道考驗C++基礎知識的好題。
至少要能實現以下:建構函式,解構函式,拷貝建構函式(copy constructor),過載賦值操作符(copy assignment operator),。
首先是至少能夠準確的寫出這幾個函式的宣告。
class String {
public:
String();
String(const char *);
//舊寫法:
//String(const String& rhs);
//String& operator=(const String& rhs);
//新寫法:
String(String rhs);
String& operator=(String rhs);
~String();
private:
char* data_;
}
其次,老版本的拷貝建構函式和過載賦值操作符時:有幾點需要注意的是:判斷自己賦值給自己 和 異常安全性。
通過使用swap可以簡化方法。
下面是老版本的拷貝建構函式的實現,new的時候有可能會丟擲異常。
String::String(const String& rhs) {
if (&rhs!=this) {
delete [] data_;
data_ = new char[rhs.size() + 1];
memcpy(data_, rhs.c_str(), rhs.size());
}
return *this;
}
String::~String() {
delete [] data_;
}
//使用swap的拷貝建構函式,通過swap將臨時變數rhs中的資料儲存到了data_中,同時data_中的資料拷貝到了臨時變數中,在函式返回時會被自動釋放。
一舉兩得,也不用擔心有異常發生了。
String::String(String rhs) {
std::swap(data_, rhs.data_);
}
String::String& operator=(String rhs) {
std::swap(data_, rhs.data_);
return *this;
}
String::String() : data_ = new char[1]{
*data_ = '\0';
}
3
STL相關的各種問題
1,用過那些容器。
最常用的容器就是:vector, list, map, hash_map等等。
2,vector,list,deque的實現。
vector是一塊連續記憶體,當空間不足了會再分配。
list是雙向連結串列。
deque是雙端佇列可在頭和尾部插入、刪除元素。
3,hashmap和map有什麼區別。
一個是基於hash表實現,一個是基於紅黑樹實現。
4,紅黑樹有什麼特性
5,STL仿函式和指標的差別。
6,配接器
7,一元、二元仿函式
4
C++面試題(一)、(二)和(三)都搞定的話,恭喜你來到這裡,這基本就是c++面試題的最後一波了。
1,你知道智慧指標嗎?智慧指標的原理。
2,常用的智慧指標。
3,智慧指標的實現。
1答案:智慧指標是一個類,這個類的建構函式中傳入一個普通指標,解構函式中釋放傳入的指標。智慧指標的類都是棧上的物件,所以當函式(或程式)結束時會自動被釋放,
2, 最常用的智慧指標:
1)std::auto_ptr,有很多問題。 不支援複製(拷貝建構函式)和賦值(operator =),但複製或賦值的時候不會提示出錯。因為不能被複制,所以不能被放入容器中。
2) C++11引入的unique_ptr, 也不支援複製和賦值,但比auto_ptr好,直接賦值會編譯出錯。實在想賦值的話,需要使用:std::move。
例如:
std::unique_ptr<int> p1(new int(5));
std::unique_ptr<int> p2 = p1; // 編譯會出錯
std::unique_ptr<int> p3 = std::move(p1); // 轉移所有權, 現在那塊記憶體歸p3所有, p1成為無效的指標.
3) C++11或boost的shared_ptr,基於引用計數的智慧指標。可隨意賦值,直到記憶體的引用計數為0的時候這個記憶體會被釋放。
4)C++11或boost的weak_ptr,弱引用。 引用計數有一個問題就是互相引用形成環,這樣兩個指標指向的記憶體都無法釋放。需要手動打破迴圈引用或使用weak_ptr。顧名思義,weak_ptr是一個弱引用,只引用,不計數。如果一塊記憶體被shared_ptr和weak_ptr同時引用,當所有shared_ptr析構了之後,不管還有沒有weak_ptr引用該記憶體,記憶體也會被釋放。所以weak_ptr不保證它指向的記憶體一定是有效的,在使用之前需要檢查weak_ptr是否為空指標。
3, 智慧指標的實現
下面是一個基於引用計數的智慧指標的實現,需要實現構造,析構,拷貝構造,=操作符過載,過載*-和>操作符。
template <typename T>
class SmartPointer {
public:
//建構函式
SmartPointer(T* p=0): _ptr(p), _reference_count(new size_t){
if(p)
*_reference_count = 1;
else
*_reference_count = 0;
}
//拷貝建構函式
SmartPointer(const SmartPointer& src) {
if(this!=&src) {
_ptr = src._ptr;
_reference_count = src._reference_count;
(*_reference_count)++;
}
}
//過載賦值操作符
SmartPointer& operator=(const SmartPointer& src) {
if(_ptr==src._ptr) {
return *this;
}
releaseCount();
_ptr = src._ptr;
_reference_count = src._reference_count;
(*_reference_count)++;
return *this;
}
//過載操作符
T& operator*() {
if(ptr) {
return *_ptr;
}
//throw exception
}
//過載操作符
T* operator->() {
if(ptr) {
return _ptr;
}
//throw exception
}
//解構函式
~SmartPointer() {
if (--(*_reference_count) == 0) {
delete _ptr;
delete _reference_count;
}
}
private:
T *_ptr;
size_t *_reference_count;
void releaseCount() {
if(_ptr) {
(*_reference_count)--;
if((*_reference_count)==0) {
delete _ptr;
delete _reference_count;
}
}
}
};
int main()
{
SmartPointer<char> cp1(new char('a'));
SmartPointer<char> cp2(cp1);
SmartPointer<char> cp3;
cp3 = cp2;
cp3 = cp1;
cp3 = cp3;
SmartPointer<char> cp4(new char('b'));
cp3 = cp4;
}