开发者

About pointer downcasts/inheritance

开发者 https://www.devze.com 2023-01-08 11:41 出处:网络
So I\'m about finishing up prata\'s C++ primer and I\'m u to RTTI. He showed a line of downcasting and just said it\'s wrong but I want to see a better example.

So I'm about finishing up prata's C++ primer and I'm u to RTTI. He showed a line of downcasting and just said it's wrong but I want to see a better example.

class Grand
{
private:
    int hold;
public:
    Grand(int h=0) : hold(h) {}
    virtual void Speak() const { cout << "I am a grand class\n";}
    virtual int Value() const {return hold; }
    void Gah() const {cout << "ok" << endl;}
};

class Superb : public Grand
{
public:
    Superb(int h = 0) : Grand(h){}
    void Speak() const {cout << "I am a superb class!!\n";}
    virtual void Say() const
    { cout << "I hold the superb value of " << Value() << "!\n";}
    void Sah() const { cout << "Noak" << endl;}
};

class Magnificent : public Superb
{
private:
    char ch;
public:
    int hour;
    Magnificent(int h = 0, char c = 'A') : Superb (h), ch(c){}
    void Speak() const {cout << "I am a magnificent class!!!\n";}
    void Say() const {cout << "I hold the character " << ch <<
        "and the integer " << Value() << "!\n";}
    void Mah() const {cout << "Ok" << endl;}
};


Grand * GetOne()开发者_运维百科;

int _tmain(int argc, _TCHAR* argv[])
{
    /*
    srand(time(0));
    Grand * pg;
    Superb * ps;
    */

    Grand * pg = new Grand;
    Grand * ps = new Superb;
    Grand * pm = new Magnificent;

    Magnificent * ps2 = (Magnificent *)pg;

    ps2->Gah();

    cout << ps2->hour << endl;

    system("pause");
}

So above, I'm casting a base to a derived which is overall not to be done. However, in this example, what am I really limited to? When I am casting pg, I still have access through ps2 to all of the grand/superb/magnificent properties and methods. In other words, nothing fails here. Can anyone give me an example or add something to the code which will clearly show to me how assigning a base to a derived can mess things up?


Do not use C style casts.
They are not safe. C++ has introduced 4 new casts the one you are looking for is dynamic_cast<>

Magnificent * ps2 = dynamic_cast<Magnificent*>(pg);  // If pg is a Magnificent 
                                                     // (or is a super class of
                                                     // Magnificent) it works fine.
// If pg is not a Magnificent (in this case) it will return NULL.

When you use a C style cast you are telling the compiler to ignore all the rules and do what you tell it (which the compiler is happy to do). There is no checking done to make sure what you are doing makes any sense.

The C++ style casts are much more limiting and each does a specific range of casting. The dynamic_cast is used to cast up and down the class hierarchy.


Simply by setting a value to ps2->hour, you'll be overrunning memory - ps2 was allocated enough to hold a Grand instance, which is not enough for a Magnificent instance (as it has more class variables).

Try this experiment:
- Allocate an array of Grand objects.
- Set value of hold to something
- By casting, set value of hour to another value
- Print values of hold for all elements
- See what happens...


It'll break as soon as you call a virtual method implemented by the derived class that actually uses data members of the derived class. Right now you are just lucky - the Gah member function does not touch any data.

Here's an example where it really fails, demonstrating dangers of C-style casts in C++:

struct base
{
    virtual ~base() {}
    virtual void print() const { std::cout << "base" << std::endl; }
};

struct second_base // :)
{
    virtual ~second_base() {}
    virtual void second_print() const { std::cout << second << std::endl;
};

class derived: second_base, public base
{
    std::string name_;

public:

    explicit derived( const std::string& n ) : name_( n ) {}

    virtual void print() const
    {
        std::cout << "derived: " << name_ << std::endl;
    }
};

base* pbase( new base );
derived* pderived = ( derived* )pbase; // C-cast will allow this

pderived->print(); // BOOM!

The RTTI way of dealing with this is dynamic_cast, which would return 0 when you try casting to wrong subclass:

base* pbad( new base );
base* pgood( new derived( "humppa" ));

derived* pfails( dynamic_cast<derived*>( pbad ));
derived* pworks( dynamic_cast<derived*>( pgood ));

if ( pfails ) pfails->print(); // no cookie
if ( pworks ) pworks->print(); // prints "derived: humppa"

Study C++ casts and use them instead of C casts. Always!

0

精彩评论

暂无评论...
验证码 换一张
取 消