我一直在試圖找到一個單子的實作,其中:
- 我可以有一個抽象的
Application類 。
- 我可以有一個抽象的
Application類 。
- 并且可以有多個從
Application派生的類,例如SampleApplication和OtherSampleApplication。 - 但是我只想要一個
Application的實體,不管實際的型別是什么,所以我一直試圖讓它成為一個單子。
這是我當前的單子實作:
template<class T>
class Singleton
{
public:
static T& GetInstance()
{
static T instance;
return instance。
}
Singleton(Singleton const&) = delete;
Singleton(Singleton& &) = delete;
Singleton& operator=(Singleton const& ) = delete;
Singleton& operator=(Singleton& &) = delete;
protected:
Singleton() = default;
virtual ~Singleton() = default;
};
然后,對于派生類,我嘗試了一些方法:
class Application : public Singleton<Application>withclass SampleApplication : public Application
這將不作業,因為我們試圖創建一個Application實體,而不是一個SampleApplication.class Application : public Singleton<Application>withclass SampleApplication : public Application, public Singleton<SampleApplication>
這不能編譯,因為GetInstance()是不明確的。我試著用virtual繼承來解決這個問題,但無法讓它編譯。class Applicationwithclass SampleApplication : public Application, public Singleton<SampleApplication>
不符合我上面列出的 "要求",因為我們可以有另一個派生類的單子。
有誰知道有什么方法可以完成這個任務?
uj5u.com熱心網友回復:
既然你提到你會滿足于一個運行時錯誤:
template<class T>。
class Singleton : private ReallySingleton {
單子的其余部分是原樣的。私有的基類將會是這樣的:
。
class ReallySingleton {
static int counter=0;
protected:
ReallySingleton()
{
if ( counter > 1)
拋出 std::runtime_error{"Too many singletons"}。
}
};
(ReallySingleton::counter將需要在方便的地方進行定義)
uj5u.com熱心網友回復:
這個解決方案顯示了只允許一個基于基類的實體是如何作業的:
//---------------------------------------------------------------------------------------------
//type for checking if something is a singleton later
結構 SingletonBase
{
};
//---------------------------------------------------------------------------------------------
//專門的單子用于基類(Application)
//keep track of whether an instance of the baseclass has already been made
template<typename T>
struct Singleton :
public SingletonBase
{
Singleton()
{
has_instance = true;
}
static bool has_instance;
};
//has_instance initilization
template<typename T>
bool Singleton<T>::has_instance = false;
//---------------------------------------------------------------------------------------------
///應用程式的基類。
class Application 。
public Singleton< Application>
{
protected:
Application() = default; // do not allow client code to make instance of base class.
};
class SomeOtherKindOfApplication 。
public Singleton< SomeOtherKindOfApplication>
{
protected:
SomeOtherKindOfApplication() = default;
};
//---------------------------------------------------------------------------------------------
//從Application和SomeOtherApplication派生出來的應用型別。
class Application1 :
public Application
{
public:
void Hello()
{
std::cout << "Hello, " << std::endl;
}
};
class Application2 。
public Application
{
public:
void World()
{
std::cout << "World !" << std::endl;
}
};
class SomeOtherKindOfApplication1 。
public SomeOtherKindOfApplication
{
public:
void Bye()
{
std::cout << "再見!" << std::endl;
}
};
class NotASingleton
{
};
//---------------------------------------------------------------------------------------------
//Singleton實體獲取器
template<typename T>
static T& GetInstance()。
{
static_assert(std::is_base_of_v<SingletonBase, T>, "只能創建從singleton派生的類實體")。
if (T::has_instance)
{
throw std::runtime_error("一個應用實體已經被制作出來了") 。
}
static T instance;
return instance。
}
//---------------------------------------------------------------------------------------------
int main()
{
auto instance = GetInstance<Application1>()。
instance.Hello()。
try
{
//獲得另一個應用派生的實體。
// from Application will fail (runtime)
auto instance2 = GetInstance<Application2> ();
instance2.World()。
}
catch (const std::exception& e)
{
std::cout << e.what() << std::endl;
}
//但是用另一個基類獲得應用應該是可以的。
auto instance3 = GetInstance<SomeOtherKindOfApplication1> ();
instance3.Bye()。
//下一行甚至無法編譯,NotASingleton不是一個單子。
// auto no_instance = GetInstance<NotASingleton>();
}
uj5u.com熱心網友回復:
這是一種方法,如果有超過一個從 這種方法的總體情況是簡單的繼承,再加上為定義從 最后一行將在后面解釋。 首先,讓我們隱藏 定義將被添加到其他地方,而不是在頭檔案中。將定義從頭檔案中移除是在 現在記錄一下,當從Application派生的類,將產生編譯時錯誤。通常情況下。它不是萬無一失的,而且它確實有缺點。我認為這是值得考慮的。
鑒于類的名稱為 "Application",我猜想這有可能是一個庫的一部分。如果該庫不是僅有頭的,那么還有一個額外的考慮,我將在最后解決這個問題。
Application派生的類的人提供的額外步驟。
class Application : public Singleton< Application> { /* ... */ };
class SampleApplication : public Application{ /* ... */ }。
REGISTER_APPLICATION(SampleApplication) // must not be in a header
Singleton<Application>::GetInstance(),由于Application是抽象的,所以它將不會被編譯。(我假設每個人都會輸入更短的Application::GetInstance()來使用這個函式;如果不是,這種方法就是廢品。) 將函式宣告添加到抽象類中,但還不要添加定義。class Application : public Singleton< Application> {
public:
static Application & GetInstance()。
//...。
};
Singleton模板中隱藏該函式的原因。(雖然extern關鍵字允許將一些模板定義移出頭檔案,但我的理解是,當有行內定義時,它并不能完全抑制行內定義,就像在這個例子中)。GetInstance()的定義將放在哪里?這就是額外步驟的地方了。這也是我可能會受到抨擊的地方,因為除非有需要,否則宏是邪惡的。它是需要的。把這個函式的定義放在一個宏里。
#define REGISTER_APPLICATION(name)
Application& Application::GetInstance()
{
靜態名稱instance。
回傳instance。
}
Application派生時,這個宏必須在源檔案中使用,而不是頭檔案。該宏的引數是派生類的名稱;如果派生類是SampleApplication,那么要添加到源檔案中的行是REGISTER_APPLICATION(SampleApplication)。
那么,如果有人搞砸了會怎樣?如果沒有使用這個宏,聯結器將抱怨對Application::GetInstance()的未定義參考。不幸的是,這并不像我希望的那樣清楚問題所在。也許你可以在你的框架的 FAQ 中加入一個涵蓋這個問題的條目。
如果該宏被使用了兩次,聯結器將抱怨Application::GetInstance()的多個定義。這就是為什么該定義不能行內給出。我們希望適用一個定義的規則。這并不是萬無一失的(也許有兩個從Application派生的類,而只有一個記住了這個宏),但它可能是足夠的。
對于一個
Application的部分內容在頭檔案之外定義的庫來說,會有一個問題,即Application::GetInstance()不能成為庫的一部分,從而導致編譯庫時出現未定義參考。一個解決方案是讓Application成為另一個應該是庫內部的類的包裝器。例如:
class Application : public InternalApplication, public Singleton< Application> {
static Application & GetInstance()。
//其他一切都繼承自InternalApplication.。
};
現在Application可以只用頭,而InternalApplication可以有靜態(或動態)庫中的組件。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/320077.html
標籤:
下一篇:在cpp中模擬列舉繼承的解決方案
