c++ - array - ¿En qué casos uso malloc vs new?
pointer c++ (18)
A menos que esté obligado a usar C, nunca debe usar malloc
. Siempre usa new
.
Si necesita una gran cantidad de datos, haga algo como:
char *pBuffer = new char[1024];
Tenga cuidado, aunque esto no es correcto:
//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;
En su lugar, debe hacer esto al eliminar una matriz de datos:
//This deletes all items in the array
delete[] pBuffer;
La new
palabra clave es la forma C ++ de hacerlo, y se asegurará de que su tipo tenga su constructor llamado . La new
palabra clave también es más segura para el tipo, mientras que malloc
no es segura para el tipo.
La única forma en que podría pensar que sería beneficioso usar malloc
sería si necesitara cambiar el tamaño de su búfer de datos. La new
palabra clave no tiene una forma análoga como realloc
. La función realloc
podría ser capaz de ampliar el tamaño de una parte de la memoria de manera más eficiente.
Vale la pena mencionar que no puede mezclar new
/ free
y malloc
/ delete
.
Nota: Algunas respuestas en esta pregunta no son válidas.
int* p_scalar = new int(5); // Does not create 5 elements, but initializes to 5
int* p_array = new int[5]; // Creates 5 elements
Veo en C ++ que hay múltiples formas de asignar y liberar datos y entiendo que cuando llamas a malloc
deberías llamar free
y cuando usas el new
operador debes vincular con delete
y es un error mezclar los dos (por ejemplo, llamar free()
sobre algo que se creó con el new
operador), pero no tengo claro cuándo debo usar malloc
/ free
y cuándo debo usar new
/ delete
en mis programas del mundo real.
Si usted es un experto en C ++, hágame saber cualquier regla general o convenciones que siga a este respecto.
Desde el C ++ FQA Lite :
[16.4] ¿Por qué debería usar malloc () nuevo en lugar de antiguo y confiable?
FAQ: new / delete call the constructor / destructor; nuevo es de tipo seguro, malloc no lo es; Nuevo puede ser anulado por una clase.
FQA: Las virtudes de lo nuevo que se mencionan en las preguntas frecuentes no son virtudes, porque los constructores, los destructores y la sobrecarga de operadores son basura (¿ve qué sucede cuando no tiene recolección de basura?), Y el tipo de problema de seguridad es realmente pequeño aquí (normalmente tiene para convertir el void * devuelto por malloc al tipo de puntero derecho para asignarlo a una variable de puntero con tipo, lo que puede ser molesto, pero lejos de ser "inseguro").
Ah, y el uso de malloc antiguo confiable hace posible usar el realloc igualmente confiable y antiguo. Lástima que no tengamos un nuevo y brillante operador renovar o algo así.
Aún así, lo nuevo no es lo suficientemente malo como para justificar una desviación del estilo común utilizado en un idioma, incluso cuando el lenguaje es C ++. En particular, las clases con constructores no triviales se comportarán mal en formas fatales si simplemente maltrata los objetos. Entonces, ¿por qué no usar nuevos en todo el código? La gente rara vez sobrecarga al operador, por lo que probablemente no se interponga demasiado en tu camino. Y si se sobrecargan de nuevo, siempre puedes pedirles que paren.
Lo siento, simplemente no pude resistirme. :)
Desde una perspectiva más baja, new inicializará toda la memoria antes de proporcionarla, mientras que malloc conservará el contenido original de la memoria.
En el siguiente escenario, no podemos usar nuevo ya que llama al constructor.
class B {
private:
B *ptr;
int x;
public:
B(int n) {
cout<<"B: ctr"<<endl;
//ptr = new B; //keep calling ctr, result is segmentation fault
ptr = (B *)malloc(sizeof(B));
x = n;
ptr->x = n + 10;
}
~B() {
//delete ptr;
free(ptr);
cout<<"B: dtr"<<endl;
}
};
Hay algunas cosas que hace new
que malloc
no hace:
-
new
construye el objeto llamando al constructor de ese objeto -
new
no requiere encasillado de la memoria asignada. - No requiere que se asigne una cantidad de memoria, sino que requiere que se construyan varios objetos.
Por lo tanto, si usa malloc
, entonces debe hacer lo anterior explícitamente, lo que no siempre es práctico. Además, lo new
puede estar sobrecargado, pero malloc
no puede estarlo.
Hay una gran diferencia entre malloc
y new
. malloc
asigna memoria. Esto está bien para C, porque en C, un bulto de memoria es un objeto.
En C ++, si no está tratando con tipos de POD (que son similares a los tipos de C), debe llamar a un constructor en una ubicación de memoria para que realmente tenga un objeto allí. Los tipos no POD son muy comunes en C ++, ya que muchas características de C ++ hacen que un objeto no sea POD automáticamente.
new
asigna memoria y crea un objeto en esa ubicación de memoria. Para los tipos que no son POD, esto significa llamar a un constructor.
Si haces algo como esto:
non_pod_type* p = (non_pod_type*) malloc(sizeof *p);
El puntero que obtiene no se puede anular debido a que no apunta a un objeto. Necesitaría llamar a un constructor en él antes de poder usarlo (y esto se hace usando una new
ubicación).
Si, por otro lado, haces:
non_pod_type* p = new non_pod_type();
Obtienes un puntero que siempre es válido, porque un new
objeto creado.
Incluso para los tipos de POD, hay una diferencia significativa entre los dos:
pod_type* p = (pod_type*) malloc(sizeof *p);
std::cout << p->foo;
Este fragmento de código imprimiría un valor no especificado, porque los objetos POD creados por malloc
no se inicializan.
Con el new
, podría especificar un constructor al que llamar, y así obtener un valor bien definido.
pod_type* p = new pod_type();
std::cout << p->foo; // prints 0
Si realmente lo desea, puede usar el uso new
para obtener objetos POD sin inicializar. Vea esta otra respuesta para más información sobre eso.
Otra diferencia es el comportamiento ante el fracaso. Cuando no se puede asignar memoria, malloc
devuelve un puntero nulo, mientras que el new
lanza una excepción.
El primero requiere que pruebe cada puntero devuelto antes de usarlo, mientras que el último siempre generará punteros válidos.
Por estas razones, en el código C ++ debe usar new
, y no malloc
. Pero incluso en ese caso, no debe usar el new
"en abierto", ya que adquiere los recursos que necesita liberar más adelante. Cuando use new
, debe pasar su resultado inmediatamente a una clase de gestión de recursos:
std::unique_ptr<T> p = std::unique_ptr<T>(new T()); // this won''t leak
La respuesta corta es: no use malloc
para C ++ sin una buena razón para hacerlo. malloc
tiene una serie de deficiencias cuando se utiliza con C ++, que se definió para superar.
Deficiencias arregladas por nuevo para el código C ++
Malloc no es seguro para los tipos de ninguna manera significativa. En C ++ se requiere que emita el retorno de
void*
. Esto potencialmente introduce muchos problemas:#include <stdlib.h> struct foo { double d[5]; }; int main() { foo *f1 = malloc(1); // error, no cast foo *f2 = static_cast<foo*>(malloc(sizeof(foo))); foo *f3 = static_cast<foo*>(malloc(1)); // No error, bad }
Aunque es peor que eso. Si el tipo en cuestión es POD (datos antiguos), entonces puede usar semi-sensiblemente
malloc
para asignar memoria, como hacef2
en el primer ejemplo.No es tan obvio, sin embargo, si un tipo es POD. El hecho de que es posible que un tipo dado cambie de POD a no POD sin un error de compilación resultante y potencialmente muy difícil de solucionar problemas es un factor importante. Por ejemplo, si alguien (posiblemente otro programador, durante el mantenimiento, mucho más tarde realizaría un cambio que causó que
foo
ya no fuera POD, entonces no aparecería un error obvio en el momento de la compilación como esperaría, por ejemplo:struct foo { double d[5]; virtual ~foo() { } };
haría que el
malloc
def2
también se vuelva malo, sin ningún diagnóstico obvio. El ejemplo aquí es trivial, pero es posible introducir accidentalmente el non-PODness mucho más lejos (por ejemplo, en una clase base, agregando un miembro no-POD). Si tiene C ++ 11 / boost puede usaris_pod
para verificar que esta suposición es correcta y generar un error si no lo es:#include <type_traits> #include <stdlib.h> foo *safe_foo_malloc() { static_assert(std::is_pod<foo>::value, "foo must be POD"); return static_cast<foo*>(malloc(sizeof(foo))); }
Aunque boost no puede determinar si un tipo es POD sin C ++ 11 o algunas otras extensiones de compilador.
malloc
devuelveNULL
si la asignación falla.new
lanzarástd::bad_alloc
. El comportamiento del uso posterior de un punteroNULL
no está definido. Una excepción tiene una semántica limpia cuando se lanza y se emite desde la fuente del error. Envolvermalloc
con una prueba adecuada en cada llamada parece tedioso y propenso a errores. (Solo tienes que olvidar una vez para deshacer todo ese buen trabajo). Se puede permitir que una excepción se propague a un nivel en el que una persona que llama pueda procesarla con sensatez, dondeNULL
es mucho más difícil de devolver de manera significativa. Podríamos extender nuestra funciónsafe_foo_malloc
para lanzar una excepción o salir del programa o llamar a algún controlador:#include <type_traits> #include <stdlib.h> void my_malloc_failed_handler(); foo *safe_foo_malloc() { static_assert(std::is_pod<foo>::value, "foo must be POD"); foo *mem = static_cast<foo*>(malloc(sizeof(foo))); if (!mem) { my_malloc_failed_handler(); // or throw ... } return mem; }
Fundamentalmente,
malloc
es una característica de C y lanew
es una característica de C ++. Como resultado,malloc
no juega bien con los constructores, solo se ve asignando una porción de bytes. Podríamos extender nuestrosafe_foo_malloc
más para usar la ubicaciónnew
:#include <stdlib.h> #include <new> void my_malloc_failed_handler(); foo *safe_foo_malloc() { void *mem = malloc(sizeof(foo)); if (!mem) { my_malloc_failed_handler(); // or throw ... } return new (mem)foo(); }
Nuestra función
safe_foo_malloc
no es muy genérica; lo ideal es que queramos algo que pueda manejar cualquier tipo, no solofoo
. Podemos lograr esto con plantillas y plantillas variadas para constructores no predeterminados:#include <functional> #include <new> #include <stdlib.h> void my_malloc_failed_handler(); template <typename T> struct alloc { template <typename ...Args> static T *safe_malloc(Args&&... args) { void *mem = malloc(sizeof(T)); if (!mem) { my_malloc_failed_handler(); // or throw ... } return new (mem)T(std::forward(args)...); } };
Ahora, aunque al solucionar todos los problemas que hemos identificado hasta ahora, prácticamente hemos reinventado el
new
operador predeterminado. Si vas a usarmalloc
y lanew
ubicación, entonces, ¡podrías comenzar con lanew
!
Los operadores new
y delete
pueden operar en clases y estructuras, mientras que malloc
y free
solo funcionan con bloques de memoria que deben ser lanzados.
El uso de new/delete
ayudará a mejorar su código, ya que no necesitará convertir la memoria asignada a la estructura de datos requerida.
Para responder a su pregunta, debe saber la diferencia entre malloc
y new
. La diferencia es simple:
malloc
asigna memoria , mientras que new
asigna memory AND llama al constructor del objeto para el que está asignando la memoria.
Por lo tanto, a menos que esté restringido a C, nunca debe usar malloc, especialmente cuando se trata de objetos C ++. Esa sería una receta para romper tu programa.
También la diferencia entre free
y delete
es la misma. La diferencia es que delete
llamará al destructor de su objeto además de liberar memoria.
Si tiene el código C que desea trasladar a C ++, puede dejar cualquier llamada malloc () en él. Para cualquier código nuevo de C ++, recomiendo usar un nuevo en su lugar.
Si trabaja con datos que no requieren construcción / destrucción y requiere reasignaciones (por ejemplo, una gran variedad de entradas), creo que malloc / free es una buena opción, ya que le proporciona realloc, que es mucho más rápido que new-memcpy -eliminar (está en mi caja de Linux, pero supongo que esto puede depender de la plataforma). Si trabaja con objetos C ++ que no son POD y requieren construcción / destrucción, entonces debe usar los operadores nuevos y eliminar.
De todos modos, no veo por qué no debería usar ambos (siempre que libere su memoria maliciosa y elimine los objetos asignados con nuevos) si puede aprovechar el aumento de velocidad (a veces uno significativo, si está reasignando matrices grandes) de POD) que realloc te puede dar.
Sin embargo, a menos que lo necesites, debes mantenerte en nuevo / eliminar en C ++.
Siempre use nuevo en C ++. Si necesita un bloque de memoria sin tipo, puede usar el operador new directamente:
void *p = operator new(size);
...
operator delete(p);
Un caso raro para considerar usar malloc / free en lugar de new / delete es cuando asignas y luego reasignas (tipos de pod simples, no objetos) usando realloc ya que no hay una función similar a realloc en c ++ (aunque esto se puede hacer usando más c ++ enfoque)
Utilice malloc
y free
solo para asignar memoria que será administrada por bibliotecas y APIs centradas en c. Use new
y delete
(y las []
variantes) para todo lo que controle.
malloc () se utiliza para asignar dinámicamente memoria en C, mientras que new () realiza el mismo trabajo en c ++. Así que no puedes mezclar convenciones de codificación de 2 idiomas. Sería bueno si pidieras una diferencia entre calloc y malloc ()
si está utilizando c ++, intente usar new / delete en lugar de malloc / calloc, ya que son operadores en sí mismos en comparación con malloc / calloc. Para ellos, solía incluir otro encabezado para eso. No mezcle dos idiomas diferentes en una sola codificación. .su trabajo es similar en todos los aspectos, ya que ambos asignan la memoria dinámicamente del segmento de pila en la tabla hash.
new
inicializará los valores predeterminados de la estructura y vinculará correctamente las referencias en sí mismo.
P.ej
struct test_s {
int some_strange_name = 1;
int &easy = some_strange_name;
}
Así que new struct test_s
devolverá una estructura inicializada con una referencia de trabajo, mientras que la versión de malloc''ed no tiene valores predeterminados y las referencias internas no se inicializan.
nuevo vs malloc ()
1) new
es un operador , mientras que malloc()
es una función .
2) new
constructores de llamadas, mientras que malloc()
no lo hace.
3) new
devuelve el tipo de datos exacto , mientras que malloc()
devuelve void * .
4) new
nunca devuelve un valor NULL (se producirá un error), mientras que malloc()
devuelve un valor nulo
5) La reasignación de memoria no es manejada por new
mientras malloc()
puede