【问题标题】:Type checking in C++C++ 中的类型检查
【发布时间】:2011-01-03 23:04:40
【问题描述】:

在 C++ 中,我想知道对象的实际类型是否来自同一个类,而不是同一个类或派生类。这类似于以下 C# 代码:

Class Base
{
}

Class Child:Base
{
}

Base childObject = new Child();

If (childObject.GetType() == typeof(Child))
{
 // do some code
}

谢谢!

【问题讨论】:

  • childObject的类型是什么?在 C++ 中没有通用的方法来做到这一点,因为在运行时,类型的概念不存在。
  • 虽然你什么也没说 childObject.GetType() 是什么,但是这段代码很糟糕,因为它在类型比较上分支。这就是 OOP 应该消除的。
  • @Gene:这个问题只有你来自C#并且知道object.GetType()做什么才能理解。
  • @DeadMG:在任何面向对象的语言中这样做都不是一件好事,包括在 CLR 上运行的语言(如 C#)。
  • @Billy:我并不是说这是个好主意——我也认为这很愚蠢。但是,从根本上说,如果您不了解他要替换的功能,您将无法处理这个问题。

标签: c++ casting typechecking


【解决方案1】:

有两种方法可以做到这一点。首先,您可以使用typeid 运算符,它返回一个type_info 结构,其中包含有关对象类型的信息。例如:

Base* ptr = /* ... */
if (typeid(*ptr) == typeid(DerivedType)) {
    /* ... ptr points to a DerivedType ... */
}

请注意,您必须在此处使用 typeid(*ptr) 而不是 typeid(ptr)。如果你使用typeid(ptr),那么你将得到一个Base*type_info对象,因为无论它指向什么,指针的类型都是Base*

需要注意的重要一点是,这将检查ptr 所指的内容是否完全DerivedType。如果ptr 指向的对象类型是从DerivedType 派生的(可能是EvenMoreDerivedType),则此代码将无法正常工作。

检查您是否指向某种更强大的类型的对象的另一种方法是使用dynamic_cast 运算符。 dynamic_cast 在运行时执行检查类型转换,如果转换成功将产生一个有效指针,否则将产生一个有效指针 nullptr。例如:

Base* ptr = /* ... */;
auto* derived = dynamic_cast<DerivedType*>(ptr);
if (derived) {
    /* ... points to a DerivedType ... */
}

这还有一个额外的好处,即如果ptr 指向类似EvenMoreDerivedType 的东西,则转换仍然会成功,因为EvenMoreDerivedType 继承自DerivedType

最后,你有时会看到这样的代码:

Base* ptr = /* ... */
if (auto* derived = dynamic_cast<DerivedType*>(ptr)) {
     /* ... points to a DerivedType ... */
}

这将derived 指针定位到if 语句的主体,并使用非零值在C++ 中计算为true 的事实。我个人觉得这更容易阅读且不易出错,但请务必选择对您来说最简单的方法。

希望这会有所帮助!

【讨论】:

  • 他想知道对象是否是基类,而不是派生类。
  • 啊...我被他的示例代码弄糊涂了,它正在检查指针是否指向子类型。一个有效的点。
【解决方案2】:

虽然 DeadMG 的答案是正确的(我已经多次使用 typeid),但我想我会把它扔在那里以供后代使用。从面向对象的角度来看,执行此操作的“正确”方法是:

Class Base
{
    virtual void something() {
        // probably a no-op, but maybe some default stuff
    }
}

Class Child : public Base
{
    virtual void something() {
        // do your child-specific code here
    }
}

Base* childObject = new Child();
childObject->something();  // does the right thing

【讨论】:

  • 我支持这个答案。 DeadMG 的回答虽然正确,但可能不是一个好的编程习惯..!
  • 当你想在对象内部做一些事情时,这很有效。另一方面,当您想根据对象的类型处理对象时,它会变得复杂且不方便(所有那些双重调度和访问者)
  • @7vies,是的,但最常见的是,这种虚拟方法方法效果最好,并且最容易维护。有时 dynamic_cast 或 typeid 是正确的方法,但这种情况很少见。
  • 感谢您的回答,但正如我所说,这不是我的代码,我只是快速解决问题而无需更改任何内容。
  • @Homam,您仍然可以通过创建自己的包装类来使用 OO。但此时通常不值得,而 typeid/dynamic_cast 成为更合理的选择。特别是如果您只是想快速解决问题。
【解决方案3】:

你可以使用 typeid()。

if (typeid(childObject) == typeid(ChildType)) {
}

如果返回 true,那么你就知道它是子类。

【讨论】:

  • 您需要确保在编译时也启用了运行时类型识别 (RTTI)。
  • 请注意,这仅在被比较的项目具有 vtbl 时才有效
  • @Billy:确实如此——但谁不给他们的基类提供虚拟析构函数呢?虚函数实际上是继承点。期待它并非没有道理。
  • @Richard Cook:如果您使用的是 C++ 编译器,它将支持 RTTI。关闭 RTTI 会将其置于不符合标准的模式,并且在关于 C++ 的一般问题中,没有真正理由列出不应关闭的 C++ 功能。
  • @7vies:您不会从 STL 容器继承。除非你疯了。或者在语言方面经验丰富,您将已经了解 typeid() 和虚函数限制。 @Charles:我也很少给我的类提供虚拟析构函数——但如果我打算从它们继承,我总是这样做。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-11-02
  • 2011-06-10
  • 2010-09-24
  • 2015-05-18
  • 1970-01-01
  • 1970-01-01
  • 2012-02-21
相关资源
最近更新 更多