C++ Tutorial
C++ Memory
new allocates. delete frees. Smart pointers and containers do this for you in modern C++.
Most objects need no new
A local variable lives until the block ends. The compiler allocates it and frees it. That is automatic storage, often called the stack. Beginners should stay here: int n, string s,vector<int> v.
Example
#include <iostream>
#include <string>
using namespace std;
int main() {
int n = 7;
string label = "count";
cout << label << " " << n << endl;
return 0;
}When main returns, n and label are gone. You did not calldelete. Prefer this until you have a reason not to.
new allocates, delete frees
new int(7) asks the heap for an int, stores 7 in it, and returns a pointer. That memory stays allocated until you delete the pointer. Forget delete and the program leaks. Delete twice and the program is undefined.
Example
#include <iostream>
using namespace std;
int main() {
int* p = new int(7);
cout << *p << endl;
delete p;
return 0;
}Pair every new with one delete on the same path. For arrays allocated withnew int[n] the match is delete[]. This tutorial shows a single object so the pairing is obvious. Do not return from the function before delete unless something else owns the pointer.
If an exception is thrown between new and delete, the delete never runs. That is one reason modern C++ wraps heap objects instead of leaving raw pointers in your functions.
Use vector instead of new[]
A resizable list is the usual reason people reach for new int[n]. vector already does that allocation and frees it in the destructor. You write push_back. You do not writedelete[].
Example
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> nums;
nums.push_back(7);
nums.push_back(11);
cout << nums[0] + nums[1] << endl;
return 0;
}When nums goes out of scope, its buffer is released. No leak, even if you add morepush_back calls later. For a tutorial list, this is the default tool.
unique_ptr when you need a single heap object
Sometimes you really want one object on the heap (a large graph node, an object whose type is chosen at runtime). In C++17 use std::make_unique from <memory>. The unique_ptr deletes the object when the pointer variable dies.
Example
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> p = make_unique<int>(42);
cout << *p << endl;
return 0;
}There is no delete in this file. make_unique<int>(42) allocates. powns the result. At the closing brace of main, the destructor of unique_ptr frees it. Do not mix this with a manual delete p.get().
Who owns the memory
| Tool | You write | Freed by |
|---|---|---|
| Automatic variable | int n = 7; | End of the block |
| vector | push_back | The vector destructor |
| unique_ptr | make_unique | The unique_ptr destructor |
| Raw new | new / delete | You, on every path |
Ownership is the question: when this name dies, does the object die with it? Automatic variables, vectors, and unique_ptr all answer yes. A raw pointer from new answers no until you delete.
A default rule
- Use a local variable or a member. No heap.
- If you need a list, use
vector. - If you need one heap object, use
make_uniqueand#include <memory>. - Write
newanddeleteonly when you are learning how the heap works, or when a library forces a raw pointer and you wrap it immediately.
make_unique arrived in C++14 and is available in the C++17 mode StudyGrid uses. You need#include <memory>. Forgetting that header is a common first compile error.
What you should remember
new is not how you create every object in C++. It is how you ask the heap for storage that outlives the current statement. Containers and smart pointers are the usual owners of that storage. If you do usenew yourself, delete once, on every path, and then look for a way to stop doing that.
Run the examples at /cpp/try. Next: a shelf of complete programs, then templates, the STL, maps, and lambdas.
Worked examples
The short listings above are there so you can see the grammar. The programs here use the same statements on quantities that already have units: a speed, a pH, a count of bases. They are classroom numbers. Air resistance is ignored. g is 9.81 m/s² unless a line says otherwise.
Open them in the C++ editor at /cpp/try. Change one measurement and check whether the result still has the right unit.
Biology
n sample masses on the heap
You often do not know the sample count when you compile. new double[n] asks for n doubles at run time. 12, 24, 16, 1 g might be carbon-12, magnesium, oxygen, hydrogen in a teaching set of relative masses.
delete[] the array. Leaking a four-double block once is invisible; doing it in a loop for a week is not. unique_ptr is the modern default; this listing shows the raw pair so you can see it.
Example
#include <iostream>
using namespace std;
int main() {
int n = 4;
double *mass = new double[n];
mass[0] = 12.0;
mass[1] = 24.0;
mass[2] = 16.0;
mass[3] = 1.0;
cout << "first sample = " << mass[0] << " g" << endl;
delete[] mass;
return 0;
}Physics
Three voltages, then the series sum
Cells in series add. 1.5 + 3.0 + 4.5 V is 9.0 V. The heap is optional for three numbers; it is the pattern you need when the count comes from a file.
V_series = V1 + V2 + V3
Example
#include <iostream>
using namespace std;
int main() {
double *v = new double[3];
v[0] = 1.5;
v[1] = 3.0;
v[2] = 4.5;
cout << "series total = " << v[0] + v[1] + v[2] << " V" << endl;
delete[] v;
return 0;
}