【问题标题】:Storing different object as void* and casting from and to void*将不同的对象存储为 void* 并从 void* 转换为
【发布时间】:2014-11-06 08:09:51
【问题描述】:

我正在尝试使用 void* 指针将不同的对象存储在一个全局表中。问题是如何取回 void* 对象。如果我有一个公共基类,比如 Object ,我总是可以将 void* 指针存储为 Object,然后再转换回 Object*。从 Object* 我可以进一步转换为特定类型。

class Object{};
class A : public Object{ virtual ~A()}
class B : public A { }
class C : public Object{ virtual ~C()} 
class D: public C{};

A* aObj = new B;
void* v = static_cast<Object*>(aObj);      

// scenario 1, try to get as A*
Object* obj = static_cast<Object*> (v);
A* vaobj = dynamic_cast<A*> ( obj); // ok

// scenario 2, try to get it as C*
Object* obj = static_cast<Object*> (v);
C* caobj = dynamic_cast<C*> ( obj); // ok, caObj will be null so I can throw exception if this happens

我知道当我有一个公共基类时的解决方案是安全的,但问题是我不能有公共基类,因为我不能控制所有不同的类型,也不能从 Object 派生它们。我没有通用基类时的代码:

class A{ virtual ~A() }; class B : public A{};
class C{ virtual ~C() }; class D : public C{};

A* aObj = new B;
void* v = dynamic_cast<void*>(aObj);// to actually store the address of B

// scenario 1, try to retrieve object as A*
A* aObj2 = static_cast<A*>(v);
A* aa = dynamic_cast<A*> (aObj2); // aa should be non null

// scenario 2, try to retrieve object as C*
C* cObj = static_cast<C*>(v);
C* cc = dynamic_cast<C*>(cObj); // cc should be null

问题:

场景 1: 我将 void* 指针与 B 的地址一起存储(dynamic_cast 到 void* 确实如此)。然后我做 我知道带有 A* 的 static_cast 是不安全的,因为我最初将 void* ptr 中的对象存储为 B*,并且我知道我们必须在 void* 中存储和检索相同类型的指针。但是,然后我会进一步使用 dynamic_cast 进行强制转换,以验证该对象实际上是正确的。所以这应该可以工作,并且 dynamic_cast 应该给我正确的对象?

场景 2: 同样,但现在我先将 static_casting 转换为 C*,然后再进行 dynamic_casting,这次我应该得到 NULL,因为存储的对象实际上是 B。

【问题讨论】:

  • 我会说“这闻起来很糟糕”。换句话说,您可能应该考虑其他一些解决方案。为什么需要将不相关的对象存储在表中?也许您应该存储一个包装器对象?
  • 不,我绝对应该存储对象指针。所以我需要一些解决方案,我想这是常见问题,但很难解决
  • 但是你最终不得不做一堆if ptr != NULL ...,这不利于代码的可读性,也不利于性能。也许您可以解释您的实际问题是什么,而不是“您如何尝试解决它”,因为我很确定这是错误的解决方案。 (哦,dynamic_cast 通常也很慢!)
  • 不,没关系,如果 ptr!=NULL,我不会有一堆检查,那将在一个地方,即从 void* 获取对象的请求应该来自一个模板函数,该函数将指定输入如何获得 void* 。在该函数中,如果 ptr == NULL,我将抛出异常。
  • 几乎可以肯定有比存储void* 更好的方法。如果您试图拥有基于功能而不是继承的多态容器,请查看基于概念的多态性。查看here 进行简要介绍,查看here 进行更深入的讨论。

标签: c++ void-pointers


【解决方案1】:

在 C++ 中几乎不需要存储 void*,而在 C 中却很常见。 C++ 有更好、更类型安全的替代方案。例如,也许您可​​以使用 Boost.Any?

【讨论】:

  • no 不能使用 boost::any。 boost::any 还需要检索存储的确切类型。因此,如果存储了派生的 ptr,我无法将其作为基类 ptr 检索。
【解决方案2】:

正如许多评论者已经说过的,在 C++ 中存储 void* 确实没有必要。如果您无法控制要存储的类型,请为它们创建一个容器。这样您就可以控制它的定义和行为(如果您愿意,还可以从 Object 派生它)。

非常粗略的工作示例:

#include <iostream>
#include <string>

using namespace std;

class Object{
    public:
    virtual ~Object(){};
};

template<typename T>
class Container : public Object{
    T* v;
    public:
    Container(T* b):v(b){}

    T* value(){
        return v;
    }

    void deleteContained(){
        delete v;
    }
};

class foo{
public:
    virtual string say(){
        return "foo";
    }
};
class bar: public foo{
public:
    string say(){
        return "bar";
    }
};

int main(){
    Object* o;
    o = new Container<foo>(new bar);

    Container<foo>* fooCon = dynamic_cast<Container<foo>*>(o);
    foo* fooObj = fooCon->value();
    // Since you now have the object in a foo*, you can dynCast it to a bar* if you like.
    bar* barObj = dynamic_cast<bar*>(fooObj);

    // Cast to the wrong container type is NULL.
    Container<int>* nullInt = dynamic_cast<Container<int>*>(o);
    if(nullInt)
        cout << "Cast Successful." << endl;
    else
        cout << "Cast failed." << endl;

    // Both will print "bar", since "o" contains a bar object.
    cout << fooObj->say() << endl;
    cout << barObj->say() << endl;

    ((Container<foo>*)o)->deleteContained();
    delete o;
}

输出:

Cast failed.
bar
bar

【讨论】:

  • 关闭,但不考虑继承。也就是说,如果 B 是从 A 继承的,我想从 B* 转换为 A*。 Object* obj = new Container(B());然后再做 Container aObj = dynamic_cast>(obj);它会失败
  • 我明白了。我会尝试提出一个更合适的解决方案。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-10-24
  • 1970-01-01
  • 1970-01-01
  • 2015-12-03
  • 2018-02-24
  • 1970-01-01
相关资源
最近更新 更多