2009-04-22 235 views
5

我有這樣的代碼來表示銀行:
通過指針到基類的指針到派生類對象操作對象

class Bank { 
    friend class InvestmentMethod; 
    std::vector<BaseBankAccount*> accounts; 
public: 
//... 

BaseBankAccount是用於在銀行的所有帳戶一個抽象類:

class BaseBankAccount { 
    public: 
     BaseBankAccount() {} 
     virtual int getInterest() const = 0; 
     virtual int getInvestedSum() const = 0; 
     virtual void increaseDepositSum(int additionalSum) = 0; 
     virtual void close(std::string& reason) = 0; 
     virtual ~BaseBankAccount() {} 
}; 

問題是,當我通過指向基類對象的派生類對象的指針進行操作時,我可以調用的這組方法受BaseBankAccount公共接口的限制 - 無論REAL對象是什麼類型。

例如,不是每個賬戶有一個選項,以增加已投入總和 - 所以,我didn`t包括在基類中此方法:

class BankAccount: public BaseBankAccount { 
protected: 
    BaseDeposit* deposit; 
    double sumInvested; 
public: 
    BankAccount(int sum, int term, int inter): sumInvested(sum), depositTerm(term), interest(inter) {} 
    int getInterest() const { return interest; } 
    int getInvestedSum() const { return sumInvested; } 
    void prolong(int increaseTerm) { 
     depositTerm += increaseTerm; 
    } 
    void increaseInvestment(double addition) { 
      sumInvested += addition; 
    } 
    virtual ~BankAccount() {} 
}; 

的話,我想稱之爲:

Bank bank1(...); 
bank1.accounts[i]->increaseInvestment(1000.0); 

那麼,在這種情況下,我能做些什麼來訪問派生類對象的接口呢?據我所知,向下轉換爲具體類型每次我需要調用具體功能不好
創建一個派生自此的抽象類來擴展接口?
爲我需要實現的每種特定類型創建並行層次結構(看起來有點重要)?

+0

方法是虛擬的,當然 - 我真的很抱歉 – chester89 2009-04-22 07:39:32

+0

在我看來,要解決這個問題最簡單的方法是從的BankAccount數據字段推到它的基類 – chester89 2009-04-22 08:50:50

回答

7

的溶液以從基類訪問更多的派生類的特徵是訪問者模式。

class BaseBankAccount { 
public: 
    ... 
    virtual void AcceptVisitor(IVisitor& v) = 0; 
}; 


class AccountTypeA : public BaseBankAccount { 
public: 
    void TypeAFeature() {...} 

    void AcceptVisitor(IVisitor& v) 
    { 
     v.VisitAccountTypeA(*this); 
    } 
}; 

class AccountTypeB : public BaseBankAccount { 
public: 
    void TypeBFeature() {...} 

    void AcceptVisitor(IVisitor& v) 
    { 
     v.VisitAccountTypeB(*this); 
    } 
}; 

class IVisitor { 
public: 
    virtual void VisitAccountTypeA(AccountTypeA& account) = 0; 
    virtual void VisitAccountTypeB(AccountTypeB& account) = 0; 
}; 

class ConcreteVisitor : public IVisitor{ 
public: 
    void VisitAccountTypeA(AccountTypeA& account) 
    { 
     account.TypeAFeature(); //Can call TypeA features 
    } 

    void VisitAccountTypeB(AccountTypeB& account) 
    { 
     account.TypeBFeature(); //Can call TypeB Features 
    } 
}; 

這種相互作用並不是很明顯。您可以在基類中定義一個純虛擬方法AcceptVisitor,該類將IVisitor類型的對象作爲參數。 IVisitor在層次結構中每個派生類都有一個Method。每個派生類都以不同方式實現AcceptVisitor,並調用與其具體類型(AccountTypeA & AccountTypeB)相對應的方法,並將具體的引用傳遞給該方法。您可以實現在源自IVisitor的對象中使用更多衍生類型的層次結構的功能。維基百科:Visitor Pattern

0

不得不沮喪,雖然總是冒險,有時是不可避免的。 例如,在將模板引入Java之前,必須在使用標準集合時一直向下翻轉,因爲它們只能與Objects一起使用。

我假設,當你向下傾斜時,你正在使用dynamic_cast來保證它安全。我還假設你的基類中的所有東西都是虛擬的,並且你在這段代碼中省略了它。

具體到你的問題,沒有一個正確的答案。一般來說,當你將一組派生類聚合到一個異構集合中時,你要麼使用集合作爲「位桶」,要麼打算使用最低公分母(基類的接口)。

如果是前者的情況,那麼可能需要向下旋轉而不好看。 如果情況是後者,那麼你必須問自己爲什麼你想要以不同方式處理特定的子類型,以及是否可以改變基類的實現以某種方式調用派生類中的相關內容(例如,通過派生方法)。

在這兩種情況下,您都可能想重新考慮一個異構集合是否合理,在某些情況下實際上並不合適。例如,爲每個主要類型的帳戶分別維護集合,並且有一個可返回聚合的「getAllAccounts」方法是有意義的?

1

對基類公共接口的限制(順便說一下,在你發佈的內容中沒有一些虛擬的東西)是C++所做的。如果您需要訪問僅屬於派生類的特定函數,則需要使用dynamic_cast將指針轉換爲該類。

如果你發現需要使用dynamic_cast很多,那麼你的設計可能是想要的,但是很難評論這一點,而沒有詳細描述你正在處理的業務領域。

一種可能的方式解決這問題是提供訪問權限的帳戶組件的方法。例如,基類方法GetPortfolio()可以返回一個Portfolio對象指針,但僅適用於擁有Portfolios的賬戶類。對於其他類,您可以將它們的GetPortfolio()方法定義爲返回NULL。一旦擁有投資組合指針,您就可以使用投資組合界面(本身可能代表一類投資組合),而不是BankAccount。

+0

我從來沒有檢查,但會這個編譯? (有= 0但沒有虛擬?) – Uri 2009-04-22 07:31:35

+0

不,它不應該,但編譯器編寫者經常將= 0作爲它們自己的特殊屬性並用它做一些奇怪的事情。 – 2009-04-22 07:33:29

1

爲什麼需要「訪問派生類對象的接口」?

這可能有助於爲討論的目的,如果你提供BaseBankAccount與你想調用的方法的子類的實例。

我假設你BaseBankAccount類純方法意思是虛擬的呢?

如果你想給你打電話通常需要添加這些方法(虛擬)的BaseBankAccount基類BaseBankAccount的子類的方法。如果這些方法對BaseBankAccount的所有子類都沒有意義,那麼您可能需要重新考慮您的類設計。

E.g.

class BaseBankAccount { 
    public: 
     BaseBankAccount() {} 

     virtual void someNewMethod() = 0; 

     // ... 
}; 

class SavingsBankAccount : public BaseBankAccount { 
    public: 
     virtual void someNewMethod() { 
      // ... 
     } 

     // ... 
}; 
相關問題