std::unique_ptr
In C++, unique_ptr is a smart pointer that manages exclusive ownership of dynamically allocated memory. Only one object can hold ownership at a time, and memory is automatically released when the pointer goes out of scope. Manual delete is not needed, making it widely used to prevent memory leaks.
Syntax
// ========================================
// Basic syntax for unique_ptr
// ========================================
#include <memory>
// Create an object with make_unique (available since C++14)
std::unique_ptr<Type> ptr = std::make_unique<Type>(constructor_args);
// Can be used just like a pointer
ptr->memberFunction();
*ptr; // dereference
// Transfer ownership to another unique_ptr (copying is forbidden)
std::unique_ptr<Type> ptr2 = std::move(ptr);
// ptr becomes nullptr after this
// Get the raw pointer (ownership is not transferred)
Type* raw = ptr2.get();
// Explicitly reset (releases managed object and sets to nullptr)
ptr2.reset();
// Using a custom deleter
auto deleter = [](Type* p) { delete p; };
std::unique_ptr<Type, decltype(deleter)> ptr3(new Type(), deleter);
Syntax Reference
| Syntax / Operation | Description |
|---|---|
| std::make_unique<T>(...) | Dynamically allocates an object of type T and returns a unique_ptr. Available since C++14. |
| std::unique_ptr<T> p(new T(...)) | Constructs a unique_ptr from a raw pointer. Used when make_unique is not available. |
| p->member / *p | Accesses members of the managed object. Works just like a regular pointer. |
| std::move(p) | Transfers ownership to another unique_ptr. Since copying is forbidden, move is required. |
| p.get() | Returns the managed raw pointer. Ownership is not transferred. |
| p.reset() | Releases the managed object and sets to nullptr. A new pointer can be passed as an argument to replace it. |
| p.release() | Releases ownership and returns the raw pointer. Responsibility for deallocation moves to the caller. |
| if (p) | Evaluates to true if not nullptr. Can be used for null checks. |
Sample Code
steinsgate_unique_ptr.cpp
// ========================================
// steinsgate_unique_ptr.cpp
// Manages Steins;Gate lab members with unique_ptr,
// demonstrating ownership transfer, reset, and null checks
// ========================================
#include <iostream>
#include <memory>
#include <string>
#include <vector>
// ========================================
// LabMember class (Future Gadget Lab member)
// ========================================
class LabMember {
private:
std::string name; // member name
int memberNumber; // lab member number
std::string nickname; // nickname
public:
LabMember(const std::string& name, int memberNumber, const std::string& nickname)
: name(name), memberNumber(memberNumber), nickname(nickname) {
std::cout << "[Alloc] Lab member #" << memberNumber << " " << name << " allocated." << std::endl;
}
~LabMember() {
std::cout << "[Free] Lab member #" << memberNumber << " " << name << " freed." << std::endl;
}
void introduce() const {
std::cout << "Lab member #" << memberNumber << ": " << name
<< " (" << nickname << ")" << std::endl;
}
const std::string& getName() const { return name; }
int getMemberNumber() const { return memberNumber; }
};
// ========================================
// Takes a unique_ptr by value (ownership transfers, move required)
// ========================================
void assignToTimeMachine(std::unique_ptr<LabMember> member) {
std::cout << " Assigning " << member->getName() << " to the time machine." << std::endl;
// member is destroyed when the function exits, releasing memory
}
// ========================================
// Takes a unique_ptr by const reference (ownership stays)
// ========================================
void printMember(const std::unique_ptr<LabMember>& member) {
if (member) {
member->introduce();
} else {
std::cout << " (This slot is empty)" << std::endl;
}
}
int main() {
std::cout << "=== unique_ptr Basic Operations ===" << std::endl << std::endl;
// ----------------------------------------
// Create objects with make_unique
// ----------------------------------------
std::unique_ptr<LabMember> okabe = std::make_unique<LabMember>(
"Okabe Rintaro", 1, "Hououin Kyouma"
);
std::unique_ptr<LabMember> kurisu = std::make_unique<LabMember>(
"Makise Kurisu", 4, "Christina"
);
std::cout << std::endl;
std::cout << "--- Introductions ---" << std::endl;
printMember(okabe);
printMember(kurisu);
// ----------------------------------------
// get() retrieves the raw pointer (no ownership transfer)
// ----------------------------------------
std::cout << std::endl << "--- get(): access raw pointer ---" << std::endl;
LabMember* rawPtr = okabe.get();
std::cout << " via rawPtr: ";
rawPtr->introduce();
std::cout << " okabe still valid: " << (okabe ? "true" : "false") << std::endl;
// ----------------------------------------
// move() transfers ownership
// ----------------------------------------
std::cout << std::endl << "--- move(): transfer ownership ---" << std::endl;
std::unique_ptr<LabMember> newOwner = std::move(okabe);
std::cout << " okabe valid: " << (okabe ? "true" : "false") << std::endl;
std::cout << " newOwner inherited: ";
newOwner->introduce();
// ----------------------------------------
// Pass ownership to a function via move
// ----------------------------------------
std::cout << std::endl << "--- pass to function via move ---" << std::endl;
assignToTimeMachine(std::move(newOwner));
std::cout << " newOwner valid: " << (newOwner ? "true" : "false") << std::endl;
// ----------------------------------------
// reset() releases and replaces the managed object
// ----------------------------------------
std::cout << std::endl << "--- reset(): replace managed object ---" << std::endl;
std::cout << " Resetting kurisu, replacing with Shiina Mayuri." << std::endl;
kurisu.reset(new LabMember("Shiina Mayuri", 2, "Mayushii"));
printMember(kurisu);
// ----------------------------------------
// release() gives up ownership
// ----------------------------------------
std::cout << std::endl << "--- release(): give up ownership ---" << std::endl;
LabMember* released = kurisu.release();
std::cout << " kurisu valid: " << (kurisu ? "true" : "false") << std::endl;
std::cout << " via released: ";
released->introduce();
// Manual delete required after release()
delete released;
// ----------------------------------------
// vector manages multiple unique_ptrs
// ----------------------------------------
std::cout << std::endl << "--- vector<unique_ptr> management ---" << std::endl;
std::vector<std::unique_ptr<LabMember>> labMembers;
labMembers.push_back(std::make_unique<LabMember>("Amane Suzuha", 7, "Part-time warrior"));
labMembers.push_back(std::make_unique<LabMember>("Hashida Itaru", 3, "Daru"));
std::cout << std::endl;
std::cout << " Lab member list:" << std::endl;
for (int i = 0; i < (int)labMembers.size(); i++) {
std::cout << " ";
printMember(labMembers[i]);
}
std::cout << std::endl << "--- main ending. All members auto-freed when vector is destroyed ---" << std::endl;
return 0;
}
# Compile g++ -std=c++14 steinsgate_unique_ptr.cpp -o steinsgate_unique_ptr && ./steinsgate_unique_ptr [Alloc] Lab member #1 Okabe Rintaro allocated. [Alloc] Lab member #4 Makise Kurisu allocated. --- Introductions --- Lab member #1: Okabe Rintaro (Hououin Kyouma) Lab member #4: Makise Kurisu (Christina) --- get(): access raw pointer --- via rawPtr: Lab member #1: Okabe Rintaro (Hououin Kyouma) okabe still valid: true --- move(): transfer ownership --- okabe valid: false newOwner inherited: Lab member #1: Okabe Rintaro (Hououin Kyouma) --- pass to function via move --- Assigning Okabe Rintaro to the time machine. [Free] Lab member #1 Okabe Rintaro freed. newOwner valid: false --- reset(): replace managed object --- Resetting kurisu, replacing with Shiina Mayuri. [Free] Lab member #4 Makise Kurisu freed. [Alloc] Lab member #2 Shiina Mayuri allocated. Lab member #2: Shiina Mayuri (Mayushii) --- release(): give up ownership --- kurisu valid: false via released: Lab member #2: Shiina Mayuri (Mayushii) [Free] Lab member #2 Shiina Mayuri freed. --- vector<unique_ptr> management --- [Alloc] Lab member #7 Amane Suzuha allocated. [Alloc] Lab member #3 Hashida Itaru allocated. Lab member list: Lab member #7: Amane Suzuha (Part-time warrior) Lab member #3: Hashida Itaru (Daru) --- main ending. All members auto-freed when vector is destroyed --- [Free] Lab member #7 Amane Suzuha freed. [Free] Lab member #3 Hashida Itaru freed.
Common Mistakes
Copying a unique_ptr causes a compile error. unique_ptr has its copy constructor and copy assignment operator deleted to guarantee exclusive ownership. To transfer it to another variable, you must use std::move() to transfer ownership.
copy_error_ng.cpp — NG: causes a compile error
#include <memory>
#include <string>
int main() {
std::unique_ptr<std::string> p1 = std::make_unique<std::string>("Okabe Rintaro");
// Copying causes a compile error
std::unique_ptr<std::string> p2 = p1; // error: use of deleted function
return 0;
}
copy_ok.cpp — OK: use move to transfer ownership
#include <memory>
#include <string>
int main() {
std::unique_ptr<std::string> p1 = std::make_unique<std::string>("Okabe Rintaro");
// Use move to transfer ownership
std::unique_ptr<std::string> p2 = std::move(p1);
// p1 becomes nullptr after this
return 0;
}
Using unique_ptr<T[]> for arrays. When managing a dynamically allocated array, use unique_ptr<T[]>. The array version automatically calls delete[]. Use make_unique<T[N]> (C++14 and later) to specify the number of elements. All elements are automatically freed when the pointer goes out of scope.
array_unique_ptr.cpp
#include <iostream>
#include <memory>
#include <string>
int main() {
// Create array unique_ptr with make_unique (C++14 and later)
// Specify element count with make_unique<T[N]>
std::unique_ptr<std::string[]> labMembers = std::make_unique<std::string[]>(5);
// Access elements with subscript operator
labMembers[0] = "Okabe Rintaro";
labMembers[1] = "Makise Kurisu";
labMembers[2] = "Shiina Mayuri";
labMembers[3] = "Hashida Itaru";
labMembers[4] = "Amane Suzuha";
std::cout << "=== Future Gadget Lab Members ===" << std::endl;
for (int i = 0; i < 5; i++) {
std::cout << " Lab member #" << (i + 1) << ": " << labMembers[i] << std::endl;
}
// delete[] is called automatically when scope exits
return 0;
}
# Compile g++ -std=c++14 array_unique_ptr.cpp -o array_unique_ptr && ./array_unique_ptr === Future Gadget Lab Members === Lab member #1: Okabe Rintaro Lab member #2: Makise Kurisu Lab member #3: Shiina Mayuri Lab member #4: Hashida Itaru Lab member #5: Amane Suzuha
Overview
std::unique_ptr is a smart pointer that manages exclusive ownership of a dynamically allocated object. Only one unique_ptr holds ownership at any time, and when it goes out of scope, delete is automatically called to release memory. Copying is forbidden; to transfer ownership to another variable, use std::move(). After moving, the original pointer becomes nullptr. get() lets you retrieve the raw pointer to pass to legacy APIs, but note that ownership is not transferred. reset() can release the managed object or replace it with a different one. release() gives up ownership and returns the raw pointer, but since deallocation responsibility moves to the caller, its use cases are limited. Combining with std::vector lets you safely manage multiple objects, all of which are automatically freed when the vector is destroyed. When shared ownership is needed, consider shared_ptr.
If you find any errors or copyright issues, please contact us.