C++ 强制转换为派生类
Posted
技术标签:
【中文标题】C++ 强制转换为派生类【英文标题】:C++ cast to derived class 【发布时间】:2011-07-15 20:39:10 【问题描述】:如何转换为派生类?以下方法都给出以下错误:
无法从 BaseType 转换为 DerivedType。没有构造函数可以接受 源类型或构造函数重载决议不明确。
BaseType m_baseType;
DerivedType m_derivedType = m_baseType; // gives same error
DerivedType m_derivedType = (DerivedType)m_baseType; // gives same error
DerivedType * m_derivedType = (DerivedType*) & m_baseType; // gives same error
【问题讨论】:
我不相信最后一个给出了同样的错误。 你确定你的 DerivedType 继承自 BaseType。你能发布更多代码吗? 如果您有任何 C# 或 Java 语言背景,您应该注意动态类型信息仅在您有指针时才真正使用(例如 BaseType *b = new DerivedType())。否则,你最终会被切片。 一开始你不应该这样做。这就是您收到错误的原因。正确完成后(通过 dynamic_cast),结果应该是 NULL 或异常。你真正想做什么? 你不能这样做,因为 BaseType 不是 DerivedType。您不能将 Animal 转换为 Dog,但可以将 Dog* 转换为 Animal*。 【参考方案1】:这样想:
class Animal /* Some virtual members */ ;
class Dog: public Animal ;
class Cat: public Animal ;
Dog dog;
Cat cat;
Animal& AnimalRef1 = dog; // Notice no cast required. (Dogs and cats are animals).
Animal& AnimalRef2 = cat;
Animal* AnimalPtr1 = &dog;
Animal* AnimlaPtr2 = &cat;
Cat& catRef1 = dynamic_cast<Cat&>(AnimalRef1); // Throws an exception AnimalRef1 is a dog
Cat* catPtr1 = dynamic_cast<Cat*>(AnimalPtr1); // Returns NULL AnimalPtr1 is a dog
Cat& catRef2 = dynamic_cast<Cat&>(AnimalRef2); // Works
Cat* catPtr2 = dynamic_cast<Cat*>(AnimalPtr2); // Works
// This on the other hand makes no sense
// An animal object is not a cat. Therefore it can not be treated like a Cat.
Animal a;
Cat& catRef1 = dynamic_cast<Cat&>(a); // Throws an exception Its not a CAT
Cat* catPtr1 = dynamic_cast<Cat*>(&a); // Returns NULL Its not a CAT.
现在回顾你的第一句话:
Animal animal = cat; // This works. But it slices the cat part out and just
// assigns the animal part of the object.
Cat bigCat = animal; // Makes no sense.
// An animal is not a cat!!!!!
Dog bigDog = bigCat; // A cat is not a dog !!!!
您应该很少需要使用动态转换。 这就是我们有虚拟方法的原因:
void makeNoise(Animal& animal)
animal.DoNoiseMake();
Dog dog;
Cat cat;
Duck duck;
Chicken chicken;
makeNoise(dog);
makeNoise(cat);
makeNoise(duck);
makeNoise(chicken);
我能想到的唯一原因是,如果您将对象存储在基类容器中:
std::vector<Animal*> barnYard;
barnYard.push_back(&dog);
barnYard.push_back(&cat);
barnYard.push_back(&duck);
barnYard.push_back(&chicken);
Dog* dog = dynamic_cast<Dog*>(barnYard[1]); // Note: NULL as this was the cat.
但是,如果您需要将特定对象转换回 Dogs,那么您的设计中就有一个根本问题。您应该通过虚拟方法访问属性。
barnYard[1]->DoNoiseMake();
【讨论】:
所以像 HerdAllTheCats(barnYard) 这样的函数没有意义,不应该做吗? ShaveTheShaveable(barnYard) 是不可能的吗? (显然 C++ 会很难做到这一点,但它是 OOP 的常见用法) @CodeAbominator 如果所有动物都是Shaveable
,那么ShaveTheShaveable()
函数似乎很合理。但是,如果只有Cats
是可剃的,那么不,这听起来不合理,不是面向对象的。但这是抽象讨论的方式。您应该提出问题并获得反馈。
我要指出的是,如果没有定义这些类的主体,上面的例子有点危险,继承本身并不能保证你的类将是多态的。并且 dynamic_cast 仅适用于多态类,这意味着 Animal 必须包含至少一个虚拟成员函数(至少是一个虚拟析构函数)。否则,您将从编译器收到以下错误:“运行时 dynamic_cast 的操作数必须具有多态类类型”。另见此处:***.com/questions/23089511/…
@PeterBulyaki:上面写着/* Some virtual members */
不知道能不能更清楚。
是的。我没有注意到评论,我只是在阅读代码。那就无视我的话。【参考方案2】:
dynamic_cast 应该是您正在寻找的。p>
编辑:
DerivedType m_derivedType = m_baseType; // gives same error
上面似乎试图调用赋值运算符,它可能没有在类型 DerivedType 上定义并接受 BaseType 类型。
DerivedType * m_derivedType = (DerivedType*) & m_baseType; // gives same error
你在正确的道路上,但使用 dynamic_cast 将尝试安全地转换为提供的类型,如果失败,将返回 NULL。
在这里继续记忆,试试这个(但请注意,当您从基类型转换为派生类型时,转换将返回 NULL):
DerivedType * m_derivedType = dynamic_cast<DerivedType*>(&m_baseType);
如果 m_baseType 是一个指针并且实际上指向一个 DerivedType 类型,那么 dynamic_cast 应该可以工作。
希望这会有所帮助!
【讨论】:
这让人感觉每种类型都缺少默认构造函数。这可能是在尝试投射并接收到指出的错误时出现的问题。 第一行是 not 试图调用赋值运算符。它试图将 m_baseType 转换为 DerivedType 类型,然后使用复制构造函数进行复制。 第一行没有复制构造函数调用。如果有的话,将调用 DerivedType 的默认构造函数,然后尝试调用赋值运算符(如果存在以 BaseType 作为参数)。【参考方案3】:您不能将基础对象强制转换为派生类型 - 它不是那种类型。
如果您有一个指向派生对象的基类型指针,那么您可以使用 dynamic_cast 来转换该指针。例如:
DerivedType D;
BaseType B;
BaseType *B_ptr=&B
BaseType *D_ptr=&D;// get a base pointer to derived type
DerivedType *derived_ptr1=dynamic_cast<DerivedType*>(D_ptr);// works fine
DerivedType *derived_ptr2=dynamic_cast<DerivedType*>(B_ptr);// returns NULL
【讨论】:
【参考方案4】:首先-向下转换的先决条件是您要转换的对象是您要转换的类型。使用 dynamic_cast 进行强制转换将在运行时检查此条件(前提是强制转换的对象具有一些虚函数)并在失败时抛出 bad_cast
或返回 NULL
指针。如果此先决条件不成立,编译时强制转换不会检查任何内容,只会导致未定义的行为。
现在分析您的代码:
DerivedType m_derivedType = m_baseType;
这里没有强制转换。您正在创建一个 DerivedType
类型的新对象,并尝试使用 m_baseType 变量的值对其进行初始化。
下一行也好不到哪里去:
DerivedType m_derivedType = (DerivedType)m_baseType;
在这里,您正在创建一个使用 m_baseType
值初始化的 DerivedType
临时类型。
最后一行
DerivedType * m_derivedType = (DerivedType*) & m_baseType;
如果BaseType
是DerivedType
的直接或间接公共基类,则应该编译。无论如何,它有两个缺陷:
-
您使用了已弃用的 C 样式转换。此类演员的正确方法是
static_cast<DerivedType *>(&m_baseType)
转换对象的实际类型不是 DerivedType(因为它被定义为 BaseType m_baseType;
,因此任何使用 m_derivedType
指针都会导致未定义的行为。
【讨论】:
以上是关于C++ 强制转换为派生类的主要内容,如果未能解决你的问题,请参考以下文章
C# 自定义 DynamicObject 强制转换为派生对象
为啥可以从指向实例化基类对象的强制转换指针调用非静态派生类方法?
c++强制类型转换:dynamic_castconst_cast static_castreinterpret_cast