1. 程式人生 > >C++基礎知識彙總

C++基礎知識彙總

        面試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關鍵字可以實現虛繼承。

C++面試題(二)——自己實現一個String類 tanglu

實現一個自己的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';
}

C++面試題——STL相關各種問題 tanglu2004 STL相關的各種問題 1,用過那些容器。
最常用的容器就是:vector, list, map, hash_map等等。

2,vector,list,deque的實現。
vector是一塊連續記憶體,當空間不足了會再分配。
list是雙向連結串列。
deque是雙端佇列可在頭和尾部插入、刪除元素。

3,hashmap和map有什麼區別。
一個是基於hash表實現,一個是基於紅黑樹實現。

4,紅黑樹有什麼特性

5,STL仿函式和指標的差別。

6,配接器

7,一元、二元仿函式

C++面試題(四)——智慧指標的原理和實現

tanglu2004 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;
}