2010-01-13 32 views
2

我想寫一組類D_I(又名I = 1,2,...只是我的意思是'_I'是類專有名稱,而不是整數)它處理一些(布爾,例如)操作F對於所有的類都有相同的含義。但是我也想用這些類的對象的「和」來操作,這個組的不同類別可能會被「添加」在一起。提到這種對象總和的操作取決於添加對象的相應操作。C++運算符代碼繼承問題:我是否需要爲所有派生類複製相同的代碼?

這就是爲什麼我要創造共同的基B類來處理的操作「總結」:

class B 
    {public: 
     B(): LEFT(NULL), RIGHT(NULL) {}; 
     B (const SpBrd& S); 
     B (const B& A, const B B); 
     ~B(); 
      {if (LEFT != NULL) delete LEFT; // NULL if it is default B or D_I 
      if (RIGHT != NULL) delete RIGHT; // NULL if it is default B or D_I 
      } 
     B operator + (const B& S) const; 
     bool F(const Point& P) const; 
      {bool b = aF(P); 
      if (LEFT != NULL) b = b && LEFT->F(P); // this is how F depends 
      if (RIGHT != NULL) b = b && RIGHT->F(P); // of added classes 
      } 
    protected: 
     virtual bool aF(const Point& P) const {return true;}; // Default! 
     B* LEFT; // Pointer to left operand of "sum" 
     B* RIGHT; // Pointer to right operand of "sum" 
     // (since it might point to D_i class too as well) 
    }; 

所以這種方法得出的d類是容易寫,因爲它只能處理它的構造和AF :

class D_I: public B 
    {public: 
     D() {...}; 
    protected: 
     virtual bool aF (const Point& P) const 
      {// return something here 
      } 
    }; 

的問題是:如何編寫操作+和複製B類的構造函數:

B:B (const B& S) 
     {if (S.LEFT != NULL) LEFT = new B (S.LEFT) 
     if (S.RIGHT != NULL) RIGHT = new B (S.RIGHT)    
     }; 
    B B:operator + (const B& S) const 
     {SpBrd S; 
     S.LEFT = new B (*this); 
     S.RIGHT = new B (S); 
     }; 

將不會產生正確的結果,因爲如果'運算符+'或'S.LEFT'或'S'中的'this','S'應該被替換爲相應的'新D_I ...'複製構造函數中的.RIGHT不是指類B的對象,而是指類D_I中的一個對象。

所以,我無法找到一種方法讓B :: operator +知道總和的類型來源,因爲可能會有大量的D_I,並且它們會不時添加。我該怎麼做才能正確寫出這一切?

對於所有的(D_I,D_J)對,我無法編寫D_I運算符+(D_J)和D_I :: D_I(常量D_J)的語句,這是不公平的! (這將代表空間中的邊界(表面),函數F代表在邊界內 - 即檢查點是否在空間體內,但是我想操作的邊界內部的體可能是交叉的例如:兩個球體邊界相交 - 內部和外部區域 - 產生球形外殼aka 3D環;也可能需要查看半徑爲R,立體角爲10 *的相交球面邊界,圍繞杆等10度)

+0

藉口StackOverflow不尊重我的分段符,但是不安全: 什麼是'SpBrd'? 運算符+在底部沒有語法意義。 'B類'甚至不會在我的腦海裏編譯,因爲它的ctors和dtor的名字是錯誤的。 另外,考慮改寫你的問題。目前還不清楚: 「Group class」? 避免被動語態。 無論我如何閱讀它,「1)在運算符+如果*這和S不是B,但D_I應該是'新D_I'之一,甚至沒有讀到我像一個適當的英文句子。點2的同上)。 我會在這裏停下來。 – wilhelmtell 2010-01-13 16:57:37

+0

哦,我的上帝,我非常抱歉...提出這個問題,我只是通過刪除額外的函數來刪除額外的函數,並將類名如SpBrd和SphBrd更改爲Base和Derive(簡稱B和D)不要混淆讀者,但它變得相反...... 另外我已經解釋了該段落。再次抱歉。 – Nick 2010-01-13 17:17:22

+0

我更新了我的答案。我認爲它回答了你現在要問的問題,儘管我仍然對此感到困惑。 – Omnifarious 2010-01-13 22:35:13

回答

3

你的問題是混亂的,而且我感覺這裏衝,但我會說這個。

通常,運算符在類之外聲明爲非成員函數並使用成員函數來實現其目標。這樣一來,操作員的兩個參數都可以平等地參與重載解析。

在這種情況下,它看起來像你試圖創建一些基於C++運算符的分析樹。在這種情況下,像這樣的代碼可能是你在找什麼:

const B operator +(const B &a, const B &b) 
{ 
    return B(a, b); 
} 

注意,這會工作,即使從B來源的東西是一個操作數。如果我對你正在做的事情是正確的,你可能會有一個特殊的節點類型加入到eachother中,而operator +可以返回它。


現在,我看到您的澄清版本,這裏是一個不同的答案。

首先,我以這種方式質疑您使用operator +。運算符重載通常會導致人們使用你的類的驚喜,除非你使用operator +的行爲很像人們希望operator +一般行爲,它會導致更多的問題解決。

但你的實際問題似乎圍繞創建未知類型對象的副本,而無需大量的重複的代碼。當我看到重複的代碼時,我傾向於考慮模板。以下是一些示例代碼,可能比您需要使用模板更復雜一些,我認爲可以解決您的問題。

#include <memory> 
#include <iostream> 

template <class BaseType, class DerivedType> 
class Cloneable { 
public: 
    virtual ~Cloneable() {} 

    ::std::auto_ptr<DerivedType> clone() const { 
     return ::std::auto_ptr<DerivedType>(static_cast<DerivedType *>(i_clone())); 
    } 

protected: 
    virtual BaseType *i_clone() const { 
     return new DerivedType(dynamic_cast<const DerivedType &>(*this)); 
    } 
}; 

class A : public Cloneable<A, A> { 
public: 
    A() {} 
    A(const A &b) { 
     const void * const voidb = &b; 
     const void * const voidme = this; 
     ::std::cerr << "Creating a copy of the A at " << voidb << " and this new copy will reside at " << voidme << "\n"; 
    }; 
    virtual ~A() { 
     const void * const voidme = this; 
     ::std::cerr << "Destroying the A at " << voidme << "\n"; 
    } 
}; 

template <class Derived> 
class B : public A, public Cloneable<A, Derived> { 
public: 
    B() {} 
    B(const B &b) { 
     const void * const voidb = &b; 
     const void * const voidme = this; 
     ::std::cerr << "Creating a copy of the B at " << voidb << " and this new copy will reside at " << voidme << "\n"; 
    }; 
    virtual ~B() { 
     const void * const voidme = this; 
     ::std::cerr << "Destroying the B at " << voidme << "\n"; 
    } 
    // Make sure clone can be mentioned in derived classes with no ambiguity 
    using Cloneable<A, Derived>::clone; 

protected: 
    // Force dominance rules to choose the correct i_clone virtual function. 
    virtual A *i_clone() const { 
     return Cloneable<A, Derived>::i_clone(); 
    } 
}; 

class C : public B<C> { 
public: 
    C() {} 
    C(const C &b) { 
     const void * const voidb = &b; 
     const void * const voidme = this; 
     ::std::cerr << "Creating a copy of the C at " << voidb << " and this new copy will reside at " << voidme << "\n"; 
    }; 
    virtual ~C() { 
     const void * const voidme = this; 
     ::std::cerr << "Destroying the C at " << voidme << "\n"; 
    } 
}; 

class D : public B<D> { 
public: 
    D() {} 
    D(const D &b) { 
     const void * const voidb = &b; 
     const void * const voidme = this; 
     ::std::cerr << "Creating a copy of the D at " << voidb << " and this new copy will reside at " << voidme << "\n"; 
    }; 
    virtual ~D() { 
     const void * const voidme = this; 
     ::std::cerr << "Destroying the D at " << voidme << "\n"; 
    } 
}; 

int main(int argc, const char *argv[]) 
{ 
    C c; 
    D d; 
    ::std::auto_ptr<A> cptr(c.clone()); 
    ::std::auto_ptr<A> dptr(d.clone()); 
    cptr = dptr->clone(); 
    return 0; 
} 

我做了i_clone和克隆方法,使每個班將與版本克隆是返回一個指向類的自己的類型結束。我還必須在模板類B中聲明using,以確保在派生類中將調用克隆的模糊性。

請注意,類C和D不包含與創建自己的克隆有關的重複代碼。

雖然當時並不知道它,但這似乎是The Curiously Recurring Template成語的另一個重新發明,適用於polymorphic copy construction

+0

返回'const B'有什麼意義?我已經看過很多次了,但是我沒有看到這一點,因爲我完全可以寫出'B result = a + b;'。我在這裏錯過了什麼嗎? – 2010-01-13 18:54:25

+0

我總是從函數中返回const X,以避免允許奇怪的結構像'(a + b)= 5;'。 – Omnifarious 2010-01-13 19:40:27

+0

@Matthieu:它使得像'a + b = c'這樣的表達式無效,因爲它們具有內置類型。當解除引用操作符返回副本時,更多的問題是 - 你想'a [i] = b'編譯失敗,而不是悄悄地分配給臨時文件。 – 2010-01-13 19:44:10

2

這是一個有點難以遵循正是你正在嘗試做的(什麼是SpBrd),但你可能會考慮新的虛擬函數是這樣的:?

class B 
{ 
    ... 
    virtual B *Clone() const { return new B(*this); } 
    ... 
}; 
class D: public B 
{ 
    ... 
    virtual D *Clone() const { return new D(*this); } 
    ... 
}; 

然後可以這樣使用:

S.LEFT = this->Clone(); 
S.RIGHT = S.Clone(); 
+0

非常感謝,這應該很好,當然。唯一的缺點是我應該使用幾乎相同的代碼爲所有D_I類創建這樣的複製函數! 這將是巨大的,如果C++允許使用的不是「新類型(ARG)」,但「pointer_to_object_of_Type - >運營商新的(ARG)」,但我不覺得,如果它是可能的。 再次感謝和抱歉的SpBrd,當然它是B.而D_I會是SphBrd,AlphaBrd,DeltaBrd等...... – Nick 2010-01-13 17:26:01

+1

這些虛擬函數應該是const限定的,即虛擬B * Copy()const – 2010-01-13 17:32:23

+1

這種模式通常使用動詞「克隆」而不是「複製」 - 命名你的函數克隆()可能會更清楚地表達意圖。見例如Java Cloneable接口:http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Cloneable.html – 2010-01-13 17:33:26