【问题标题】:Finding the type of an object in C++在 C++ 中查找对象的类型
【发布时间】:2010-09-25 23:53:37
【问题描述】:

我有一个类 A 和另一个继承自它的类 B。我正在重写一个接受 A 类型对象作为参数的函数,因此我必须接受 A。但是,我稍后调用的函数仅B 有,所以如果传递的对象不是 B 类型,我想返回 false 并且不继续。

找出传递给我的函数的对象是哪种类型的最佳方法是什么?

【问题讨论】:

  • 这些似乎都没有解决根本问题...code void *p1 = new int(); void *p2 = new double(); void f(void *p) {....code goes here } f(p1) should print int... f(p2) should print double...

标签: c++ types


【解决方案1】:

这称为RTTI,但您几乎肯定想在这里重新考虑您的设计,因为找到类型并根据它做一些特殊的事情会使您的代码更加脆弱。

【讨论】:

  • 是的。不幸的是,我正在处理一个现有项目,所以我不能真正改变设计或 A 类中的任何内容。
【解决方案2】:

您正在寻找dynamic_cast<B*>(pointer)

【讨论】:

    【解决方案3】:

    你的描述有点混乱。

    一般来说,虽然一些 C++ 实现有它的机制,但你不应该询问类型。相反,您应该对指向 A 的指针执行 dynamic_cast。这将在运行时检查指向 A 的指针的实际内容。如果您有 B,您将获得指向 B 的指针。否则,您将获得异常或 null。

    【讨论】:

    • 应该注意,只有如果你执行一个失败的引用转换,你会得到一个异常。即dynamic_cast(t)。失败的指针转换返回 NULL。即 dynamic_cast(t)
    • 是的,我应该更好地澄清这一点。谢谢。我希望 C 类型中有一个单词 describe 是按引用而不是按值。
    【解决方案4】:

    可能会在您的对象中嵌入一个 ID“标签”,并使用它来区分 A 类对象和 B 类对象。

    然而,这表明设计存在缺陷。理想情况下,B 中 A 没有的那些方法应该是 A 的一部分但留空,然后 B 会覆盖它们。这消除了特定于类的代码,更符合 OOP 的精神。

    【讨论】:

      【解决方案5】:

      dynamic_cast 应该可以解决问题

      TYPE& dynamic_cast<TYPE&> (object);
      TYPE* dynamic_cast<TYPE*> (object);
      

      dynamic_cast 关键字将数据从一种指针或引用类型转换为另一种,执行运行时检查以确保转换的有效性。

      如果您尝试将指针转换为不是实际对象类型的类型,则转换结果将为 NULL。如果您尝试转换为对不是实际对象类型的类型的引用,则转换将引发 bad_cast 异常。

      确保基类中至少有一个虚函数可以使 dynamic_cast 工作。

      维基百科话题Run-time type information

      RTTI 仅适用于多态类,这意味着 他们至少有一种虚拟方法。在实践中,这不是 限制,因为基类必须有一个虚拟析构函数 允许派生类的对象执行适当的清理,如果它们是 从基指针中删除。

      【讨论】:

      • Base 类中必须有一个虚函数才能使 dynamic_cast 工作是什么意思。在我看来这很重要,我只是猜测。
      • OK 找到它:运行时类型信息 (RTTI) 仅适用于多态类,这意味着它们至少有一个虚拟方法。 dynamic_cast 和 typeid 需要 RTTI。
      • dynamic_cast 如果它不可转换,它不会抛出吗?有没有办法在不产生投掷的情况下做到这一点?
      • A* aptr = dynamic_cast&lt;A*&gt;(ptr); //不应该是这样的
      • 这是否适用于已转换为 uint8_t* 的 POD?也就是说,我可以检查uint32_t* x = dynamic_cast&lt;uint32_t*&gt;(p),其中puint8_t*? (我正在尝试寻找双关语违规的测试)。
      【解决方案6】:

      正如其他人指出的那样,您可以使用 dynamic_cast。但通常使用 dynamic_cast 来找出您正在处理的派生类的类型表明设计不好。如果您要覆盖一个将 A 的指针作为参数的函数,那么它应该能够使用 A 类本身的方法/数据,并且不应该依赖于 B 类的数据。确定您正在编写的方法仅适用于 B 类,那么您应该在 B 类中编写一个新方法。

      【讨论】:

        【解决方案7】:

        使用重载函数。不需要 dynamic_cast 甚至 RTTI 支持:

        class A {};
        class B : public A {};
        
        class Foo {
        public:
            void Bar(A& a) {
                // do something
            }
            void Bar(B& b) {
                Bar(static_cast<A&>(b));
                // do B specific stuff
            }
        };
        

        【讨论】:

        • 正确来自原始问题:“我稍后调用只有 B 具有的函数” - 在这种情况下重载将如何工作?
        • 当你用 A 调用 Bar 时,不会发生 B 的事情。当您使用 B 调用 Bar 时,可以调用仅存在于 B 上的方法。你读过最初的问题吗? Bar 是他的“我正在重写一个接受 A 类型对象作为参数的函数”
        • 这不适用于动态多态性,我怀疑提问者正在使用它。 C++不能根据参数的运行时类来选择重载,只能根据编译时类型来选择。
        【解决方案8】:

        动态转换最适合您描述问题, 但我只想补充一点,您可以通过以下方式找到类类型:

        #include <typeinfo>
        
        ...
        string s = typeid(YourClass).name()
        

        【讨论】:

        • 如果您真的不知道您的对象是什么,那就太好了。接受的答案假设您这样做。
        • @xus 是的。它是标准头文件的一部分
        • don't see 怎么样。类型 ID 名称不需要有用,而是由实现定义的。
        • 这里最有趣的是:同一个类的实例名不必相同。但是 typeid 本身必须比较相同类的实例是否相等,请参阅stackoverflow.com/questions/1986418/typeid-versus-typeof-in-c
        • 注意 gcc 返回 magled 名称,例如11MyClass。要解开,您可以使用 cxxabi.h 中的 ABI 扩展库。这会给你abi::__cxa_demangle,这会给你真实姓名
        【解决方案9】:

        因为你的类不是多态的。试试:

        struct BaseClas { int base; virtual ~BaseClas(){} };
        class Derived1 : public BaseClas { int derived1; };
        

        现在BaseClas 是多态的。我将 class 更改为 struct,因为 struct 的成员默认情况下是公共的。

        【讨论】:

          【解决方案10】:

          为了完整起见,我将构建 Robocide 并指出 typeid 可以单独使用而不使用 name():

          #include <typeinfo>
          #include <iostream>
          
          using namespace std;
          
          class A {
          public:
              virtual ~A() = default; // We're not polymorphic unless we
                                      // have a virtual function.
          };
          class B : public A { } ;
          class C : public A { } ;
          
          int
          main(int argc, char* argv[])
          {
              B b;
              A& a = b;
          
              cout << "a is B: " << boolalpha << (typeid(a) == typeid(B)) << endl;
              cout << "a is C: " << boolalpha << (typeid(a) == typeid(C)) << endl;
              cout << "b is B: " << boolalpha << (typeid(b) == typeid(B)) << endl;
              cout << "b is A: " << boolalpha << (typeid(b) == typeid(A)) << endl;
              cout << "b is C: " << boolalpha << (typeid(b) == typeid(C)) << endl;
          }
          

          输出:

          a is B: true
          a is C: false
          b is B: true
          b is A: false
          b is C: false
          

          【讨论】:

            【解决方案11】:

            如果你可以访问boost库,也许type_id_with_cvr()函数是你需要的,可以provide data type without removing const, volatile, & and && modifiers。下面是 C++11 中的一个简单示例:

            #include <iostream>
            #include <boost/type_index.hpp>
            
            int a;
            int& ff() 
            {
                return a;
            }
            
            int main() {
                ff() = 10;
                using boost::typeindex::type_id_with_cvr;
                std::cout << type_id_with_cvr<int&>().pretty_name() << std::endl;
                std::cout << type_id_with_cvr<decltype(ff())>().pretty_name() << std::endl;
                std::cout << typeid(ff()).name() << std::endl;
            }
            

            希望这是有用的。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2016-02-19
              • 1970-01-01
              • 2011-10-16
              • 2013-06-16
              • 2012-10-23
              • 2016-04-14
              • 2013-12-08
              相关资源
              最近更新 更多