1. 程式人生 > >經典問題解析五(五十五)

經典問題解析五(五十五)

構造函數異常 可變參數函數 匹配優先級

在面試中有可能會遇到這個面試題,編寫程序判斷一個變量是不是指針。我們咋一看是不是有點懵逼,我們可以想到利用 C 語言中的可變參數函數。在 C++ 中依然是支持的,C++ 編譯器的匹配調用優先級是:1、重載函數;2、函數模板;3、變參函數。我們可以將變量分為兩類:指針和非指針。需要編寫函數的功能是當是指針變量調用時便返回 true,是非指針變量調用時返回 false。

下來我們就來試著編寫下這個函數

#include <iostream>
#include <string>

using namespace std;

template
< typename T >
bool IsPtr(T* v)
{
    return true;
}

bool IsPtr(...)
{
    return false;
}

int main()
{
    int i = 0;
    int* p = &i;
    
    cout << "p is a pointer: " << IsPtr(p) << endl;
    cout << "i is a pointer: " << IsPtr(i) << endl;

    return 0;
}

我們利用函數模板和可變參數函數來實現,下來看看編譯結果是不是我們所期望的

技術分享圖片

我們看到已經實現了,於是滿意的交給了面試官。面試官看了下,笑著說你這個程序對一般的數據類型是可行的,對於類類型還是進行判斷嘛?我們接著來試下類類型的判斷是否還可行,在程序中添加一個類,再生成一個類對象 t,指向類對象 t 的指針 pt,下來看看編譯結果技術分享圖片

我們看到編譯直接報錯了,也就是說對於類對象來說並不行,變參函數無法解析對象參數。那麽我們想想怎麽辦呢,既然不能直接 IsPtr 函數的調用,我們還可以利用它的返回值類型的大小來進行判斷,將模板函數的返回值類型設置為 char,返回一個字符;將全局函數的返回值類型設置為 int,直接返回 0。再定義一個宏用來判斷函數 IsPtr 的返回值是不是等於 char 類型的大小,如果是則返回 1,否則返回 0

。我們來看看程序

#include <iostream>
#include <string>

using namespace std;

class Test
{
public:
    Test()
    {
    }
    virtual ~Test()
    {
    }
};

template
< typename T >
char IsPtr(T* v)
{
    return 'c';
}

int IsPtr(...)
{
    return 0;
}

#define ISPTR(p) (sizeof(IsPtr(p)) == sizeof(char))

int main()
{
    int i = 0;
    int* p = &i;
    
    cout << "p is a pointer: " << ISPTR(p) << endl;
    cout << "i is a pointer: " << ISPTR(i) << endl;
    
    cout << endl;
    
    Test t;
    Test* pt = &t;
    
    cout << "pt is a pointer: " << ISPTR(pt) << endl;
    cout << "t is a pointer: " << ISPTR(t) << endl;

    return 0;
}

我們再次編譯看看結果

技術分享圖片

我們看到已經編譯通過了,並且也正確進行類對象類型的判斷了。那麽這個面試題我們就完美的進行回答了。還有一個面試題:如果在構造函數中拋出異常會發生什麽?這便綜合考查到了我們的基礎知識了,涉及到對象的構造、異常以及其他方面的知識。那麽在構造函數中拋出異常,最直接的影響就是構造過程會立即停止,那麽當前的對象便無法生成了。由於是異常,析構函數同樣也無法被調用了,對象所占用的空間會立即收回。那麽在工程項目中的建議是:不要在構造函數中拋出異常,當構造函數可能產生異常時,我們便要使用二階構造模式

下來我們還是以代碼為例來進行分析

#include <iostream>
#include <string>

using namespace std;

class Test
{
public:
    Test()
    {
        cout << "Test()" << endl;
        
        throw 0;
    }
    virtual ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    Test* p = reinterpret_cast<Test*>(1);
    
    try
    {
        p = new Test();
    }
    catch(...)
    {
        cout << "Exception..." << endl;
    }
    
    cout << "p = " << p << endl;

    return 0;
}

我們在構造函數先打印函數名,在進行異常的拋出。先將指針 p 指向地址為 1 處,如果對象生成,那麽便會返回一個地址值。我們來看看編譯結果

技術分享圖片

在拋出異常後,我們看到 p 的地址還是為 1,證明並沒有對象的生成。我們應避免在析構函數中拋出異常!!析構函數的異常將導致對象所使用的資源無法完全釋放。通過對一些經典問題的學習,總結如下:1、C++ 中依然支持變參函數;2、變參函數無法很好的處理對象參數;3、利用函數模板和變參函數能夠判斷指針變量;4、構造函數和析構函數中不要拋出異常。


歡迎大家一起來學習 C++ 語言,可以加我QQ:243343083

經典問題解析五(五十五)