2010-01-27 99 views
2

所以我有幾個類正是如此定義:使用C++子類實例作爲默認參數?

class StatLogger { 
public: 
    StatLogger(); 
~StatLogger(); 

    bool open(<parameters>); 

private: 
    <minutiae> 
}; 

,使用它下降來實現空對象模式的子類(未開啓它自己的空對象)

class NullStatLogger : public StatLogger { 
public: 
    NullStatLogger() : StatLogger() {} 
}; 

然後,我有我想第三類有一個可選的記錄器實例在其構造函數:

class ThirdClass { 
public: 
    ThirdClass(StatLogger& logger=NullStatLogger()); 
}; 

我的問題是,當我這樣做如上,我得到:

error: default argument for parameter of type ‘StatLogger&’ has type ‘NullStatLogger’

如果我把一個明確鑄造的定義,我得到:

error: no matching function for call to ‘StatLogger::StatLogger(NullStatLogger)

抱怨沒有從NullStatLogger構造函數,即使它的一個子類。我在這裏做錯了什麼,這是允許在C + +?

+0

@luke:'NullStatLogger()'不是一個類型,而是一個類型爲'NullStatLogger'的值初始化右值。 – 2010-01-27 20:06:52

+0

在使用Jerry接受的解決方案之前,請閱讀該答案的所有可用註釋以便討論提議的解決方案和其他替代方案的含義。 – 2010-01-28 08:08:45

回答

5

我想要使用繼承和多態,ThirdClass需要使用一個指針或對StatLogger對象的引用,而不是實際的對象。同樣,在這種情況下,您幾乎肯定需要虛擬StatLogger::~StatLogger()

例如,作如下修改,代碼應該順利地編譯:

class StatLogger { 
public: 
    StatLogger(); 
    virtual ~StatLogger(); 

// bool open(<parameters>); 

private: 
// <minutiae> 
}; 

class NullStatLogger : public StatLogger { 
public: 
    NullStatLogger() : StatLogger() {} 
}; 

class ThirdClass { 
    StatLogger *log; 
public: 
    ThirdClass(StatLogger *logger=new NullStatLogger()) : log(logger) {} 
}; 

編輯:如果你喜歡一個參考,代碼看起來是這樣的:

class StatLogger { 
public: 
    StatLogger(); 
    virtual ~StatLogger(); 

// bool open(<parameters>); 

private: 
// <minutiae> 
}; 

class NullStatLogger : public StatLogger { 
public: 
    NullStatLogger() : StatLogger() {} 
}; 

class ThirdClass { 
    StatLogger &log; 
public: 
    ThirdClass(StatLogger &logger=*new NullStatLogger()) : log(logger) {} 
}; 
+0

只要記得刪除'ThirdClass''析構函數中的日誌。 – luke 2010-01-27 17:45:27

+2

@luke:這實際上是相當醜陋的,因爲它被寫入 - 如果和(醜陋的部分)只有在使用被分配作爲默認參數的記錄器時,'ThirdClass'應該刪除它的dtor中的'log'。否則,你*通常*希望分配和刪除被鏡像,所以刪除'log'應留給分配給它的人...... – 2010-01-27 17:53:05

+0

有沒有什麼辦法可以通過引用來實現呢?如果可能的話,我寧願在傳遞指針時使用它... – 2010-01-27 18:10:06

1

好一個默認值我相信你必須提供默認值...

ThirdClass(StatLogger *logger = NULL) 

例如

2

使用派生實例作爲基準引用的默認參數沒有問題。

現在,你不能綁定一個非常量引用臨時(右值),這可能是編譯器抱怨你的代碼的原因之一,但我希望有一個更好的診斷消息(不能將臨時引用或引用一樣)。

這個簡單的測試編譯完美:

class base {}; 
class derived : public base {}; 
void f(base const & b = derived()) {} // note: const & 
int main() { 
    f(); 
} 

如果函數必須修改所接收到的參數考慮重構的指針,並提供一個默認空值(不是默認動態分配對象)。

void f(base * b = 0) { 
    if (b) b->log("something"); 
} 

只有當你想保持非const引用接口,並在同一時間提供了一個默認實例,則必須提供一個靜態實例,但我會建議不要這樣:

namespace detail { 
    derived d; 
    // or: 
    derived & null_logger() { 
     static derived log; 
     return log; 
    } 
} 
void f(base & b = detail::d) {} 
// or: 
void g(base & b = detail::default_value()) {} 
3

基於在Jerry's answer的討論,怎麼樣通過不使用在所有默認的變量簡化了問題:

class ThirdClass 
{ 

    StatLogger log; 

    public: 

     ThirdClass() : log(NullLogger()) {} 
     ThirdClass(const StatLogger& logger) : log(logger) {} 
}; 
+0

這會很好地工作,我認爲... – 2010-01-28 02:52:27

0

嗯,我知道這是一個oooold的問題,但我剛剛有完全相同的問題,並閱讀所有建議的答案和評論後,我想出了一個稍微不同的解決方案。

我想這也可能只是適用於這裏提出的問題的實例,所以這裏有雲:

充分利用NullStartLogger一個singleton類型的對象!

對我來說,這是相當優雅和排序。很快,單例是一個你不能隨意構造的對象,因爲在任何時候只有一個實例可以存在。 (或者,第一次使用之前可能有0個實例,因爲您可以推遲初始化)。您當然可以將單例功能添加到派生類中,而父類的所有其他實例(和派生)都可以正常初始化並創建。但是,如果NullStatLogger與我的情況一樣,只是一個虛擬類,它不會在外部存儲任何數據,並且根據實例/ init參數沒有不同的行爲,單例類很適合。

這裏是一個簡短的代碼剪斷使您NullStatLogger一個單身,還有一種方式在ThirdClass使用它:

class NullStatLogger : public StatLogger { 
private: 
    NullStatLogger() : StatLogger() {} 
    static NullStatLogger *_instance; 
public:  
    static NullStatLogger &instance(); 
}; 

NullStatLogger::_instance = 0; 

NullStatLogger &NullStatLogger:instance() { 
    if (_instance == 0) 
     _instance = new NullStatLogger(); // constructor private, this is 
              // the only place you can call it 
    return *_instance;      // the same instance always returned 
} 


class ThirdClass { 
public: 
    ThirdClass(StatLogger& logger=NullStatLogger::instance()); 
}; 

我知道這肯定不會幫助任何人問的問題,但希望它可以幫助別人。

+0

如果對象是讀/寫的,這是行不通的,因爲隨着時間的推移,null可能會變成別的東西(儘管坦率地說它不應該)。但是我會擔心的因爲ThirdClass接受一個非const記錄器。 – 2012-10-27 23:55:11

+0

@AlexisWilke在我的情況下,我作爲單例使用的類是一個沒有真正功能的虛擬對象(例如,只有函數具有常量'return 1'行爲),只是在實現更復雜的功能之前進行測試的佔位符。它從來不打算被最終用戶使用。另外,在'ThirdClass()'中,如果你嘗試給'logger'分配任何不是'NullStatLogger'的默認參數啓動的任何東西,你試圖分配一個錯誤的類類型,並且唯一可用的'NullStatLogger'實例是已經包含在'logger'中的那個,所以沒有造成任何傷害。 – penelope 2012-10-28 12:47:07