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
};
如下图所示:
[img]http://dl.iteye.com/upload/attachment/257950/636d052c-72b7-3f3d-af84-ef63c639a52e.png[/img]
这时候,你可以这么使用:
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风格,比起虚函数,拙劣之极.
万不得已,不要这么使用.
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
};
如下图所示:
[img]http://dl.iteye.com/upload/attachment/257950/636d052c-72b7-3f3d-af84-ef63c639a52e.png[/img]
这时候,你可以这么使用:
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风格,比起虚函数,拙劣之极.
万不得已,不要这么使用.