See this page as a slide show
CS253 Smart Pointers
Overview
- Smart pointers typically help you to manage memory.
- You don’t have to remember to
delete
the memory.
- Remember, no garbage collection!
- Even if you remember to
delete
the memory,
an exception might prevent that from happening.
The Problem
#include "Loud.h"
int main(int argc, char **) {
auto p = new Loud;
if (argc < 5) {
cerr << "Not enough arguments.\n";
return 1;
}
delete p;
return 0;
}
Loud::Loud()
Not enough arguments.
- Should have put it on the stack.
- Repeat to yourself, “It’s just an example, I should really just relax.”
- The memory never got freed!
- I could fix it by duplicating
delete p
, but that violates DRY.
- What if some deep function throws an exception?
Three kinds of smart pointers
unique_ptr
if the thing has one owner
shared_ptr
if the thing has several owners
weak_ptr
for voyeurs & lurkers
auto_ptr
for people who use obsolete features
auto_ptr
- An old solution is
auto_ptr
.
- It’s faulty.
auto_ptr
is deprecated.
- “Deprecated” means that it still exists, but its use is discouraged.
You’ve received fair notice that it will go away someday.
- Don’t use it.
unique_ptr
unique_ptr
is built-in RAII.
- A
unique_ptr
“owns” the pointed-to object.
- When the
unique_ptr
is destroyed (usually, by falling out of scope)
then the pointed-to objected is destroyed, as well.
- You cannot copy a
unique_ptr
. Which one would be the unique owner?
- No space overhead, and practically no time overhead.
unique_ptr
example #1
#include "Loud.h"
int main(int argc, char **) {
unique_ptr<Loud> p(new Loud);
if (argc < 5) {
cerr << "Not enough arguments.\n";
return 1;
}
// don’t need to delete
return 0;
}
Loud::Loud()
Not enough arguments.
Loud::~Loud()
unique_ptr example #2
#include "Loud.h"
void foo() {
unique_ptr<Loud> p(new Loud);
throw "I am unhappy";
}
int main() {
try {
foo();
}
catch (const char *p) {
cerr << p << '\n';
}
return 0;
}
Loud::Loud()
Loud::~Loud()
I am unhappy
shared_ptr
shared_ptr
is built-in RAII.
- A
shared_ptr
is a “counting pointer”. It keeps track of how many
shared owners this object has, via a counter.
- A new owner (via assignment) increments the counter.
- When any owner is destroyed, the counter decrements.
- When the counter goes to zero, there are no more owners,
so the object itself is destroyed.
- You can assign a
shared_ptr
. It just increments the use count.
shared_ptr
example
#include "Loud.h"
int main() {
shared_ptr<Loud> p(new Loud);
cout << p.use_count() << '\n';
{
cout << p.use_count() << '\n';
auto q=p;
cout << p.use_count() << '\n';
cout << q.use_count() << '\n';
}
cout << p.use_count() << '\n';
}
Loud::Loud()
1
1
2
2
1
Loud::~Loud()
Weak Pointers
- With a
unique_ptr
, there’s only a single owner.
When the owner is destroyed, the dynamic object is destroyed.
- A single person has a contract with Comcast, an ISP.
- The person moves away, the contract is terminated.
Weak Pointers
- With a
shared_ptr
, there are multiple owners.
If any of the owners are destroyed, the shared object still exists,
as long as any owners remain.
- A family shares their internet service.
- Junior goes off to college; parents keep using the internet.
Weak Pointers
- What if we want to play the multiple-owner game, but not contribute
any ownership?
- I steal my neighbor’s Wi-Fi.
- I die of a ketchup-related incident; no effect on the internet service.
weak_ptr
example
#include <iostream>
#include <memory>
using namespace std;
weak_ptr<int> wp;
void observe() {
cout << "use_count=" << wp.use_count() << ": ";
if (auto sp = wp.lock())
cout << *sp << '\n';
else
cout << "wp is expired\n";
}
int main() {
{
shared_ptr<int> mem(new int(42));
wp = mem;
observe();
}
observe();
}
use_count=1: 42
use_count=0: wp is expired
weak_ptr
: why?
- Why would you want a
weak_ptr
?
- Cached data: if you held a
shared_ptr
to cached data,
then it would never get freed, because you own it.
This way, you can use it if it’s still around.
- Avoiding circular dependencies, e.g., a doubly-linked list.