以下代码由于从未调用 std::string 的构造函数而中断,因为对 malloc 的调用似乎并未调用类成员的构造函数。
如何在不使用 new 的情况下调用 std::string 的构造函数?或者我是否必须在分配内存后在整个结构上使用新的位置?
#include <string>
class MyClass
{
std::string value;
};
int main()
{
MyClass* cls = (MyClass*)malloc(sizeof(MyClass));
cls->value = "hello";
system("pause");
}
Run Code Online (Sandbox Code Playgroud)
编辑:
阅读评论后,我想知道以下代码是否会按预期工作?不确定是否需要调用除整个类的析构函数之外的任何析构函数。
#include <string>
class MyClass
{
std::string value;
};
int main()
{
MyClass* cls = (MyClass*)malloc(sizeof(MyClass));
cls = new(cls) MyClass;
cls->value = "hello";
cls->~MyClass();
free(cls);
system("pause");
}
Run Code Online (Sandbox Code Playgroud)
Hum*_*ler 14
malloc 是一个 C 函数,不了解类、构造函数等。
如果您对使用此方法一无所知,您将必须始终new在每个对象上调用放置(注意:这需要包括 header <new>),并且还必须在释放之前手动调用析构函数。您还需要存储从放置返回的指针new以避免未定义的行为(因为这在技术上是唯一指向对象生命周期开始的指针)。
正如您在编辑中所做的那样,它看起来像:
auto cls = static_cast<MyClass*>(std::malloc(sizeof(MyClass)));
cls = new(cls) MyClass(/* any arguments here ... */);
cls->value = "hello";
cls->~MyClass();
std::free(cls);
Run Code Online (Sandbox Code Playgroud)
但是,请注意,通过使用malloc您实际上会引入与异常安全相关的其他问题。如果您的放置new调用抛出异常,则分配的内存std::malloc将泄漏——这很糟糕。如果返回 null,您也不希望对放置new进行操作,并且需要传达此错误 - 可能是通过异常。您可能希望将其包装到一个帮助程序中,以确保这些情况不会发生:nullptrmalloc
#include <new> // placement-new, std::bad_alloc
#include <utility> // std::forward
#include <cstdlib> // std::malloc, std::free
template <typename T, typename...Args>
T* make(Args&&...args)
{
auto* p = static_cast<T*>(std::malloc(sizeof(T)));
// Placement new can't operate on nullptr
if (p == nullptr) {
// alternatively, this could be 'return nullptr' if not using exceptions
throw std::bad_alloc{};
}
try {
p = new (p) T(std::forward<Args>(args)...);
} catch (...) {
std::free(p);
throw; // rethrow the exception here
}
return p;
};
Run Code Online (Sandbox Code Playgroud)
类似地,您可能希望在释放对象的同时销毁对象,这样资源就不会泄漏:
template <typename T>
void dispose(T* p)
{
p->~T();
std::free(p);
}
Run Code Online (Sandbox Code Playgroud)
使用这些实用程序,上面的示例现在变为:
auto cls = make<MyClass>(/* any arguments here ... */);
cls->value = "hello";
dispose(cls);
Run Code Online (Sandbox Code Playgroud)
如果这看起来像是一大堆样板,那是因为这正是您所做的,new并且delete已经为您做了。如果可以,在处理 C++ 类型时使用这些工具会好得多。这也使您可以轻松地继续使用其他 RAII 包装器,例如智能指针,而无需手动指定分配器 ( shared_ptr) 或删除器 ( unique_ptr)。
在使用C++ 代码时,IMO 确实没有充分的理由阻止使用new和delete支持malloc/ free。如果您有一个公开 C-only 接口但在C++ 代码中实现的库 - 实际上最好将分配内部化并切换到newanddelete而不是使用malloc,free这样您就可以省去麻烦。1
需要注意的另一件事是,如果您系统中分配的类型曾经通过alignas超过的自定义对齐方式alignof(std::max_align_t),则 usingstd::malloc将是未定义的行为,因为它将无法满足对齐要求。这是new/delete会为您处理的另一件事。
1如果您的“混合 C++/C”代码在某个时候公开了 C 接口,那么使用newanddelete而不是malloc,并且简单地在 API 上显式公开创建/删除函数会更简洁。例如:
// C header:
extern "C"
my_class* make_my_class(void);
extern "C"
void dispose_my_class(my_class* p);
// C++ implementation:
extern "C"
my_class* make_my_class(void)
{
return new my_class{ ... };
}
extern "C"
void dispose_my_class(my_class* p)
{
delete p;
}
Run Code Online (Sandbox Code Playgroud)
这通常是 C/C++ 互操作所采用的方法。