我有这个代码代表银行:
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对象是什么类型。
例如,并非每个帐户都可以选择增加已投入的金额 - 因此,我没有在基类中包含此方法:
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 :(得分: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的Object作为参数。 IVisitor在层次结构中每个派生类有一个方法。每个派生类都以不同方式实现AcceptVisitor,并调用与其具体类型(AccountTypeA&amp; AccountTypeB)对应的方法,并将具体引用传递给该方法。您实现了在从IVisitor派生的对象中使用更多派生类型的函数的功能。维基百科:Visitor Pattern
答案 1 :(得分:1)
对基类公共接口的限制(顺便说一下,在你发布的内容中没有一些'虚拟缺失')是C ++的作用。如果需要访问仅属于派生类的特定函数,则需要使用dynamic_cast将指针强制转换为该类。
如果您发现需要大量使用dynamic_cast,那么您的设计可能需要,但是如果不知道您正在处理的业务领域的确切细节,就很难对此进行评论。
解决问题的一种可能方法是提供访问帐户组件的方法。例如,基类方法GetPortfolio()可以返回Portfolio对象指针,但仅适用于具有Portfolios的帐户类。对于其他类,您将其GetPortfolio()方法定义为返回NULL。一旦你有了投资组合指针,你就可以使用投资组合界面(它本身可能代表一个类层次),而不是BankAccount。
答案 2 :(得分:1)
为什么需要“访问派生类对象的接口”?
如果您提供BaseBankAccount子类的示例以及要调用的方法,则可能有助于讨论。
我认为BaseBankAccount类中的纯方法也是虚拟的?
如果要调用BaseBankAccount的子类方法,通常需要将这些方法(作为虚拟方法)添加到BaseBankAccount基类。如果这些方法对BaseBankAccount的所有子类都没有意义,那么您可能需要重新考虑您的类设计。
E.g。
class BaseBankAccount {
public:
BaseBankAccount() {}
virtual void someNewMethod () = 0;
// ...
};
class SavingsBankAccount : public BaseBankAccount {
public:
virtual void someNewMethod () {
// ...
}
// ...
};
答案 3 :(得分:0)
虽然总是有风险,但有时是不可避免的。 例如,在将模板引入Java之前,您必须在使用标准集合时始终向下转换,因为它们只能使用对象。
我假设当你正在向下转换时,你通过dynamic_cast使它变得安全。我也假设你的基类中的所有东西都是虚拟的,你只是在这段代码中省略了它。
特别针对您的问题,没有一个正确的答案。一般来说,当您将一堆派生类聚集到一个异构集合中时,您要么将该集合用作“位桶”,要么打算使用最低公分母(基类的接口)。
如果案件是前者,那么可能有必要进行向下倾斜,而丑陋。 如果案例是后者,那么你必须问自己为什么你想要以不同的方式处理特定的子类型,以及你是否可以改变基类的实现以某种方式调用派生类中的相关内容(例如,通过派生法)。
在任何一种情况下,你可能都想重新考虑一下异类集合是否有意义,在某些情况下它确实没有。例如,为每个主要类型的帐户单独维护集合是否有意义,并且有一个“getAllAccounts”方法可以返回聚合?