求教一個c++單例模式面試問題?
我在面試中回答了要把構造函數、拷貝函數和operator =置為私有保證單例的唯一性,面試官就說malloc一片內存然後利用memcpy初始化該內存,最後將內存的指針轉換為class Singleton類型的指針,一樣可以調用函數,還是構造了另外一個單例類。這個我沒get到點,我回來搜索了一下也沒找到能夠重載強制轉換運算符的。請各位指點一下。
面試官講的語法大概是這樣:
Fuck* shit = (Fuck*)malloc(sizeof(Fuck));
new(shit)Fuck();
shit-&>~Fuck();
free(shit);
/* 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二面都是問些什麼問題?