Free tutorials & notes in Hindi & English · Clean code examples · Mobile friendly learning
📘 Lesson  ·  Lesson 76

new and delete

Written and reviewed by · Senior IT Faculty · 15+ years’ experience

Storage, Object Lifetime and Ownership

Dynamic allocation has three distinct ideas that beginners often mix:

  1. Storage: bytes must be obtained with suitable size and alignment.
  2. Object lifetime: an object is initialized/constructed in that storage and later destroyed.
  3. Ownership: some program component is responsible for ending the lifetime and releasing the resource exactly once.

A C++ new-expression normally combines allocation and initialization, then yields a pointer. A delete-expression destroys the pointed-to object and invokes the corresponding deallocation function.

int local = 42;              // automatic lifetime: preferred when possible
int* dynamic = new int{42}; // dynamic lifetime: manual ownership
delete dynamic;
dynamic = nullptr;
Modern rule: do not use dynamic allocation just because a pointer is needed. A pointer can be non-owning; prefer scoped objects, references and standard containers unless lifetime genuinely must be dynamic.

Single-Object new and delete

#include <iostream>

int main() {
    int* score = new int{95};
    std::cout << "Score: " << *score << '\n';

    delete score;
    score = nullptr;
}
Score: 95

new int{95} allocates and initializes one integer. The returned pointer is dereferenced with *score. Exactly one matching delete score is required along every successful ownership path.

After deletion, the old pointer value is dangling and must not be read or dereferenced. Assigning nullptr helps this variable, but any copied pointer still dangles. That is why “remember to delete” is not a scalable ownership strategy.

ExpressionMeaning
new intdefault-initialized int; value is indeterminate
new int()value-initialized to zero
new int{}value-initialized to zero
new int{95}initialized to 95

Brace initialization makes the intended initial value explicit and rejects many narrowing conversions.

Dynamic Arrays and Matching Forms

int* marks = new int[3]{72, 88, 91};
for (int i = 0; i < 3; ++i) {
    std::cout << marks[i] << ' ';
}
delete[] marks;
marks = nullptr;
72 88 91

The syntax pair is strict:

AllocationRequired release
new Tdelete pointer
new T[n]delete[] pointer

Mixing the forms, deleting the same allocation twice, deleting an interior pointer, or deleting storage not obtained by a matching new-expression causes undefined behavior.

Prefer a container:

std::vector<int> marks{72, 88, 91};
marks.push_back(96); // owns memory, tracks size, releases automatically

Vector supplies size, bounds-aware at(), iteration and exception-safe cleanup. Use std::string for text rather than manually managed character arrays. Continue with vector and dynamic-memory fundamentals.

Object Construction and Destruction

#include <iostream>
#include <string>
#include <utility>

class Student {
public:
    explicit Student(std::string name) : name_(std::move(name)) {
        std::cout << "Constructed: " << name_ << '\n';
    }
    ~Student() {
        std::cout << "Destroyed: " << name_ << '\n';
    }
    void print() const { std::cout << "Student: " << name_ << '\n'; }
private:
    std::string name_;
};

int main() {
    Student* student = new Student{"Aman"};
    student->print();
    delete student;
}
Constructed: Aman Student: Aman Destroyed: Aman

Construction completes before new returns. delete first invokes the destructor and then deallocates storage. For arrays of class objects, delete[] destroys each constructed element.

If a constructor throws, the new-expression releases the storage it obtained through the matching deallocation mechanism; no pointer is returned. Resources acquired inside class members should themselves be RAII owners so partial construction is safely unwound. See destructors.

Allocation Failure and Advanced Forms

Throwing new

#include <cstddef>
#include <new>

constexpr std::size_t huge_count = 1'000'000;

try {
    auto* data = new int[huge_count];
    delete[] data;
} catch (const std::bad_alloc&) {
    // recover, report, or propagate at an appropriate boundary
}

Ordinary allocation failure normally throws std::bad_alloc. Catch it only where the program has a meaningful recovery policy; blindly retrying can worsen memory pressure.

Nothrow new

int* value = new (std::nothrow) int{7};
if (value == nullptr) {
    // handle failure
} else {
    delete value;
}

The nothrow form returns null on allocation failure. It does not remove other exceptions that an object's constructor may throw.

Placement new: advanced infrastructure tool

alignas(Student) std::byte storage[sizeof(Student)];
Student* p = new (storage) Student{"Aman"};
p->~Student(); // storage itself was not allocated by ordinary new

Placement new constructs an object in supplied storage. Do not apply ordinary delete to that pointer. Correct alignment, lifetime reuse, exceptions and explicit destruction make this a low-level technique for allocators/containers, not ordinary application code.

Ownership Rules and Failure Modes

BugWhat happensDesign prevention
Memory leakOwner loses the only pointerRAII owner
Double deleteSame allocation released twiceUnique ownership type
Use after freeDangling pointer is accessedClear lifetime boundaries
Mismatched deletenew[]/delete mismatchContainer/RAII
Ownership ambiguityNo one knows who releasesExpress owner in the type/API
Exception leakControl exits before deleteAcquire directly into RAII
void process(const Student* student);       // non-owning observation
void take(std::unique_ptr<Student> student); // ownership transfer
Student& require_student(Student& student);  // non-null borrowed object

A raw pointer or reference should normally be a non-owning view. If an API transfers ownership, express that in a smart-pointer type and document nullability/lifetime. Never create two independent owning smart pointers from the same raw pointer.

Rule of zero: if members are standard containers, strings and smart pointers, the compiler-generated destructor/copy/move operations are often correct. Write manual resource management only inside a small, well-tested abstraction.

RAII, unique_ptr and shared_ptr

Resource Acquisition Is Initialization (RAII) ties resource ownership to an object's lifetime. Cleanup happens in the destructor on normal return, early return and exception unwinding.

#include <memory>

int main() {
    auto student = std::make_unique<Student>("Aman");
    student->print();
} // Student is destroyed automatically
Constructed: Aman Student: Aman Destroyed: Aman
NeedPreferred type
Scoped ordinary valueObject by value
Dynamic sequencevector/string
Exclusive dynamic ownerunique_ptr + make_unique
Genuinely shared lifetimeshared_ptr + make_shared
Non-owning observation of shared objectweak_ptr where needed

Prefer unique_ptr: its ownership is explicit, movable and inexpensive. Use shared_ptr only when the ownership graph genuinely requires reference-counted shared lifetime; cycles need weak_ptr or a redesigned owner relationship.

Do not pass shared_ptr everywhere by habit. Pass T&/const T& when a function only uses an existing object, and pass/return an owning type only when ownership semantics require it. Study smart pointers.

Common Mistakes and 100-Point Safety Checklist

MistakeCorrection
Manual array for ordinary collectionUse vector/array/string
Return before deleteAcquire immediately into RAII
delete after new[]Match forms; preferably remove manual pair
Dereference without failure/lifetime proofValidate and express invariants
Two owners for one raw pointerCreate one owner and borrow from it
shared_ptr by defaultPrefer value/unique ownership
Placement new in business codeKeep it inside expert infrastructure
  1. Can the object have automatic storage?
  2. Can a standard container own the data?
  3. Is the owner visible in the type?
  4. Is exclusive ownership sufficient?
  5. Are allocation/deallocation forms matched?
  6. Are exceptions and early returns safe?
  7. Are all borrows shorter than the owner lifetime?
  8. Are array bounds and sizes explicit?
  9. Are AddressSanitizer/UndefinedBehaviorSanitizer tests run?
  10. Does review find zero naked owning new/delete outside RAII internals?
g++ -std=c++20 -Wall -Wextra -Wpedantic \
    -fsanitize=address,undefined -fno-omit-frame-pointer app.cpp

Authoritative References

The object-lifetime, matching-form and allocation-failure rules were checked against the current working draft. The production guidance follows the C++ Core Guidelines: prefer scoped objects, RAII containers and ownership types over naked owning new/delete.

Frequently Asked Questions

What exactly do new and delete do in C++?
A new-expression obtains storage and initializes or constructs an object in it, then returns a pointer. A matching delete-expression destroys the object and releases the storage through the corresponding deallocation function.
What happens if new[] is paired with delete instead of delete[]?
The forms do not match, so program behavior is undefined. Pair new with delete and new[] with delete[]. Better, use vector, string or an owning RAII type so the pairing is automatic.
Is deleting a null pointer safe?
Yes. A delete-expression whose operand evaluates to a null pointer has no effect. Setting every deleted pointer to null does not repair aliases or make manual ownership safe, however.
What happens when memory allocation fails?
An ordinary throwing new-expression normally throws std::bad_alloc. The std::nothrow form returns nullptr on allocation failure, so that result must be checked before dereferencing.
When should modern C++ code use raw new and delete?
Rarely in application code. Prefer automatic objects and standard containers; use std::make_unique for exclusive dynamic ownership and std::make_shared only for genuinely shared ownership. Raw owning allocation is mainly needed inside low-level resource abstractions.

Storage, Lifetime और Ownership

  1. Storage: suitable size/alignment के bytes मिलें।
  2. Lifetime: object construct होकर बाद में destroy हो।
  3. Ownership: कोई component exactly once cleanup की जिम्मेदारी ले।
int local=42;              // automatic, preferred
int* dynamic=new int{42}; // manual dynamic owner
delete dynamic;
dynamic=nullptr;

new allocation + initialization करता, delete destructor + deallocation। Pointer चाहिए इसलिए dynamic allocation जरूरी नहीं; pointer non-owning भी हो सकता है।

Scoped object/reference/container चुनें, जब तक lifetime को सच में dynamic रखने की requirement न हो।

Single Object new और delete

int* score=new int{95};
std::cout<<"Score: "<<*score<<'\n';
delete score;
score=nullptr;
Score: 95

Successful allocation path पर exactly one matching delete चाहिए। Delete के बाद pointer dangling; nullptr assignment केवल उसी variable को मदद करता है, copied aliases फिर भी dangling हैं। new int का value indeterminate, new int{} zero और new int{95} 95 initialize करता है।

Dynamic Arrays और Matching Forms

int* marks=new int[3]{72,88,91};
for(int i=0;i<3;++i) std::cout<<marks[i]<<' ';
delete[] marks;
72 88 91
AllocationRelease
new Tdelete p
new T[n]delete[] p

Mismatch, double delete, interior pointer delete या non-new storage delete undefined behavior है। Ordinary collection के लिए std::vector<int> marks{72,88,91}; लें—size और cleanup automatic। vector तथा dynamic memory पढ़ें।

Objects और Destruction

class Student{
 std::string name_;
public:
 explicit Student(std::string n):name_(std::move(n)){
  std::cout<<"Constructed: "<<name_<<'\n'; }
 ~Student(){std::cout<<"Destroyed: "<<name_<<'\n';}
 void print()const{std::cout<<"Student: "<<name_<<'\n';}
};
Student* s=new Student{"Aman"};
s->print();
delete s;
Constructed: Aman Student: Aman Destroyed: Aman

Construction पूरा होने के बाद pointer मिलता है। Delete पहले destructor, फिर storage deallocate करता है। Constructor throw हो तो new-expression obtained storage release करता है; members को RAII owner रखें। destructors देखें।

Failure और Advanced Forms

Throwing new

try{
 auto* data=new int[huge_count];
 delete[] data;
}catch(const std::bad_alloc&){ /* policy */ }

Ordinary failure सामान्यतः bad_alloc throw करता है। केवल meaningful recovery boundary पर catch करें।

Nothrow

int* p=new(std::nothrow) int{7};
if(!p){ /* failure */ } else delete p;

Nothrow allocation failure पर nullptr; object constructor के other exceptions फिर भी संभव।

Placement new

Supplied aligned storage में object construct करता है; ordinary delete नहीं लगती और explicit destruction/lifetime rules चाहिए। यह allocators जैसी expert infrastructure technique है, application default नहीं।

Ownership Rules

BugPrevention
LeakRAII owner
Double deleteUnique ownership
Use-after-freeClear lifetime
MismatchContainer/RAII
Ambiguous ownerOwner type/API में
Exception leakImmediate RAII acquisition
void process(const Student*);          // non-owner
void take(std::unique_ptr<Student>);  // transfer
Student& require(Student&);           // borrowed non-null

Raw pointer/reference normally non-owning view हो। एक raw pointer से दो independent smart owners कभी न बनाएं। Standard members होने पर Rule of Zero अपनाएं।

RAII और Smart Pointers

auto student=std::make_unique<Student>("Aman");
student->print();
// scope end पर automatic destruction
Constructed: Aman Student: Aman Destroyed: Aman
NeedPreferred
Scoped valueObject by value
Dynamic sequencevector/string
Exclusive ownerunique_ptr/make_unique
Real shared lifetimeshared_ptr/make_shared
Non-owning shared observationweak_ptr when needed

unique_ptr default dynamic owner है। shared_ptr केवल real shared graph में; cycles weak_ptr या ownership redesign से तोड़ें। Function केवल object use करता है तो reference लें, ownership pointer नहीं। smart pointers पढ़ें।

Mistakes और 100-Point Checklist

MistakeCorrection
Manual ordinary arrayvector/array/string
Early return leakImmediate RAII
new[] + deleteMatching form/remove pair
Two ownersOne owner, बाकी borrowers
shared_ptr defaultValue/unique ownership
Placement new business codeExpert infrastructure only
  1. Automatic object possible?
  2. Standard container possible?
  3. Owner type में visible?
  4. Unique ownership enough?
  5. Forms matched?
  6. Exceptions/returns safe?
  7. Borrow owner से shorter?
  8. Bounds explicit?
  9. Sanitizer tests?
  10. RAII internals के बाहर zero naked owning new/delete?
g++ -std=c++20 -Wall -Wextra -Wpedantic \
 -fsanitize=address,undefined app.cpp

Authoritative संदर्भ

Lifetime, matching और failure rules current working draft से verified हैं। Production guidance scoped objects, RAII containers और ownership types को naked new/delete पर prefer करती है।

← Back to C++ Tutorial
🔗

Share this topic with a friend

यह topic किसी दोस्त को भेजें

Found it useful? Send it to a classmate learning the same thing.

अच्छा लगा? जो दोस्त यही सीख रहा है, उसे भेज दीजिए।

💻 Live Code Editor

This page's programs are ready here — run them, edit them, and learn. No installation needed.
Powered by OneCompiler. The code loads into the editor automatically — press Run to see the output. If the editor does not open, open it in a new tab.