Does deleting void pointer guarantee to delete right size?
Possible Duplicate:
Is it safe to delete a void pointer?
假设我有一个new分配给一个名为MyClass的类,分配简单如下:
1
| MyClass *myClassPtr = new MyClass(); |
我在这里简单地说
1
| myListOfPointers.add(static_cast<void*>(myClassPtr)); // this has to be void* |
后来我释放了记忆,所以我不再做:
我使用:
1
| delete MyListOfPointer.get(0) |
(假设myClassptr引用为零索引。)另外,请注意,它必须是void*,因为此列表可以存储不同类型的指针,所以我不知道要删除的指针类型:
所以我不能做这样的事:
1
| delete static_cast<MyClass*>(MyListOfPointer.get(0)) |
这样可以释放正确的内存大小吗?(sizeof(MyClass)号)?
注:我找不到任何指向智能指针的答案。
- 您还可能遇到未调用析构函数的潜在问题…希望你知道…
- 是的,我当然知道这一点,但我主要关心的是这里的内存释放大小。
- 这类似于stackoverflow.com/questions/941832/&hellip;(答案是删除空指针是不安全的)
通过void*删除会导致未定义的行为,因此您没有任何保证。
5.3.5 Delete [expr.delete]
1 The delete-expression operator destroys a most derived object (1.8) or array created by a new-expression.
[...]
The operand shall have a pointer to object type, or a class type having a single non-explicit conversion function (12.3.2) to a pointer to object type. The result has type void.78
78) This implies that an object cannot be deleted using a pointer of type void* because void is not an object type.
强调我的。
所以,即使你说不说,答案是创建某种形式的智能指针。它需要使用类型擦除在外部隐藏该类型(允许异类列表),但在内部跟踪它所提供的类型以及如何删除它。类似于boost::any的东西。
- 除了很多不确定因素=)
- 您认为存储类型信息以便稍后检索的任何方式。其结构将类似于:myclassptr-myclass mytext-string….等
- 这不是未定义的行为,它是不正确的。void*不是指向对象类型的指针,也不是带有转换运算符的类类型,例如条件失败。
- @Xeo:我认为其他地方的共识是,破坏"应"是不明确的行为,如果文本包含"应"是不正确的,否则程序是不正确的。"不管怎样,我不反对这种解释(这是我所期望的)。
- @mkhan3189:视情况而定。正如我在回答中所补充的,boost::any朝着正确的方向前进,以实现纯类型和基本类型的擦除。否则,听起来你的设计需要改变。您的所有对象都可以从具有虚拟析构函数的基类派生吗?然后将std::unique_ptr存储到派生类的实例中。如果不是,类型擦除实际上是抽象的一个级别。
- 我从来没有意识到delete是一种表达,我只在声明中看到过它本身。所以你可以做的不是你想做的,当然,但仍然是。
- @gmannick,是的,这就是我要做的,使用带有虚拟dest'r的公共基类:)谢谢。
void指针没有类型信息。如果MyClass有析构函数,则不会调用它。编译器需要知道它正在删除什么,以便生成适当的代码。如果列表中的所有指针都是相同的类型,那么应该将该类型存储在列表中,而不是像void那样。如果指针是不同的类型,但派生自公共的基类型,则为该基类型提供一个虚拟构造函数,并存储该类型的指针。
- 我以前也想过这个(有一个共同的基础类),但我认为我的设计有问题,我相信有这样的设计是一个伟大的想法。这篇文章很有帮助,所以+1:)
- 你是怎么想的:如果我有一个公共的基类(包括虚拟的cont'r和dest'r),并通过类型转换删除这个基类static_cast(myClassPointer),这会删除正确的大小吗?(显然,dest'r问题将得到解决,因为它有虚拟析构函数),但我关心的是内存大小。
- 当然你的意思是"虚拟析构函数"。没有"虚拟构造函数"这样的东西。
- @mkhan3189:如果您有一个带有虚拟析构函数的公共基类,那么delete将可以正常工作。这就是拥有一个虚拟析构函数的全部意义。(此外,static_cast是不必要的。派生类型的指针可以隐式转换为指向基类的指针。)
不需要使用智能指针,它只是智能指针。
也就是说,还有许多其他的可能性;唯一要做的就是沿着实际对象存储类型信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| class Holder {
public:
template <typename T>
explicit Holder(T const volatile* t):
_data(static_cast<void const volatile*>(t)),
_delete(&Delete<T>)
{}
void apply() { _deleter(_data); }
private:
typedef void (*Deleter)(void const volatile*);
template <typename T>
static void Delete(void const volatile* p) {
delete static_cast<T const volatile*>(p);
}
void const volatile* _data;
Deleter _deleter;
}; |
现在:
1 2 3 4 5
| std::list<Holder> owningList;
owningList.push_back(Holder(somePointer));
for (Holder& h: owningList) { h.apply(); } |
- @马修,哦,不好意思,兄弟不是故意粗鲁,但你的例子一点都没有帮助,因为我清楚地说我的指针是不同类型的。我收回我的话,但并不想无礼。振作起来,兄弟,
- @MKHAN3189:很抱歉这么急躁。如果仔细观察,您会注意到类的构造函数是模板化的。因此,您可以将它与不同的类型一起使用,只要您一直保留类型信息,直到构建Holder类为止。用在它们的析构函数中打印的对象测试它,你会惊讶的。
- @马修兄弟,我不需要测试它来让人惊讶,我已经惊讶地看到它了。我不是说这个类是一个模板。我说的是对象std::list正在存储的类型。它不应该仅仅局限于Holder类,它应该是一个generic pointer holder类,它要么是无效的,要么是许多派生类的公共基类。好兄弟,你的榜样不错,但肯定不是最聪明的。我仍然想知道你为什么把事情搞得这么复杂,但那是你的选择。非常感谢你的回答
- @MKHAN3189:这实际上就是我在谈论类型擦除时所说的……仅仅因为对你来说有些事情似乎很复杂并不意味着它过于复杂或不是最聪明的。这是一个相当标准的方法(尽管我个人会在这里或那里做一些调整)。
- @ GManNickG。谢谢兄弟,你让我意识到有时候接受别人的帮助是件好事。
- @ MatthieuM。为你的回答干杯,兄弟。我通过进一步研究类型删除和阅读cplusplus.com/articles/oz18t05o得到了你的观点。
- @mkhan3189:很高兴看到你终于明白了这一点:)它看起来有点像魔术,但是当你意识到虚拟方法只不过是动态调用的函数指针时,你也意识到你可以完美地获得这种"动态"行为而不需要继承。将模板放入组合中,既可以自动进行类型推断,也可以自动创建函数,这样您就有了一个神奇的组合:)
这个问题的正确答案当然是"不"
编辑:被要求提供更多信息,即使我已经在对问题的评论中做过,删除一个void*是未定义的,这个问题是询问这个问题的另一种方式:删除一个void指针是否安全?-有关详细信息,请参阅其中的答案。