`
kmplayer
  • 浏览: 496825 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

条款39:避免在继承体系中做向下转型(downcast)动作

 
阅读更多
1,先看个例子:
class Person { ... };
class BankAccount
{
public:
  BankAccount(const Person *primaryOwner,
              const Person *jointOwner);
  virtual ~BankAccount();
  virtual void makeDeposit(double amount) = 0;
  virtual void makeWithdrawal(double amount) = 0;
  virtual double balance() const = 0;
  ...
};

class SavingsAccount: public BankAccount
{
public:
  SavingsAccount(const Person *primaryOwner,
                 const Person *jointOwner);
  ~SavingsAccount();
  void creditInterest();                // add interest to account
  ...
};

使用:
list<BankAccount*> allAccounts;
for (list<BankAccount*>::iterator p = allAccounts.begin(); p != allAccounts.end(); ++p)
{
  (*p)->creditInterest();      // error!
}
错误:creditInterest在BankAccounts中不存在.

可行的纠正:
for (list<BankAccount*>::iterator p = allAccounts.begin(); p != allAccounts.end(); ++p)
{
  static_cast<SavingsAccount*>(*p)->creditInterest(); //downcast
}

注:转型(cast)之于程序员,犹如苹果之于夏娃.

2,如果又有一个新的账户加入.
class CheckingAccount: public BankAccount
{
public:
  void creditInterest();    // add interest to account
  ...
};

我们这时候必须这么做:
for (list<BankAccount*>::iterator p = allAccounts.begin(); p != allAccounts.end(); ++p)
{
  if (*p points to a SavingsAccount)
    static_cast<SavingsAccount*>(*p)->creditInterest();
  else
    static_cast<CheckingAccount*>(*p)->creditInterest();
}

根据型别做事,这不是C++的精神,应该使用虚拟函数.
class BankAccount { ... };      // as above

// new class representing accounts that bear interest
class InterestBearingAccount: public BankAccount
{
public:
  virtual void creditInterest() = 0; //提供接口
  ...
};
class SavingsAccount: public InterestBearingAccount
{
  ...                           // as above
};
class CheckingAccount: public InterestBearingAccount
{
  ...                           // as above
};

如下图所示:



这时候,你可以这么使用:
for (list<BankAccount*>::iterator p = allAccounts.begin(); p != allAccounts.end(); ++p)
{
  static_cast<InterestBearingAccount*>(*p)->creditInterest();
}

3,还有另外一种解决的办法:
class BankAccount
{
public:
  virtual void creditInterest() {} //基类提供一个什么都不做的缺省实现
  ...
};
class SavingsAccount: public BankAccount { ... };
class CheckingAccount: public BankAccount { ... };

list<BankAccount*> allAccounts;// look ma, no cast!

for (list<BankAccount*>::iterator p = allAccounts.begin(); p != allAccounts.end(); ++p)
{
  (*p)->creditInterest();
}

4,总结:为了摆脱downcast,不论花多少努力都是值得的.
但是偶尔还是无法摆脱downcast.
例如:当我们无权改变BankAccount, SavingsAccount, 或 allAccounts的定义.
这时候,相对于static_cast,还有个比较好的办法:"safe downcasting"(安全向下转型)
使用dynamic_cast,如果失败,会传回null指针.

回到一开始的地方:

class Person { ... };
class BankAccount
{
public:
  BankAccount(const Person *primaryOwner,
              const Person *jointOwner);
  virtual ~BankAccount();
  virtual void makeDeposit(double amount) = 0;
  virtual void makeWithdrawal(double amount) = 0;
  virtual double balance() const = 0;
  ...
};

class SavingsAccount: public BankAccount
{
public:
  void creditInterest();                // add interest to account
  ...
};

class CheckingAccount: public BankAccount
{
public:
  void creditInterest();                // add interest to account
  ...
};

使用:
list<BankAccount*> allAccounts; 

void error(const string& msg);     

//至少可以保证downcast失败时,可以侦测到.
for (list<BankAccount*>::iterator p = allAccounts.begin(); p != allAccounts.end(); ++p)
{
  if (SavingsAccount *psa = dynamic_cast<SavingsAccount*>(*p))
  {
    psa->creditInterest();
  }
  else if (CheckingAccount *pca = dynamic_cast<CheckingAccount*>(*p))
  {
    pca->creditInterest();
  }
  // uh oh — unknown account type
  else
  {
    error("Unknown account type!");
  }
}

注:downcast必然导致if-then-else风格,比起虚函数,拙劣之极.
万不得已,不要这么使用.
  • 大小: 24.2 KB
分享到:
评论

相关推荐

    downcast-localization:Downcast的本地化字符串

    downcast-localization:Downcast的本地化字符串

    downcast:用于下载播客的 Python 实用程序

    沮丧 用于下载播客的 Python 实用程序 亲爱的小灵魂,你有什么问题吗? 告诉我你为什么这么沮丧。 我已经有很长的运气不好了但我祈祷它终于消失了。 ,保罗西蒙

    深度探索模C++对象模型PDF

    Type-Safe Downcast(保证安全的向下转型操作) Type-Safe Dynamic Cast(保证安全的动态转型) References并不是Pointers Typeid运算符 7.4 效率有了,弹性呢? 动态共享函数库(Dynamic Shared Libraries) 共享...

    深度探索C++对象模型 超清版

    Type-Safe Downcast(保证安全的向下转型操作) Type-Safe Dynamic Cast(保证安全的动态转型) References并不是Pointers Typeid运算符 7.4 效率有了,弹性呢? 动态共享函数库(Dynamic Shared Libraries) 共享...

    《深度探索C++对象模型》(Stanley B·Lippman[美] 著,侯捷 译)

    Type-Safe Downcast(保证安全的向下转型操作) Type-Safe Dynamic Cast(保证安全的动态转型) References并不是Pointers Typeid运算符 7.4 效率有了,弹性呢? 动态共享函数库(Dynamic Shared Libraries) 共享...

    pandas_downcast-1.2.4.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pandas-downcast-1.2.2.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pandas-downcast-1.2.3.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pandas-downcast-1.1.0.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pandas-downcast-0.1.0.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pandas-downcast-1.2.1.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pandas-downcast-1.0.0.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pandas-downcast-1.0.1.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    pandas-downcast-1.2.0.tar.gz

    Python库是一组预先...例如,Matplotlib和Seaborn库在数据可视化领域内非常受欢迎,它们提供了广泛的工具和技术,可以创建高度定制化的图表和图形,帮助数据科学家和分析师在数据探索和结果展示中更有效地传达信息。

    重构_改善既有代码的设计[高清版]中文版

     Encapsulate Downcast 封装向下转型   Replace Error Code with Exception 用异常代替错误码   Replace Exception with Test 用测试代替异常  Chapter 11:Dealing with Generalization 处理泛化关系  ...

    重构-改善既有代码的设计+中文版

     Encapsulate Downcast 封装向下转型   Replace Error Code with Exception 用异常代替错误码   Replace Exception with Test 用测试代替异常  Chapter 11:Dealing with Generalization 处理泛化关系   ...

    重构-改善既有代码的设计 中文版.pdf

    10.13 Encapsulate Downcast(封装「向下转型」动作) 10.14 Replace Error Code with Exception(以异常取代错误码) 10.15 Replace Exception with Test(以测试取代异常) 第11章 处理概括关系 11.1 Pull Up ...

    重构——改善既有代码的设计

     Encapsulate Downcast 封装向下转型   Replace Error Code with Exception 用异常代替错误码   Replace Exception with Test 用测试代替异常  Chapter 11:Dealing with Generalization 处理泛化关系   ...

    重构-改善既有代码的设计(中文版)

     Encapsulate Downcast 封装向下转型   Replace Error Code with Exception 用异常代替错误码   Replace Exception with Test 用测试代替异常  Chapter 11:Dealing with Generalization 处理泛化关系   ...

    重构,改善既有代码的设计

     Encapsulate Downcast 封装向下转型   Replace Error Code with Exception 用异常代替错误码   Replace Exception with Test 用测试代替异常  Chapter 11:Dealing with Generalization 处理泛化关系  ...

Global site tag (gtag.js) - Google Analytics