Keep Calm and Game On
r3dux | March 28, 2012
Would love one of these on the wall =D

Would love one of these on the wall =D
If you have to down-cast objects from a base class to a derived class then there’s probably a design flaw in the class structure. It might all work but it’s going to be a brittle design.
// C++ FAQS by Cline & Lowom // FAQ 162 - What is a down-cast? // Answer: Trouble. #include <iostream> using namespace std; class Asset { public: virtual ~Asset() { } virtual bool isLiquidatable() const { return false; } // BAD-FORM: Capability query }; class LiquidAsset : public Asset { protected: int mValue; public: LiquidAsset(int value = 100) : mValue(value) { } int getValue() const { return mValue; } void setValue(int theValue) { mValue = theValue; } virtual bool isLiquidatable() const { return true; } // BAD-FORM: Capability query }; // Userland function to liquidate an asset (if it can) int tryToLiquidate(Asset &asset) { int value; if ( asset.isLiquidatable() ) // BAD-FORM: Finds code using code { value = ((LiquidAsset&)asset).getValue(); // BAD-FORM: Down-cast ((LiquidAsset&)asset).setValue(0); // BAD-FORM: Down-cast cout << "Liquidated $" << value << endl; } else { value = 0; cout << "Sorry, couldn't liquidate this asset.\n"; } return value; } int main() { Asset a; LiquidAsset b; tryToLiquidate(a); tryToLiquidate(b); return 0; } |
A better way to accomplish this is to give the users of your code the right tools in the first place as member functions, rather than them having to cobble together their own routines in userland:
// C++ FAQS by Cline & Lowom // FAQ 163 - What is an alternative to using down-casts? // Answer: An if/down-cast pair can often be replaced by a virtual function call. The key // insight is to move the -context- of the capability query from the user's code into the // virtual function; don't just move the primitive query used in the user's if statements. #include <iostream> using namespace std; class Asset { public: virtual ~Asset() { } virtual int tryToLiquidate() { cout << "Sorry, couldn't liquidate this asset.\n"; return 0; } }; class LiquidAsset : public Asset { protected: int mValue; public: LiquidAsset(int value = 100) : mValue(value) { } int getValue() const { return mValue; } void setValue(int theValue) { mValue = theValue; } virtual int tryToLiquidate() { int value = mValue; mValue = 0; cout << "Liquidated $" << value << endl; return value; } }; int main() { Asset a; LiquidAsset b; a.tryToLiquidate(); b.tryToLiquidate(); return 0; } |
Much better =D
I’m reading Design Patterns: Elements of Reusable Object-Oriented Software at the moment, because when it comes time for me to write the biggest project I’ve ever worked on (which’ll be in a couple of months), I want it done on solid foundations, and without re-inventing the wheel. I’d heard of design patterns before but never used them in any of my code, so it’s going to be a pretty interesting learning curve – I’m actually kinda excited about it all.
Anyways, I came across this diagram in the book and thought it was pretty spiffy in the way it shows relationships between patterns, and what patterns work with others.

Neat!