简体   繁体   中英

How can I get the type of the variable a pointer points to?

I'm working on some 3D rending using OpenGL and I have made a base class object , which should be used for any type of object that can be rendered. I'm storing all my objects in a vector of Object Pointers.

Because different objects may require different treatment by some functions, it would be handy to know, if a pointer points to a for example cube or a sphere.

So is there a way in C++ to get the type of a variable when a pointer to this variable is known?

The best solution to your use case seems to be the visitor pattern.

Visitor Pattern Explanation

https://en.wikipedia.org/wiki/Visitor_pattern

The pattern allows you to implement type-specific behavior without changing the implementation of these types (except extending it once of course).

Long story short: No.

If you own a typed pointer (eg char * ) - well, that might be a char what the pointer points, but we will never know the truth. If you own a void * pointer, then good luck, you aren't going to find anything interesting.

The types don't exist at runtime. Size is preserved (int = 4 bytes, double = 8 bytes), but no type info is usually guaranteed to be stored.

因此,除了使用虚函数之外,似乎没有真正的解决方案。

Option 1, dynamic cast.

class Object {};
class Box : public Object {};
class Sphere : public Object {};

void f(Object* obj)
{
  if(Sphere* sphere = dynamic_cast<Sphere*>(obj))
  {
    // do something with sphere
  }
  if(Box* box = dynamic_cast<Box*>(obj))
  {
    // do something with box
  }
}

Option 2, roll your own form of RTTI:

#include <iostream>
#include <vector>
#include <cstdint>

#define DECLARE_TYPE(TypeId, ObjectType, Base) \
  bool is(uint32_t typeId) const override { \
    if(typeId == (TypeId)) return true; \
    return Base::is(typeId); \
  } \
  constexpr static uint32_t kTypeId = (TypeId); \
  const char* typeName() const override { return #ObjectType; } \
  uint32_t typeId() const override { return kTypeId; }

class Object
{
public:

  constexpr static uint32_t kTypeId = 0;

  template<typename T>
  T* as() const 
  {
    return is(T::kTypeId) ? (T*)this : nullptr;
  }

  virtual bool is(uint32_t typeId) const
  {
    return typeId == kTypeId;
  }

  virtual const char* typeName() const
  { 
    return "Object";
  }

  virtual uint32_t typeId() const
  { 
    return kTypeId;
  }
};

class Box : public Object
{
public: 
  DECLARE_TYPE(1, Box, Object);
};

class Sphere : public Object
{
public: 
  DECLARE_TYPE(2, Sphere, Object);
};

int main()
{
    std::vector<Object*> objs;
    objs.emplace_back(new Box);
    objs.emplace_back(new Sphere);
    objs.emplace_back(new Object);

    for(auto obj : objs)
    {
        std::cout << obj->typeName() << std::endl;
        std::cout << obj->typeId() << std::endl;

        Box* b = obj->as<Box>();
        std::cout << "is box? " << (b ? "Yes" : "No") << std::endl;

        Sphere* s = obj->as<Sphere>();
        std::cout << "is sphere? " << (s ? "Yes" : "No") << std::endl << std::endl;
    }

    for(auto obj : objs)
      delete obj;
    return 0;
}

My advice however, would be to rethink your design if possible, so that you start thinking about collections of objects, rather than single objects

class ObjectCollection 
{
  virtual void drawAll();
  virtual void updateAll();
};
class BoxCollection : public ObjectCollection {};
class SphereCollection : public ObjectCollection {};

std::vector<ObjectCollection*> thingsInMyGame;

When you are doing things in OpenGL, you generally want to minimise the amount of state changes needed during your render loop, and if possible, leverage hardware instancing. This is significantly easier if you have already grouped your game objects by type. Retroactively trying to optimise a std::vector<Object*> for OpenGL rendering won't be very successful.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM