2010-07-19 76 views
0

所以我正在完成prata的C++入门,我是你的RTTI。他表现出一系列的下降,只是说这是错误的,但我想看到一个更好的例子。关于指针向下转换/继承

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"); 
} 

因此,上面我将一个基础投射到一个总体上不被完成的派生。然而,在这个例子中,我真的只限于?当我投Pg时,我仍然可以通过ps2访问所有宏伟/精湛/宏伟的属性和方法。换句话说,这里没有任何失败。任何人都可以给我一个例子或添加一些代码,这将清楚地向我展示如何分配一个基地派生可以搞砸了吗?

回答

2

不要使用C风格的演员。
他们不安全。 C++已经推出4个新蒙上你正在寻找一个是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. 

当您使用C风格的转换,你是在告诉编译器忽略所有的规则,做你告诉它(编译器是什么很高兴做)。没有检查,以确保你在做什么是有道理的。

C++风格强制转换更具有限制性,每个强制转换都有特定的范围。 dynamic_cast用于在类层次结构中上下颠倒。

0

只需通过一个值设置为ps2->hour,你会超越内存 - 被分配PS2到足以容纳一个Grand实例,这是不够的,一个Magnificent实例(因为它有更多的类变量)。

试试这个实验:
- 分配一个Grand对象的数组。
- 的hold设定值的东西
- 铸造的hour设定值到另一个值
- 的hold打印值的所有元素
- 看看会发生什么......

0

它会为打破一旦您调用实际使用派生类的数据成员的派生类实现的虚方法。现在你真幸运 - Gah成员函数不会触及任何数据。

下面是一个例子,其中它真的失败了,证明C样式用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! 

处理这个问题的RTTI方法是dynamic_cast,这将返回0当您尝试投放到错误的子类:

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" 

研究C++ casts并使用它们代替C casts。总是!

+0

好吧,现在明白了,但上面的例子运行的是基础打印而不是派生的打印,即使它们是虚拟功能... – Ilya 2010-07-19 20:56:00

+0

是的,意识到发布后。更新了多个继承的示例,其中'base'不是第一个基类。 – 2010-07-19 21:28:31