【问题标题】:"Cross-cast" attempt using static_cast failing; why?使用 static_cast 的“交叉投射”尝试失败;为什么?
【发布时间】:2017-12-02 06:34:08
【问题描述】:

为什么“交叉演员”不起作用?

以下创建此对象继承模型:

     FOOD
    /    \
 CHEESE  CAKE

在这里,我尝试将static_cast 的指针从cheese 指向cake,制作芝士蛋糕:D。我从 Apple Clang/LLVM 收到以下错误:

 ERROR: Static cast from 'Cheese *' to 'Cake *', which are not related by inheritance, is not allowed

但它们是通过继承相关的:它们是兄弟姐妹。

为什么我不能将cheese 转换为cake,就像我将int 转换为float 一样?为什么我不能从继承自同一个基类的派生对象中“交叉转换”?

以下是最小、完整且可验证的示例,其中包括 static_castdynamic_cast 尝试,以突出显示错误。

#include <iostream>
#include <string>

class Food {
public:
    Food(std::string brand):brand_(brand) {}
    virtual ~Food() {};
    std::string brand() { return brand_; }
    virtual void cut()const { std::cout << "Food cut." << std::endl; }
    void eat()const { std::cout << "Food eaten." << std::endl; }
private:
    std::string brand_;
};

class Cheese : public Food {
public:
    Cheese(std::string brand):Food(brand) {};
    virtual void cut()const { std::cout << "Cheese cut.\n"; }
    void eat()const { std::cout << "Cheese eaten.\n"; }
};

class Cake : public Food {
public:
    Cake(std::string brand):Food(brand) {};
    virtual void cut()const { std::cout << "Cake cut.\n"; }
    void eat()const { std::cout << "Cake eaten.\n"; }
};

int main() {
    Food f("tbd");
    Cheese c("Cheddar");
    Cake cc("Cheesecake");
    Food * food_ptr;
    Cheese *cheese_ptr, *cheeseCake_ptr;
    Cake *cake_ptr;

    food_ptr = &f;
    food_ptr->cut();  //-> "Food cut."

    food_ptr = &c;
    food_ptr->cut();  //-> "Cheese cut." Result of virtual keyword.

    cheese_ptr = dynamic_cast<Cheese*> (food_ptr);
    cheese_ptr->cut(); //-> "Cheese Cut." The downcast worked

    food_ptr = &cc;
    cake_ptr = dynamic_cast<Cake*> (food_ptr);
    cake_ptr->cut(); //-> "Cake Cut." pointer reassignment and downcast worked.


    cheeseCake_ptr = dynamic_cast<Cheese*> (food_ptr);
    cheeseCake_ptr->cut(); //-> "Cake cut." Again, Food* dynamically casted to Cheese*

    /* ~~~ NOTE: THE FOLLOWING EXAMLES INTENTIONALLY THROW ERRORS ~~~ */

    /*
     Dynamic cross-cast attempt:
     ERROR: Assigning to 'Cheese *' from incompatable type 'Cake *'
     Dynamic cast: doensn't make sense, as the relationshiop between cheese and cake is not polymorphic
    */

    cheeseCake_ptr = dynamic_cast<Cake*> (cheese_ptr);
    cheeseCake_ptr->cut();

    /*
     Static cross-cast attempt:
     ERROR: Static cast from 'Cheese *' to 'Cake *', which are not related by inheritance, is not allowed
     Static cast: why doesn't this work? We know the data types.
     */

    cheese_ptr = &c;
    cake_ptr = &cc;
    cheeseCake_ptr = static_cast<Cake*> (cheese_ptr);

    std::cout << "\n\n";
    return 0;
}

【问题讨论】:

    标签: oop c++11 polymorphism dynamic-cast static-cast


    【解决方案1】:

    not related by inheritance 表示在同一个分支上...

    您不应将转换值误认为转换指针(或相反),它们是不同的类型转换。

    intfloat使用函数将给定类型的值转换为目标类型的值(注意这个函数可以是显式的也可以是隐式的,它是隐式的对于那种特殊情况,即在语言中定义)。在 C++ 中,您可以在自定义类类型上实现这一点,方法是为 A 类实现一个能够接收 B 类型值的 ctor,或者为 B 类实现一个返回 A 类型值的强制转换运算符。

    Cake *Cheese * 不转换值,而是尝试以不同的方式查看给定对象。它是子类型多态性(所以你需要使用dynamic_cast)。给定您的类型层次结构,通过使用 Food 指针指向 Cheese 对象,可以轻松地将 Cheese 对象视为(不转换)为 Food 对象(就像您可以被不同的人以不同的方式看待一样,(想想朋友、教授的方式)或者医生可以在您始终是同一个人时逮捕您!). 很明显(至少在您的层次结构中)奶酪不能被视为蛋糕,如果奶酪对象会如何反应的话Cake的具体做法?你是Animal,也是Dog,但很难想象我能把你看成Dog(你不是有4条腿吗?...

    不要被你的情况所迷惑。好吧,看起来 Cheese 和 Cake 的内容是一样的,但是给定的层次结构说明了事实:不在同一个分支上,你不能从 Cake 指针中获取 Cheese 指针。 C++ 编译器可以保护您免受这种情况的影响。

    现在,如果您使用多重继承,例如从 Cheese 和 Cake 继承的 CheeseCake,情况会发生轻微变化。然后给定一个通过 Cheese 指针查看的 CheeseCake 对象,可以将其转换为 Cake 指针,因为 CheeseCake 两者兼有!当然,即使在那种多重继承的情况下,Cheese 对象也不能通过指针被视为 Cake。

    【讨论】:

    • 但是同样的逻辑不也适用于转换值吗?假设我们将浮点数转换为整数。现在,我们可以截断 int 上的小数点吗?检索小数部分,找到它的天花板或地板?不!在从 float 转换为 int 之后,这些都没有意义......但我们仍然这样做。
    • 我告诉过你,在转换值时,你将类型转换 为另一种类型。使用指针没有转换!在值转换中,您有两个值,原始值和通过转换函数转换的值。在指针转换中,总是有一个对象,但有两个视图(指针是对象上的视图),根本没有转换(我可以将您视为 OP,而您的一些朋友将您视为足球运动员,同样人,两种不同的观点,没有价值转换,只有不同的方式与你互动)。
    【解决方案2】:

    错误说明了一切,ERROR: Static cast from 'Cheese *' to 'Cake *', which are not related by inheritance, is not allowed。尽管他们是兄弟姐妹,但他们没有继承关系。

    如果你绝对想做芝士蛋糕,你可以做多重继承。

        FOOD
       /    \
    CHEESE   CAKE
       \     /
      CHEESECAKE
    

    像这样:

    class CheeseCake : public Cake, public Cheese
    

    【讨论】:

    • 但是热量是两倍,因为继承不是虚拟的:(
    • 我不想把两者结合起来做芝士蛋糕;我想将芝士蛋糕称为cake 对象而不是cheese 对象。回想起来,我意识到这是一个令人困惑的例子。
    猜你喜欢
    • 2023-03-31
    • 2012-05-11
    • 2014-04-09
    • 2023-01-29
    • 1970-01-01
    • 2014-07-16
    • 1970-01-01
    • 2021-05-24
    • 1970-01-01
    相关资源
    最近更新 更多