【问题标题】:How to pass a object* as a void*&?如何将对象*作为 void*& 传递?
【发布时间】:2012-06-09 20:42:43
【问题描述】:

我有以下代码:

void testCopy(){
   Balloon* b1=new Balloon(1,5,6,3);
   Balloon* b2=new Ballon(2,4,4,2);
   cpyBallon(b1,b2);
   assert(b1->getCenterx()==5);
   cout<<" cpyBalloon() OK!\n";
}

cpyBalloon() 的代码是

void cpyBalloon(TCE& dest,TCE src){
    Balloon* b=(Balloon*) src;
    Balloon* d=new Balloon();
    *d=*b;
    dest=d;
}

如果 TCE 类型为 void*。 问题是我不知道如何将Balloon* 类型的对象b2 作为void*& 传递。我不断收到以下错误:

    ..\src\/Domain/BalloonTest.h:68:18: error: invalid initialization of reference of type 'void*& {aka void*&}' from expression of type 'domain::Balloon*'
    ..\src\/Domain/Balloon.h:102:10: error: in passing argument 1 of 'void domain::delBalloon(void*&)'    

我知道 b2 是我遇到问题的论点,但我不知道如何使它起作用。

【问题讨论】:

  • Holyjesus 可怕的代码。
  • @DeadMG 对吧? daknok 编写更好的代码。 :Đ
  • 您知道您的testCopy 代码会泄漏内存吗?具体来说,它泄漏了 3 个气球对象——两个分配在 testCopy 中,一个分配在 cpyBalloon 中。其中,第一行中分配的泄漏似乎是最严重的泄漏,因为它在 testCopy 函数结束之前已经无法访问。
  • 是的,我知道它仍然有问题,但首先我想解决这个问题。
  • 糟糕的代码?将 Balloon* 分配给 TCE& 有什么问题?您总是可以创建一个转换构造函数或运算符——或者,更好的是,一个 TCE::operator= 重载——来编译并执行……嗯,几乎任何你想要的。而 cpyBallon 是 copyBalloon 的一个很好的缩写,因为它节省了输入的两个字符,并且它们是三个 O 中的两个,这使得它特别容易记住。

标签: c++ void void-pointers


【解决方案1】:

正如您的代码所述,您有一个 Balloon 类。您已经为两个 Balloon 实例动态分配了内存并调用了它们的构造函数,用各自的数据填充它们。然后,您将每个对象的地址存储到适当的原始指针中。

这个类型定义:

typedef void* TCE; // why?

毫无意义。对void* (void*&amp;) 的引用也毫无意义。为什么?因为void* 只包含一个值——字节流的第一个字节(当您将任何内容解释为void* 时,它们的计数会丢失,这是因为计数取决于类型)。所以,它已经是一个指针。您没有传递整个字节流,只是第一个字节的地址。然后你可以根据需要在函数中进一步解释它。

但在这种情况下,没有必要这样解释。一个引用,当从一个非常低的层次分析时,只是一个伪装的指针,使我们能够安全地获得指针的好处(不用考虑太多)。还有更多,但就目前而言,这很重要。

您可以:

  1. 在堆栈上(本地)创建实例并将其作为引用传递。
  2. 在堆上创建实例并获取内存地址,将它们传入并进一步处理。
  3. 或者...您可以采取行动 2 并将它们转换为 void*,然后撤消函数中的转换(无论这样做有什么好处)。

先观察:

    Balloon b1(1,5,6,3);
    Balloon b2(2,4,4,2);

    cpyBalloon(b1, b2);

    void cpyBalloon(Balloon& dest, const Balloon& src) 
    {
        dest = src; // invokes the assignment copy operator (imp. generated)
    }

观察秒:

    Balloon* b1 = new Balloon(1,5,6,3);
    Balloon* b2 = new Balloon(2,4,4,2);

    cpyBalloon(b1, b2);

    void cpyBalloon(Balloon* dest, const Balloon* src) 
    {
        *dest = *src; // invokes the assignment copy operator (imp. generated)
    }

观察第三个(亲爱的主……):

    Balloon* b1 = new Balloon(1,5,6,3);
    Balloon* b2 = new Balloon(2,4,4,2);

    cpyBalloon((void*)b1, (void*)b2);

    void cpyBalloon(void* dest, const Balloon* src) 
    {
        *(Balloon*)dest = *(const Balloon*)src; // oh god...
    }

你也可以 reinterpet_cast 来规避所有编译器对类型检查的帮助,这是非常危险的,如果你必须使用它,你要么非常错误,要么有一个非常讨厌的问题(这可能源于你的方法是 非常错误)。请注意源代码周围的constness,当您想要复制和完好无损的内容时,这是一种很好的做法。

还要注意,在这些情况下会出现很多内存管理,这只是一个简单的情况。

【讨论】:

  • 感谢您对我的帮助很大。我不太了解void*。再次感谢。
【解决方案2】:

您不能将T* 类型的左值(其中T 不是void)作为void *&amp; 类型的参数传递。这是语言不允许的。仅仅因为任何数据指针类型都可以转换为void *,并不意味着任何数据指针类型都可以转换为void *&amp;。这些是完全不同的东西。

如果您真的想这样做,您必须使用reinterpret_cast 强制转换为void *&amp;。但是,您似乎试图执行的操作并没有多大意义。即使您的代码可能强制使用reinterpret_cast“工作”,它仍然是一个hack。

【讨论】:

  • 如果不清楚,“工作”在引号中的原因是因为生成的代码可能看起来可以工作,但不能保证按标准工作。在与您对抗的事情中,主要的是严格的别名规则(这是一种说法“允许优化器破坏您的代码”)。同样具有理论意义的是object* 的表示不需要与void* 的表示相同,因此将object*&amp; 重新解释为void*&amp; 在形式上是不正确的。当 AndreyT 说“这是一个 hack”时,他的意思是“不要这样做”。
猜你喜欢
  • 2012-10-06
  • 2019-10-19
  • 1970-01-01
  • 2015-12-01
  • 1970-01-01
  • 2012-03-30
  • 2018-08-02
  • 1970-01-01
  • 2018-01-10
相关资源
最近更新 更多