本文是一道C++面试的基础题——new出来的对象可以用free释放吗?它甚至还有一个变体——malloc分配的内存可以使用delete释放吗?其实这两道题都是在考察new/delete、malloc/free的区别,只是面试官换了个问法而已。如果面试官直接问:new和malloc的区别是什么?可能会更熟悉一些。
在进入正文前,在回顾下new/delete和malloc/free的区别:
综上,new/delete在分配和释放内存的基础上会调用对象的构造函数和析构函数,而malloc/free只会分配和释放内存,不会调用构造函数和析构函数。如上可以作为该问题的回答。但是这并不是我想写这篇文章的目的。
结合如上理论知识,new出来的对象使用free释放时无法调用其析构函数;同理,malloc出来的内存使用delete释放时会调用其析构函数。这些原则要求我们必须配套使用new/delete和malloc/free。由此我猜想:
由上的理论分析可知,非POD类型必须配套使用new/delete和malloc/free已经达成共识了,只是对于POD类型,目前还持怀疑态度,所以本文将以实验的形式对POD类型进行验证。
#include <iostream>
#include <iosfwd>
#include <string>
#include <type_traits>
struct Point
{
int x;
int y;
friendstd::ostream& operator<<(std::ostream& os, const Point& p)
{
os << "(" << std::to_string(p.x) << ", " << std::to_string(p.y) << ")";
return os;
}
};
template <typename T>
void using_pod_new_free()
{
for (size_t i = 0; i < 100000; i++)
{
auto p = new T(10);
std::cout << *p << std::endl;
free(p);
}
}
template <>
void using_pod_new_free<Point>()
{
for (size_t i = 0; i < 100000; i++)
{
auto p = new Point(10,5);
std::cout << *p << std::endl;
free(p);
}
}
template <typename T>
void using_pod_malloc_delete()
{
for (size_t i = 0; i < 100000; i++)
{
auto p = (T*)malloc(sizeof(T));
if (p)
{
*p = 10;
std::cout << *p << std::endl;
delete p;
}
}
}
template <>
void using_pod_malloc_delete<Point>()
{
for (size_t i = 0; i < 100000; i++)
{
auto p = (Point*)malloc(sizeof(Point));
if (p)
{
p->x = 10;
p->y = 20;
std::cout << *p << std::endl;
delete p;
}
}
}
int main()
{
/*using_pod_new_free<int>();
using_pod_malloc_delete<int>();*/
/*using_pod_new_free<float>();
using_pod_malloc_delete<float>();*/
/*using_pod_new_free<long>();
using_pod_malloc_delete<long>();*/
using_pod_new_free<Point>();
using_pod_malloc_delete<Point>();
return0;
}
运行如上代码在windows平台下,IDE为MSVC2022,C++20标准,编译程序,运行程序,并借助VS自带的性能分析工具,分析内存使用率,并未出现内存泄漏。
尽管如上测试具有不充分性,但结合理论分析和实验,确认所有的POD类型都可以混用。
结合面试问题,并分析new/delete和malloc/free的区别,在分析其原理的基础上,通过实验验证POD类型混用new/delete和malloc/free是可行的,而非POD类型必须配套使用。但是,为了代码的可维护性,建议还是使用new/delete和malloc/free配套使用。