Question

When to Use malloc vs new in C++

cppmemory-managementmallocnew-operator

Question

In C++, there are multiple ways to allocate and free memory. I understand that if you call malloc, you should later call free, and if you use the new operator, you should pair it with delete. I also know that mixing them is a mistake—for example, calling free() on memory created with new.

However, I am not sure when to use malloc/free versus new/delete in real-world C++ programs.

What rules of thumb or conventions do experienced C++ developers follow when deciding between them?

Short Answer

By the end of this page, you will understand the difference between malloc/free and new/delete in C++, why new is usually preferred over malloc for C++ objects, and why modern C++ often avoids both in favor of RAII and standard library containers such as std::vector, std::string, and smart pointers.

Concept

malloc and free come from C. new and delete are part of C++.

The key difference is that C++ objects are more than raw memory.

What malloc does

malloc:

  • allocates a block of raw memory
  • returns a pointer to that memory
  • does not call constructors
  • does not know about C++ object lifetime
int* p = (int*)std::malloc(sizeof(int));
if (p) {
    *p = 42;
    std::free(p);
}

This works for simple memory, but malloc does not create a real C++ object in the full language sense for class types.

What new does

new:

  • allocates memory
  • constructs an object in that memory
  • returns a correctly typed pointer

delete:

  • destroys the object by calling its destructor
  • releases the memory
int* p = new int(42);
delete p;

For class types, constructors and destructors matter a lot.

class File {
public:
    File() { /* open resource */ }
    ~File() { /* release resource */ }
};

File* f = new File();
delete f;

If you used malloc for File, the constructor would not run, so the object would not be properly initialized.

Why this matters

In C++, object lifetime is central:

  • constructors set objects up
  • destructors clean them up
  • many classes manage resources like files, sockets, memory, locks, or database handles

Using malloc skips that system. That is why in normal C++ code, malloc is rarely the right choice.

Modern C++ guidance

In real-world modern C++:

  • prefer automatic storage when possible
  • prefer standard containers like std::vector and std::string
  • prefer smart pointers like std::unique_ptr and std::shared_ptr when dynamic allocation is needed
  • avoid direct new/delete in most application code
  • use malloc/free mainly when interacting with C APIs or special low-level memory systems

So the practical rule is:

  • Do not use malloc for normal C++ objects
  • Do not use raw new/delete unless you truly need manual lifetime control
  • Prefer higher-level C++ tools first

Mental Model

Think of memory like an empty apartment.

  • malloc gives you the apartment space, but it does not bring in furniture, set up electricity, or make it livable.
  • new gives you the apartment space and moves the object in properly.
  • delete moves the object out properly and cleans things up.
  • free only releases the apartment space.

For simple raw bytes, an empty apartment may be enough. For real C++ objects, you usually need the full move-in and move-out process.

A class constructor is like setup instructions. A destructor is like cleanup instructions. malloc and free ignore those instructions.

Take Quiz

Syntax and Examples

Core syntax

malloc and free

#include <cstdlib>

int* p = (int*)std::malloc(sizeof(int));
if (p != nullptr) {
    *p = 10;
    std::free(p);
}

new and delete

int* p = new int(10);
delete p;

Arrays

int* numbers = new int[5];
delete[] numbers;

Example with a class

#include <iostream>

class User {
public:
    User() {
        std::cout << "User created\n";
    }

    ~User() {
        std::cout << "User destroyed\n";
    }
};

int main() {
    User* u = new User();
    delete u;
}

Why new is better here

  • new User() allocates memory and calls User()
  • delete u calls ~User() and then releases memory

Using malloc(sizeof(User)) would only reserve bytes. It would not call User().

Modern C++ example: avoid manual allocation

#include <memory>

int main() {
    auto p = std::make_unique<int>(10);
}

This is usually better than:

int* p = new int(10);
delete p;

Because std::make_unique cleans up automatically.

Step by Step Execution

Consider this example:

#include <iostream>

class Box {
public:
    Box() {
        std::cout << "Constructor\n";
    }

    ~Box() {
        std::cout << "Destructor\n";
    }
};

int main() {
    Box* b = new Box();
    delete b;
}

Step by step

  1. new Box() requests enough memory for one Box.
  2. That memory is allocated.
  3. The Box constructor runs.
  4. The pointer is stored in b.
  5. delete b is executed.
  6. The Box destructor runs.
  7. The memory is released.

Output:

Constructor
Destructor

Now compare with malloc:

#include <cstdlib>

class Box {
public:
    Box() {}
    ~Box() {}
};

int main() {
    Box* b = (Box*)std::malloc(sizeof(Box));
    std::free(b);
}

What happens here

  1. Raw memory is allocated.
  2. No constructor runs.
  3. b points to bytes, not a properly constructed Box object in the usual C++ sense.
  4. std::free(b) releases the bytes.
  5. No destructor runs.

This is why malloc is unsafe for normal class-object creation in C++.

Real World Use Cases

When new/delete might be used

Although modern C++ often wraps dynamic memory, raw new may still appear in:

  • legacy C++ codebases
  • low-level libraries
  • custom data structures written for learning or performance work
  • code that transfers ownership into another API

Example:

Node* node = new Node(value);
// later
delete node;

When malloc/free might be used

These are more common in special situations:

  • calling a C library that expects memory from malloc
  • working with raw byte buffers
  • implementing custom allocators or memory pools
  • interfacing with operating-system or network APIs using plain buffers

Example:

char* buffer = (char*)std::malloc(1024);
if (buffer) {
    // fill buffer
    std::free(buffer);
}

What is more common in real applications

Most real C++ application code uses:

  • std::vector for dynamic arrays
  • std::string for text
  • std::unique_ptr for exclusive ownership
  • std::shared_ptr for shared ownership when necessary

Example:

std::vector<int> scores = {10, 20, 30};

No manual new, delete, malloc, or free is needed.

Take Quiz

Real Codebase Usage

In real C++ projects, experienced developers usually follow these patterns:

1. Prefer stack allocation first

If an object does not need dynamic lifetime, create it directly.

User user;

This is simpler and safer than:

User* user = new User();
delete user;

2. Prefer containers over manual arrays

Instead of:

int* values = new int[100];
delete[] values;

Use:

std::vector<int> values(100);

3. Prefer smart pointers over raw ownership

Instead of:

Widget* w = new Widget();
delete w;

Use:

auto w = std::make_unique<Widget>();

4. Use RAII

RAII means resources are tied to object lifetime.

  • object created -> resource acquired
  • object destroyed -> resource released

This prevents leaks, especially when exceptions occur.

5. Use malloc mainly for C interop or raw storage

Example pattern:

extern "C" void c_api_take_buffer(void* buffer);

void sendData() {
    void* buffer = std::malloc(256);
    if (!buffer) {
        return;
    }

    c_api_take_buffer(buffer);
    std::free(buffer);
}

6. Avoid manual delete spread across code

In large codebases, manual deletion is error-prone. Developers prefer designs where cleanup is automatic and local.

7. Use guard clauses and early returns safely

With raw pointers, early returns can leak memory:

void process(bool failed) {
    int* p = new int(5);
    if (failed) {
        return; // leak
    }
    delete p;
}

With RAII:

#include <memory>

void process(bool failed) {
    auto p = std::make_unique<int>(5);
    if (failed) {
        return; // safe
    }
}

That is a major reason modern C++ code avoids manual memory management.

Common Mistakes

1. Mixing allocation and deallocation styles

Broken code:

int* p = new int(5);
std::free(p); // wrong
int* p = (int*)std::malloc(sizeof(int));
delete p; // wrong

Fix

  • new pairs with delete
  • new[] pairs with delete[]
  • malloc pairs with free

2. Using malloc for objects with constructors

Broken code:

MyClass* obj = (MyClass*)std::malloc(sizeof(MyClass));

This does not call the constructor.

Fix

MyClass* obj = new MyClass();
delete obj;

Or better:

auto obj = std::make_unique<MyClass>();

3. Forgetting delete[] for arrays

Broken code:

int* arr = new int[10];
delete arr; // wrong

Fix

int* arr = new int[10];
delete[] arr;

4. Memory leaks from early returns

Broken code:

void run(bool error) {
    int* p = new int(42);
    if (error) return;
    delete p;
}

Fix

Use RAII:

#include <memory>

void run(bool error) {
    auto p = std::make_unique<int>(42);
    if (error) return;
}

5. Double deletion

Broken code:

int* p = new int(3);
delete p;
delete p; // wrong

Fix

Delete only once, or use smart pointers.

6. Assuming malloc and new fail the same way

  • malloc returns nullptr on failure
  • new normally throws std::bad_alloc

This affects error handling.

Comparisons

Conceptmalloc / freenew / delete
Language originCC++
Allocates memoryYesYes
Calls constructorNoYes
Calls destructorNoYes
Returns typed pointerNo, returns void*Yes
Needs cast in C++Usually yesNo
Array supportManual size handlingnew[] / delete[]
Failure behaviorReturns nullptrThrows std::bad_alloc by default
Suitable for C++ class objectsUsually noYes
Common in modern C++ app codeRareLess common than smart pointers, but valid

new/delete vs smart pointers

| Approach | Manual cleanup | Safer ownership | Recommended for most code | |---|---|---| | Raw new / delete | Yes | No | No | | std::unique_ptr | No | Yes | Yes | | std::shared_ptr | No | Shared ownership | Sometimes |

Practical recommendation

SituationBest choice
Local object with normal lifetimeStack allocation
Dynamic arraystd::vector
Dynamically owned objectstd::unique_ptr
Shared ownershipstd::shared_ptr
C API requiring malloc memorymalloc / free
Legacy C++ code using raw pointersnew / delete if necessary
Take Quiz

Cheat Sheet

Quick rules

  • Prefer plain objects when possible.
  • Prefer std::vector, std::string, and other standard containers.
  • Prefer std::unique_ptr over raw new.
  • Use new for C++ objects only when manual dynamic allocation is truly necessary.
  • Use malloc mainly for C interoperability or raw byte buffers.
  • Never mix allocation and deallocation families.

Pairing rules

T* p = new T();
delete p;
T* arr = new T[10];
delete[] arr;
void* p = std::malloc(100);
std::free(p);

Important differences

  • malloc does not call constructors
  • free does not call destructors
  • new returns typed pointers
  • new usually throws on failure
  • malloc returns nullptr on failure

Best modern alternatives

std::vector<int> values(10);
std::string name = "Ada";
auto p = std::make_unique<MyClass>();

Edge cases

  • new[] must use delete[]
  • never free() a pointer from new
  • never delete a pointer from malloc
  • for class types, avoid malloc unless you are doing advanced low-level work

FAQ

Should I ever use malloc in modern C++?

Usually no for normal application code. Use it mainly when working with C libraries or raw memory buffers.

Is new better than malloc in C++?

For creating C++ objects, yes. new constructs objects properly, while malloc only allocates raw memory.

Should I use new and delete directly in modern C++?

Usually not. Prefer stack allocation, containers, and smart pointers.

Why is mixing new with free undefined behavior?

Because the allocation and cleanup mechanisms are different. free does not know how to destroy objects created by new.

What should I use instead of new[] for arrays?

Usually std::vector.

What happens if new cannot allocate memory?

By default, it throws std::bad_alloc.

What happens if malloc fails?

It returns nullptr.

If malloc is lower level, is it faster?

Not necessarily in a meaningful way for normal code. Correctness, safety, and maintainability matter more.

Take Quiz

Mini Project

Description

Build a small C++ program that manages a list of scores. The goal is to practice choosing the right memory-management tool and to see why standard containers are usually better than manual allocation. This project demonstrates dynamic storage without needing malloc or raw new[].

Goal

Create a program that stores, prints, and averages a user-defined number of scores using std::vector instead of manual memory management.

Requirements

  • Ask the user how many scores they want to enter.
  • Store the scores in a dynamically sized container.
  • Print all entered scores.
  • Calculate and print the average score.
  • Do not use malloc, free, new, or delete in the final solution.
Take Quiz

Keep learning

Related questions

View more