假设我有以下代码:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
这样安全吗?或者必须ptr
被强制转换为char*
之前删除?
假设我有以下代码:
void* my_alloc (size_t size)
{
return new char [size];
}
void my_free (void* ptr)
{
delete [] ptr;
}
这样安全吗?或者必须ptr
被强制转换为char*
之前删除?
Answers:
这取决于“安全”。通常会起作用,因为信息与有关分配本身的指针一起存储,因此取消分配器可以将其返回到正确的位置。从这个意义上讲,只要您的分配器使用内部边界标记,它就是“安全的”。(很多)
但是,如其他答案中所述,删除void指针不会调用析构函数,这可能是一个问题。从这个意义上讲,它不是“安全的”。
没有充分的理由按照自己的方式去做自己的事情。如果要编写自己的释放函数,则可以使用函数模板来生成具有正确类型的函数。这样做的一个很好的理由是生成池分配器,它对于特定类型可能非常高效。
如其他答案所述,这是C ++中的未定义行为。通常,最好避免出现不确定的行为,尽管主题本身很复杂并且充满了相互矛盾的观点。
sizeof(T*) == sizeof(U*)
所有的T,U
建议,它应该是可能有1个非模板,void *
基于垃圾收集器的实现。但是,当gc实际上必须删除/释放指针时,就会出现此问题。为了使它起作用,您要么需要lambda函数析构器包装器(urgh),要么需要某种动态的“类型作为数据”类型的东西,它允许在类型和可存储的东西之间来回移动。
C ++标准未定义通过void指针进行删除-请参见5.3.5 / 3节:
在第一个替代方案(删除对象)中,如果操作数的静态类型与动态类型不同,则静态类型应为操作数动态类型的基类,并且静态类型应具有虚拟析构函数或行为未定义。在第二种选择(删除数组)中,如果要删除的对象的动态类型与静态类型不同,则行为是不确定的。
及其脚注:
这意味着不能使用类型为void *的指针删除对象,因为不存在类型为void的对象
。
NULL
应用程序内存管理填充空白指针的有针对性的地址存储器吗?
这不是一个好主意,也不是您在C ++中会做的事情。您无缘无故丢失了类型信息。
当您为非基本类型调用析构函数时,不会在要删除的数组对象上调用析构函数。
您应该改写new / delete。
删除void *可能会偶然地正确释放您的内存,但这是错误的,因为结果是不确定的。
如果由于某种原因我不知道您需要将指针存储在void *中,然后释放它,则应使用malloc和free。
这个问题没有道理。您的困惑可能部分是由于人们经常使用粗俗的语言delete
:
您可以delete
用来销毁动态分配的对象。这样做,您将形成一个指向该对象的指针的删除表达式。您永远不会“删除指针”。您真正要做的是“删除由其地址标识的对象”。
现在我们明白为什么这个问题没有意义了:空指针不是“对象的地址”。这只是一个地址,没有任何语义。它可能来自实际对象的地址,但是该信息丢失了,因为它是以原始指针的类型进行编码的。还原对象指针的唯一方法是将void指针强制转换回对象指针(这要求作者知道指针的含义)。void
本身是不完整的类型,因此永远不是对象的类型,并且空指针永远不能用于标识对象。(对象通过它们的类型和地址共同标识。)
delete
可能是空指针值,指向由先前的new-expression创建的非数组对象的指针或指向a的指针。子对象,代表此类对象的基类。否则,该行为未定义。” 因此,如果编译器在没有诊断的情况下接受了您的代码,那只不过是编译器中的错误……
delete void_pointer
。这是未定义的行为。程序员永远都不应调用未定义的行为,即使响应看起来像程序员想做的一样。
如果您真的必须这样做,为什么不删掉中间人(new
和delete
运算符)operator new
并operator delete
直接调用全局函数呢?(当然,如果您尝试使用new
and delete
运算符,则实际上应该重新实现operator new
and operator delete
。)
void* my_alloc (size_t size)
{
return ::operator new(size);
}
void my_free (void* ptr)
{
::operator delete(ptr);
}
请注意,与不同malloc()
,它operator new
会引发std::bad_alloc
失败(new_handler
如果已注册,则会调用)。
因为char没有特殊的析构函数逻辑。这行不通。
class foo
{
~foo() { printf("huzza"); }
}
main()
{
foo * myFoo = new foo();
delete ((void*)foo);
}
d'ctor不会接到电话。
如果要使用void *,为什么不只使用malloc / free?new / delete不仅仅是内存管理。基本上,new / delete调用构造函数/析构函数,并且还有更多事情要做。如果仅使用内置类型(例如char *)并通过void *删除它们,则可以使用,但仍然不建议这样做。如果要使用void *,最底行是使用malloc / free。否则,您可以使用模板功能以方便使用。
template<typename T>
T* my_alloc (size_t size)
{
return new T [size];
}
template<typename T>
void my_free (T* ptr)
{
delete [] ptr;
}
int main(void)
{
char* pChar = my_alloc<char>(10);
my_free(pChar);
}
很多人已经发表评论说不,删除void指针并不安全。我同意这一点,但是我还想补充一点,如果您正在使用空指针来分配连续数组或类似的东西,则可以这样做,new
以便可以delete
安全地使用(使用ahem ,还有一些额外的工作)。这是通过为存储区域分配一个空指针(称为“竞技场”),然后将指向竞技场的指针提供给new来完成的。请参阅C ++常见问题解答中的此部分。这是在C ++中实现内存池的常用方法。
我在框架中使用void *(又称未知类型)进行代码反射和其他含糊之处,到目前为止,我还没有遇到任何编译器带来的麻烦(内存泄漏,访问冲突等)。仅警告由于操作不规范。
删除未知数(void *)非常有意义。只需确保指针遵循这些准则,否则它可能会失去意义:
1)未知指针一定不能指向具有琐碎解构函数的类型,因此,当将其转换为未知指针时,绝对不应将其删除。仅在将未知指针投射回ORIGINAL类型后才将其删除。
2)实例是否在堆栈绑定或堆绑定内存中被引用为未知指针?如果未知指针引用了堆栈上的实例,则永远不要删除它!
3)您是否100%肯定未知指针是有效的内存区域?不,那么它永远都不要被删除!
总之,使用未知(void *)指针类型可以完成的直接工作很少。但是,间接地,void *是C ++开发人员在需要数据歧义时可以依靠的巨大资产。
如果只需要缓冲区,请使用malloc / free。如果必须使用new / delete,请考虑一个简单的包装器类:
template<int size_ > struct size_buffer {
char data_[ size_];
operator void*() { return (void*)&data_; }
};
typedef sized_buffer<100> OpaqueBuffer; // logical description of your sized buffer
OpaqueBuffer* ptr = new OpaqueBuffer();
delete ptr;
对于char的特殊情况。
char是没有特殊析构函数的内部类型。因此,泄漏论点是有争议的。
sizeof(char)通常为1,因此也没有对齐参数。对于sizeof(char)不为1的罕见平台,它们会为char分配足够对齐的内存。因此,alignment参数也是有争议的。
在这种情况下,malloc / free会更快。但是您没收std :: bad_alloc,必须检查malloc的结果。调用全局new和delete运算符可能会更好,因为它绕过了中间人。
new
实际上定义为抛出。这不是真的。它取决于编译器和编译器开关。请参阅例如MSVC2019 /GX[-] enable C++ EH (same as /EHsc)
开关。同样在嵌入式系统上,许多人选择不为C ++异常支付性能税。因此,以“但是您没收std :: bad_alloc ...”开头的句子值得怀疑。