標籤:

求教一個c++單例模式面試問題?

我在面試中回答了要把構造函數、拷貝函數和operator =置為私有保證單例的唯一性,面試官就說malloc一片內存然後利用memcpy初始化該內存,最後將內存的指針轉換為class Singleton類型的指針,一樣可以調用函數,還是構造了另外一個單例類。這個我沒get到點,我回來搜索了一下也沒找到能夠重載強制轉換運算符的。請各位指點一下。


面試官講的語法大概是這樣:

Fuck* shit = (Fuck*)malloc(sizeof(Fuck));
new(shit)Fuck();
shit-&>~Fuck();
free(shit);

C++可以惡搞的方法太多了,所以通常來講,我是這麼做的:

/* Fuck.h */

class IFuck abstract
{
public:
virtual ~IFuck() = default;

virtual void FuckHim() = 0;
virtual void FuckHer() = 0;
};

// 用於在main函數裡面調用確保正確的初始化順序,不需要考慮多線程問題。
extern void CreateFuck();

// 你調用的時候已經構造好了,不需要考慮多線程問題。
extern IFuck* GetFuck();

// 用於在main函數裡面刪除後調用檢查函數來看是否有內存泄漏,不需要考慮多線程問題。
extern void DestroyFuck();

/* Fuck.cpp */

class TheFuck : public IFuck
{
...
};

IFuck* fuck = nullptr;

void CreateFuck()
{
assert(fuck == nullptr);
fuck = new TheFuck();
}

IFuck* GetFuck()
{
assert(fuck != nullptr);
return fuck;
}

void DestroyFuck()
{
assert(fuck != nullptr);
delete fuck;
fuck = nullptr;
}

大概意思就是,頭文件裡面只留下介面,都沒有構造函數,你當然只能使用我提供的實例。


單例可以問一堆問題

1。 如何保證唯一?

2。如何處理初始化依賴?

3。初始化時,是否線程安全

4。進程退出時,能否自動釋放資源?


面試官腦抽了。也可能他根本不理解什麼是構造。也可能是想試試你的反應能力。

他的意思是,首先獲取單例對象的類型T。然後T *ptr = (T *)malloc(sizeof(T));然後就能通過ptr調用T的成員函數。

他這個做法的問題在於,根本沒有構造,他卻當構造好的用。事實上,malloc得到的存儲區是未被初始化的,任何讀都會導致未定義行為。他好歹memcpy一下阿。(你可以這樣回答)

即便memcpy了,也分情況。從語義上來說:

  • 如果T是trivially copyable的,那memcpy得到的是完全獨立的副本是可用的。並且兩個對象能一同使用。

  • 如果T僅僅只是trivial的,那memcpy得到的副本單獨用是安全的。但兩個對象一同使用是不安全的。
  • 如果T是非trivial的,那memcpy得到的副本,用了就炸。

或者他是想把T也藏起來。比方說將單列類定義在源文件的匿名命名空間里,用戶代碼通過一系列的非成員函數來訪問單例T。但這樣做很麻煩,而且容易出錯,他舉的情形不能很好的支撐這個做法。並且如果這樣做,對象就沒了,也不存在單例一說。

或者……我想不到了。我覺得你的說法是沒問題的,但是不夠嚴謹。

也就是說,你提出的措施是不能保證單例的,理由如上。如果足夠嚴謹,只能說這些措施能在一定程度上阻止用戶破壞單例模式。

你的說法的確存在一點問題,不過以正常的心態是能理解的。也就是說,面試官是在強行挑刺,動機不明。

總結:

在C++里,想要黑點東西出來,騙騙編譯器,只要你足夠努力,總歸是有辦法的。但他這個思路是胡來。


目測該面試官根本不會c++ 還強行裝逼


你讓面試官調用一個虛函數試試


就是這樣的面試官 以及他們所代表的對C++的認識,把C++這門語言給妖魔化了。


面試官的意思是,即便你隱藏了構造函數,按照c++的原理,仍然可以通過直接分配內存+強制類型轉換的方式得到一個全新的該class類型的對象實例。所以,面試官想提示你,隱藏構造函數並不是單實例模式中的關鍵性措施。關鍵性的措施是,提供一個用局部靜態變數保證返回的class指針唯一的靜態函數,並且要以編碼規範約定在class外部只能用該靜態函數來訪問該class的實例指針,這樣就能杜絕面試官所說的用內存分配獲得對象實例方法。有了這樣的規範,你甚至不需要去隱藏構造函數了。在實際編程中,程序員往往過分關注隱藏函數這類便捷性的手法,而忽視編碼的規範性。


我猜他是想說用malloc申請內存,然後又用placement new去調用構造函數初始化這片內存。

然而,幹嘛跟自己找不自在,明明有普遍接受且簡單的方法,非要炫技以示功力。


re @maxime maxime,一般構造單例用靜態成員加null檢查,然後只提供靜態介面比較安全。

話說回來,cast會調用構造么?這裡面邏輯是什麼?難道繼承類cast還要重新來一遍?不構造只做cast用了就掛吧……


其實要獲得單件,不一定要從保護構造函數入手, 也可以從數據本身入手:

1.一個沒有實例的單件

class Single1

{

private:

static Data *data;

public:

static Data* getData() { return data; }

static void setData(Data* p) { data = p; }

};

2.一個可以有無數實例的單件

class Single2

{

private:

static Data *data;

public:

Single2() {}

Data* getData() { return Single2::data; }

void setData(Data* p) { Single2::data = p; }

};

當然,C style的程序員說了,搞個全局變數不就完了嘛!

然後你問問面試官還有什麼辦法作妖:)


推薦閱讀:

最近去面試運營崗,一老總問:產品重要,還是營銷重要?
履歷毫無亮點的人,面試的自我介紹環節怎麼破?
面試結尾時,HR問「你還有什麼問題?」,我能問他薪資的問題嗎?
電話面試該問面試官什麼問題才「有戲」?
公司hr二面都是問些什麼問題?

TAG:面試問題 | CC |