【问题标题】:C++ void* to int* - get int pointed by int*C++ void* 到 int* - 获取 int* 指向的 int
【发布时间】:2012-07-16 12:55:57
【问题描述】:

这个问题之前可能已经回答过了,但是经过两天的搜索,我找不到解决方案。

我做了一个堆栈类来存储__data:

class __data
{
private:
    void* __p;
    __datatype __type;

public:
    __data(void);
    __data(int i);
    __data(double d);
    __data(char* s);
    __data(void (*f)(Stack));

    bool IsFunction(void);
    bool IsInteger(void);
    bool IsNumber(void);
    bool IsString(void);

    void* Get(void);
};

我创建了这个类,因为堆栈将能够存储字符串、函数(指向函数的指针)、双精度数和整数。

问题是当我将一个整数或双精度压入堆栈然后弹出它并获取指向数据的指针(void* Get(void))我通过打印出它的值来调试它,因此我基本上有这个:

void print(Stack s)
{
    __data d = s.Pop();

    if (d.IsNumber()) // BUG: never the number I pass to __data
        std::cout << "IsNumber - " << *((double*)d.Get()) << std::endl;
    else if (d.IsInteger()) // BUG: it always prints out 1
        std::cout << "IsInteger - " << *((int*)d.Get()) << std::endl; // 
    else if (d.IsString())
        std::cout << "IsString - " << (char*)d.Get() << std::endl;
    else if (d.IsFunction())
    {
        std::cout << "IsFunction - " << d.Get() << std::endl;
        ((void (*)(Stack))d.Get())(s); // calls the function
    }
}

不知道哪里出了问题,可能是我分配__p的方式(Get()返回的指针),这些是__data整数和双精度的构造函数:

__data::__data(int i)
{
    __type = _dt_integer;
    __p = &i;
}
__data::__data(double d)
{
    __type = _dt_number;
    __p = &d;
}

所以基本上我的问题是,当我尝试获取返回的void* 指向的整数或双精度时,它要么给我一个值1(整数)或2.13171e-314(双精度)。

感谢您抽出宝贵时间,如果已经有答案,我们深表歉意。

编辑:

我将重写 Stack 类并改用联合。这似乎是实现我的目标的最佳方法。

【问题讨论】:

  • 为什么要强调类名?
  • 顺便说一句,你不应该使用那些名字(以 2 _s 开头)stackoverflow.com/questions/228783/…
  • 为什么要使用指针?只需使用union(不要忘记复制任何字符串,并删除析构函数中的重复项)。

标签: c++ pointers void-pointers


【解决方案1】:

你的问题是你保存了一个临时对象的地址。例如:在本例中,您保存临时double d 的地址。

__data::__data(double d)
{
    __type = _dt_number;
    __p = &d;
}

对象只存在于当前作用域(函数)中,之后地址就没有用了,因为它很可能被堆栈中的其他东西覆盖。

我建议的解决方案是制作一个动态分配的副本:

__data::__data(double d)
{
    __type = _dt_number;
    __p = new double(d);
}

这将创建一个对象,该对象将一直存在,直到您使用 delete 手动释放指针。你应该在__data的析构函数中做什么:

__data::~__data()
{
    delete __p;
}

更新:我强烈建议您不要使用 char * 作为字符串类型,而是使用 C++ 等效的 std::string。您还可以查看库boost::any,它基本上与您想要实现的功能相同,但使用模板代替。

【讨论】:

  • 不要忘记在析构函数和弹出时添加删除。
  • 谢谢!我认为这将是一个简单的解决方案 xD
  • 问题是每当我实现析构函数时,我的应用程序就会崩溃。
  • 我不认为构造函数是问题所在。为什么您的应用程序崩溃?您是否尝试过使用调试器?
  • 我正在使用 VS2010。每当我尝试定义析构函数时,应用程序都会崩溃并显示“调试断言失败!”在文件dbgdel.cpp 第 52 行。
【解决方案2】:
__data::__data(int i)
{
    __type = _dt_integer;
    __p = &i; // this only stores the address of i on the stack
}

你需要的是

__data::__data(int* i) // have the caller pass the address of their i
{
    __type = _dt_integer;
    __p = i; // this stores what you need
}

【讨论】:

  • 输入整数仍然有可能是本地对象,你会弄得一团糟。
  • 它将整数值存储在一个空指针中。不完全正确,我会说。
  • @BartFriederichs:不,一个指向整数的指针。它在语法上是正确的,但容易出错。
  • Constantinius 是对的,因为您必须注意这一点,否则您将按照他的回答创建自己的输入值副本。
  • @Constantinius,是的,我明白了,它与 C++ 的引用调用混淆了。
【解决方案3】:
__data::__data(int i) 
{
    __type = _dt_integer;
    __p = &i; 
}
__data::__data(double d) 
{
    __type = _dt_number;
    __p = &d; 
}

因为你得到一个指向局部变量的指针

【讨论】:

    【解决方案4】:
    __data::__data(int i)
    {
        __type = _dt_integer;
        __p = &i;
    }
    

    你不能这样做!函数返回后变量i 不存在,因此任何指向它的指针都会包含垃圾。您必须将i 存储在__data 类中,而不是其地址。您有(至少)两种方法:使用多态性(为每种数据类型定义 __data 的子类),或在 __data 内声明一个联合,其中包含您支持的每种数据类型的成员。

    【讨论】:

      【解决方案5】:

      变量 i 和 d 仅在函数 __data(int i)、__data(double d) 的生命周期内有效。我的意思是这些变量被创建到当前堆栈中并在退出函数时被删除,因此您存储一个指向变量的指针,该指针在程序运行的整个过程中都将无效。调用 Get 函数时,您会访问一个指向“死”变量的指针。

      您应该将这些成员添加到您的班级。

      class __data
      {
      private:
        int iInt;
        double dDouble;
      }
      

      并修改您的函数...以存储值而不是地址...

      __data::__data(int i)
      {
          __type = _dt_integer;
          iInt = i;
      }
      __data::__data(double d)
      {
          __type = _dt_number;
          dDouble = d;
      }
      

      别忘了修改Get函数。

      void* __data::Get()
      {
        switch( __type )
        {
          case _dt_integer:
            return (void*)iInt;
            break;
          case _dt_number:
            return (void*)dDouble;
            break;
        }
      
        return (void*)0;
      }
      

      那么你可以像下面这样使用它。

      cout << "Int value " << (int)d.Get();
      cout << "double value " << (double)d.Get();
      

      这是一种做你想做的事但唯一的方法!您可以使用联合成员来存储值,以节省内存使用量。

      【讨论】:

      • 这可能会浪费大量内存,因为许多成员变量可能从未使用过......另外,switch 的用途是什么?你返回的只是一个void*,所以为什么还要提前投射它呢?
      • 确保您可以使用联合来存储值...强制转换防止编译器警告!!!
      • 我明白了,那么你应该在你的答案中写下这个。你不应该强制转换,以防止编译器警告,因为它们告诉你你的代码有问题。而显式转换只是忽略了这个错误。在您的情况下,您将 intdouble 显式转换为指针值,这简直是垃圾。编译器试图警告你,但被沉默了。
      • 我同意,但有时做这种把戏真的很实用……谁从来没有做过?
      猜你喜欢
      • 2016-09-18
      • 2020-02-05
      • 2016-01-05
      • 2012-04-04
      • 2013-12-25
      • 2014-01-28
      • 1970-01-01
      • 1970-01-01
      • 2011-06-28
      相关资源
      最近更新 更多