劍指offer面試題--類

       劍指offer和網上有很多考察類的面試題,我覺得很具有代表性,在這裏做一個總結:

     簡答題一:我們可以用static修飾一個類的成員函數,也可以用const修飾類的成員函數?

     答:錯誤的。一個類的成員函數使用const修飾,其意義是隱式的在參數中傳一個const this*參數。然而,static修飾的成員函數是沒有this指針的,與const是相反的。
     簡答題二:靜態成員函數能不能同時也是虛函數?
     答:不能。調用靜態成員函數不需要實例,但調用虛函數需要從一個實例中指向虛函數表的指針以得到函數的地址,因此調用虛函數是需要一個實例的,所以這靜態成員函數和虛函數是不能共存的。

 一。劍指offer面試題2:實現一個單例模式的類。

             要求:設計一個類,我們只能生成該類的一個實例。

       該題目要求是這個類只能生成一個實例,所以我們要想辦法控制類的構造函數,因爲生成實例的入口都是構造函數。

      解法一:將構造函數設置爲私有的,對於類的外部將是不可見的。並且將它設置爲靜態的,只有當條件滿足時唯一的調用一次

     

<pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:14px;">class Singleton
{
public:
    static Singleton GetInstance()
{
          if(instance==NULL)
        {
               instance=new Singleton();
         }
    return instance;
}
 private:
     static Singleton(){};
      static Singleton instance;
}</span>


           這種解法思路清晰且直觀,但是這隻適合單線程的模式下,如果多線程的情況下,會出現兩個線程同時訪問這個類,當遇到if語句時instance的值還爲NULL,兩個線程就都創建出一個實例,這就與題目要求不符合。


基於上面的解法一,我們可以得到可行解法二:

    

<pre name="code" class="cpp"><span style="font-family:Microsoft YaHei;font-size:14px;">class Singleton
{
public:</span>
<span style="font-family:Microsoft YaHei;font-size:14px;">  Singleton  getInstance()</span>
{ if (instance == NULL) {lock(); if (instance == NULL) { instance = new Singleton(); } unlock(); } return instance;}
<span style="font-family:Microsoft YaHei;font-size:14px;"> private:
     static Singleton(){};
      static Singleton instance;
}</span>

   
這樣只夠極低的機率下,通過越過了if (instance == NULL)的線程纔會有進入鎖定臨界區的可能性,這種機率還是比較低的,不會阻塞太多的線程,但爲了防止一個線程進入臨界區創建實例,另外的線程也進去臨界區創建實例,又加上了一道防禦if (instance == NULL),這樣就確保不會重複創建了。

二。劍指offer面試題48:不能被繼承的類

        要求:用C++設計一個不能被繼承的類。

      要想要實現一個不能繼承的類在C#中是比較簡單的,使用關鍵字sealed就行,所以在這裏我們既要實現一個簡易版的sealed關鍵字,是不是很牛的感覺,其實並不是很難得一件事。

       解法一:要想要一個類不能繼承,我們首先應該想到的是將類的構造函數設置爲私有的,如果一個類的構造函數被設置爲私有的,那麼它的子類將會無法調用它的構造函數,這和不能繼承是一樣的效果。同題一一樣,我們還想要得到這個類的一個實例並且釋放它,這是作爲一個類所應該具有的功能。我們可以通過定義公有的靜態函數來創建和釋放類的實例。

<span style="font-family:Microsoft YaHei;font-size:14px;">class  A
{
public:
    static  A * Construct(int  n)
    {
        A *pa = new A;
        pa->num = n;
        cout<<"num  is:"<<pa->num<<endl;
        return pa;
    }
    static  void Destruct(A * pIntance)
    {
        delete  pIntance;
        pIntance = NULL;
    }
private:
    A(){}
    ~A(){}
private:
    int num;
};
</span>
   這種方法只可以創建堆上的對象,不可以構建棧上的對象。

      這種方法相信大家都可以想到,但是爲了打動安靜的面試官,我們得拿出點料來才行。

解法二:虛擬繼承+友元類

<span style="font-family:Microsoft YaHei;font-size:14px;">#include<iostream>
using namespace std;
template <typename T> 
class Base
{
    friend T;
private:
    Base() {}
    ~Base() {}
};

class Finalclass : virtual public Base<Finalclass>
{                
public:
    Finalclass() {}
    ~Finalclass() {}
};
</span>
  Finalclass就是那個不能被繼承的類。

  繼承於Base,Base爲虛基類,因爲它是Base的友元,所以,它可以訪問基類的私有構造函數,以及析構函數。編譯運行時是正確的。也就是說,可以創建堆上的對象,並且可以構建棧上的對象。


三。含有指針成員的類的拷貝

      題目:下面是一個數組類的聲明和實現,分析這個類有什麼問題,並針對問標提出集中解決方案。

      

<span style="font-family:Microsoft YaHei;font-size:14px;">  Template<typename T> class Array
{
public:
 Array(unsigned arraySize):data(0), size(arraySize)
{
   if(size > 0)
   data = new T[size];
}
 ~Array()
{
   if(data) delete[] data;
}
 void setValue(unsigned index, const T& value)
{
   if(index < size)
   data[index] = value;
}
T getValue(unsigned index) const
{
   if(index < size)
     return data[index];
   else
     return T();
}
private:
   T* data;
   unsigned size;
}</span>
    眼尖的朋友應該看出來一點端倪,此類只定義了帶參數的構造函數,沒有定義無參數的構造函數,還有沒有定義拷貝構造函數和複製運算符的重載,系統將會自動生成一個,但是,恰恰系統就會坑了你,此類中有指針的數據成員,這是很危險的

       編譯器生成的缺省的構造拷貝函數和拷貝運算符的重載函數,對指針實行的是按位拷貝,僅僅只是拷貝指針的地址,而不會拷貝指針的內容。若執行Array A(10);Array B(A)時。A.data和B.data指向的同一地址。當A或者B中任意一個結束其生命週期調用析構函數時,會刪除data。由於他們的data指向的是同一個地方,兩個實例的data都被刪除了。但另外一個實例並不知道它的data已經被刪除了,當企圖再次用它的 data的時候,程序就會不可避免地崩潰。

    爲了防止這樣的情況發生,我們同樣將拷貝構造函數和賦值運算符的重載設爲私有的函數。
<span style="font-family:Microsoft YaHei;font-size:14px;">private:
   Array(const Array& copy);
   const Array& operator = (const Array& copy);</span>
     但是這樣的類並不是一個功能齊全的類,所以我們只能自己實現着兩個函數來解決問題了:
<span style="font-family:Microsoft YaHei;font-size:14px;">rray(const Array& copy):data(0), size(copy.size)
{
    if(size > 0)
  {
     data = new T[size];
     for(int i = 0; i < size; ++ i)
         setValue(i, copy.getValue(i));
   }
}
const Array& operator = (const Array& copy)
{
    if(this == &copy)
       return *this;
    if(data != NULL)
  {
     delete []data;
     data = NULL;
   }
   size = copy.size;
  if(size > 0)
  {
     data = new T[size];
    for(int i = 0; i < size; ++ i)
        setValue(i, copy.getValue(i));
   }
}</span>





     

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章