2009-12-11 43 views
2

我曾嘗試使用VS2008中的代碼(可能已經包括在樣品中過多的情況下...):繼承模板參數和上溯造型早在C++

class Base 
{ 
    public: 
    void Prepare() { 
     Init(); 
     CreateSelectStatement(); 
     // then open a recordset 
    } 
    void GetNext() { /* retrieve next record */ } 
    private: 
    virtual void Init() = 0; 
    virtual string CreateSelectStatement() const = 0; 
}; 
class A : public Base 
{ 
    public: 
    int foo() { return 1; } 
    private: 
    virtual void Init() { /* init logic */ } 
    virtual string CreateSelectStatement() { /* return well formed query */ } 
}; 

template<typename T> class SomeValueReader : protected T 
{ 
    public: 
    void Prepare() { T::Prepare(); } 
    void GetNext() { T::GetNext(); } 
    T& Current() { return *this; } // <<<<<<<< this is where it is interesting 
    SomeValue Value() { /* retrieve values from the join tables */ } 
    private : 
    string CreateSelectStatement() const 
    { 
    // special left join selection added to T statement 
    } 
}; 

void reader_accessAmemberfunctions_unittest(...) 
{ 
    SomeValueReader<A> reader(); 
    reader.Prepare(); 
    reader.GetNext(); 
    A a = reader.Current(); 
    int fooresult = a.foo(); 
    // reader.foo()   >> ok, not allowed 
    Assert::IsEqual<int>(1, fooresult); 
}; 

可正常工作,即,具有獲得「A」成員函數和fooresult返回1。但是,當物體在單元測試函數的末尾被刪除則拋出異常:

System.AccessViolationException: 試圖讀取或寫入受保護的 記憶。這往往是一個跡象 其他內存已損壞

如果我改變電流()函數的返回類型:

T* Current() 
{ 
    T* current = dynamic_cast<T*>(this); 
    return current; 
} 

那麼一切都OK並沒有訪問衝突單元測試結束。有人可以告訴我第一個Current()實現有什麼問題嗎?謝謝,bouchaet。

+0

我很驚訝任何代碼生成「在單元測試功能的結尾」。似乎沒有任何這些對象具有析構函數。 –

+2

您有足夠的代碼將其粘貼到新項目中,將unittest函數重命名爲'main()',並從頭開始重現問題。我會嘗試。 –

回答

5

改變CreateSelectStatement,返回該實現的函數值(不是純虛)

string CreateSelectStatement() const { return ""; } 

,改變讀者的聲明之後(必須聲明應嚴格解釋爲函數原型C++)

SomeValueReader<A> reader; 

上面的例子使用gcc編譯和執行沒有錯誤,導致我相信實際的錯誤可能不存在於上面的源代碼中。不幸的是,我現在無法用VC進行測試。

我看不到任何明顯的原因,爲什麼你提出的改變可以解決問題,我能看到的唯一的其他錯誤是Base沒有聲明的虛擬析構函數,這意味着如果你刪除了一個Base *(或者其他一些不是實際類型的派生類),不正確的析構函數將會觸發。你應該聲明它爲

virtual ~Base() {} 

即使它是空的。

文體,它也是一個有點古怪,因爲在這裏你正在使用的模板,以解決在編譯時功能使用模板和虛函數以這種方式。我看不出SomeValueReader需要從T派生(而不是有成員變量)的原因。

+0

+1用於提示虛擬析構函數。這也是我認爲會成爲問題的原因。 – strager

+0

不錯的答案...... – KeatsPeeks

+0

+1用於暗示構圖而不是繼承。 – avakar

1

我沒有訪問visual studio,但是你的代碼的一個問題是CreateSelectStatement()在類A中沒有被聲明爲const。因此它與Base和SomeValueReader中的其他人有不同的簽名。沒關係,只要你不嘗試實例化一個A(也就是說,它是一個純粹的虛擬類,就像Base一樣)。但是你可以在reader_accessAmemberfunctions_unittest中實例化一個。我會希望你的編譯器爲此產生一個錯誤.... g ++ 4.4.1。可能這不是你的問題;很難說,因爲你的示例代碼包含其他一些錯誤。你應該儘量保持儘可能簡單的例子,同時仍然是可編譯的(它們應該包含你包含的頭文件)。您的代碼包含多餘的語句以及不可編譯的僞代碼,這使得調試工作變得更加困難。通過將您的代碼簡化爲最簡單的形式,您將學到很多東西,並且通常情況下,您隨後能夠解決您的問題,而無需訴諸幫助。這是調試代碼的基本步驟。

+0

+1我忽略了提到缺少的const聲明。 –

1

好吧,我的壞我沒有嘗試從VS編譯代碼。我只是簡單地打字並故意忽略一些細節。我必須說,不直接測試樣本並且只依賴我在真實項目中看到的行爲是一個非常糟糕的主意。所以一個可編譯的版本將是:

/* /clr option enabled */ 

class Base 
{ 
public: 
    void FuncA() {} 
protected : 
    Base() {} 
}; 

class Derived : public Base 
{ 
public: 
    int foo() { return 1; } 
}; 

template<typename T> class SomeValueReader : protected T 
{ 
public: 
    void FuncA() { T::FuncA(); } 
    T& Current() { return *this; } 
}; 


void main(char* args) 
{ 
    SomeValueReader<Derived> reader; 
    reader.FuncA(); 
    Derived derived; 
    derived = reader.Current(); 
    int fooresult = derived.foo(); 
    //reader.foo()   >> ok, not allowed 
}; 

現在,我不得不說,我不能讓這個示例產生訪問衝突。所以這是無關緊要的。儘管如此,我提出的修改是我在我的真實項目中發現的唯一的解決方案,我想知道爲什麼。

項目34:宗教組成繼承
我深知這個一般準則的,我也希望我的定義爲SomeValueReader組成。但是,SomeValueReader確實需要訪問受保護的函數和T的成員才能夠將自身調整爲T.只有一個成員T纔會向SomeValueReader提供足夠的信息來執行其響應。此外,SomeValueReader通過實施一組私有虛擬功能來利用Base公共非虛擬接口。否則,它將不得不重複一些代碼或邏輯。所以,我結束了這些選項,我要麼:

  • 聲明SomeValueReader基地的朋友,複製一些邏輯和訪問T受保護的成員;
  • 將基地保護的方法推廣給公衆(並且向所有人揭示太多的信息);
  • 或嘗試這個好奇的受保護的繼承(但確實增加了複雜性)。

我可能錯過了另一種選擇。由於我無法解決自己與朋友課「作弊」,因此我決定採用這種模板多態性。但我願意接受建議。

丟失的常量和析構函數
的常量是一個錯誤,由於注意力不集中(並且不嘗試編譯的代碼)。
缺少析構函數是一個遺漏,因爲我沒有看到它作爲一個重要的細節。但很少有人認爲這可能是錯誤的。事實上,這也是我想要的。內存損壞導致我們達到析構函數內部的錯誤。但是在真正的項目中,基本析構函數實際上是公開的和虛擬的。或者在本例中可能已經被保護和非虛擬,因爲沒有使用Base *。

+0

這個問題必須存在於其他地方,我擔心你所做的更改是隱藏它而不是以某種方式修復它。 要檢查的另一件事是你沒有多個類/結構具有相同的名稱(和名稱空間),但具有不同的定義。根據我們的經驗,VC常常在鏈接期間混合析構函數,導致在所述結構被釋放時導致分段錯誤。顯然這是一個錯誤,但編譯器會讓你擺脫它。 –