Author: Hayden Smith
Why?
What?
// myintpointer.h
class MyIntPointer {
public:
// This is the constructor
MyIntPointer(int* value);
// This is the destructor
~MyIntPointer();
int* GetValue();
private:
int* value_;
};
// myintpointer.cpp
#include "myintpointer.h"
MyIntPointer::MyIntPointer(int* value): value_{value} {}
int* MyIntPointer::GetValue() {
return value_
}
MyIntPointer::~MyIntPointer() {
// Similar to C's free function.
delete value_;
}
Don't use the new / delete keyword in your own code
We are showing for demonstration purposes
void fn() {
// Similar to C's malloc
MyIntPointer p{new int{5}};
// Copy the pointer;
MyIntPointer q{p.GetValue()};
// p and q are both now destructed.
// What happens?
}
demo551-safepointer.cpp
Type | Shared ownership | Take ownership |
---|---|---|
std::unique_ptr<T> | No | Yes |
raw pointers | No | No |
std::shared_ptr<T> | Yes | Yes |
std::weak_ptr<T> | No | No |
Also note the use of 'nullptr' in C++ instead of NULL
#include <memory>
#include <iostream>
int main() {
auto up1 = std::unique_ptr<int>{new int};
auto up2 = up1; // no copy constructor
std::unique_ptr<int> up3;
up3 = up2; // no copy assignment
up3.reset(up1.release()); // OK
auto up4 = std::move(up3); // OK
std::cout << up4.get() << "\n";
std::cout << *up4 << "\n";
std::cout << *up1 << "\n";
}
demo552-unique1.cpp
#include <memory>
#include <iostream>
int main() {
auto up1 = std::unique_ptr<int>{new int{0}};
*up1 = 5;
std::cout << *up1 << "\n";
auto op1 = up1.get();
*op1 = 6;
std::cout << *op1 << "\n";
up1.reset();
std::cout << *op1 << "\n";
}
Can we remove "new" completely?
demo553-observer.cpp
#include <iostream>
#include <memory>
auto main() -> int {
// 1 - Worst - you can accidentally own the resource multiple
// times, or easily forget to own it.
// auto* silly_string = new std::string{"Hi"};
// auto up1 = std::unique_ptr<std::string>(silly_string);
// auto up11 = std::unique_ptr<std::string>(silly_string);
// 2 - Not good - requires actual thinking about whether there's a leak.
auto up2 = std::unique_ptr<std::string>(new std::string("Hello"));
// 3 - Good - no thinking required.
auto up3 = std::make_unique<std::string>("Hello");
std::cout << *up2 << "\n";
std::cout << *up3 << "\n";
// std::cout << *(up3.get()) << "\n";
// std::cout << up3->size();
}
This method avoids the need for "new". It has other benefits that we will explore.
demo554-unique2.cpp
#include <iostream>
#include <memory>
auto main() -> int {
auto x = std::make_shared<int>(5);
std::cout << "use count: " << x.use_count() << "\n";
std::cout << "value: " << *x << "\n";
x.reset(); // Memory still exists, due to y.
std::cout << "use count: " << y.use_count() << "\n";
std::cout << "value: " << *y << "\n";
y.reset(); // Deletes the memory, since
// no one else owns the memory
std::cout << "use count: " << x.use_count() << "\n";
std::cout << "value: " << *y << "\n";
}
Can we remove "new" completely?
demo555-shared.cpp
#include <iostream>
#include <memory>
auto main() -> int {
auto x = std::make_shared<int>(1);
auto wp = std::weak_ptr<int>(x); // x owns the memory
auto y = wp.lock();
if (y != nullptr) { // x and y own the memory
// Do something with y
std::cout << "Attempt 1: " << *y << '\n';
}
}
demo556-weak.cpp
void g() {
throw std::runtime_error{""};
}
int main() {
auto ptr = new int{5};
g();
// Never executed.
delete ptr;
}
void g() {
throw std::runtime_error{""};
}
int main() {
auto ptr = std::make_unique<int>(5);
g();
}
Not safe
Safe
void g() {
throw std::runtime_error{""};
}
int main() {
auto ptr = new int{5};
g();
auto uni = std::unique_ptr<int>(ptr);
}
Not safe
#include <exception>
class my_int {
public:
my_int(int const i) : i_{i} {
if (i == 2) {
throw std::exception();
}
}
private:
int i_;
};
class unsafe_class {
public:
unsafe_class(int a, int b)
: a_{new my_int{a}}
, b_{new my_int{b}}
{}
~unsafe_class() {
delete a_;
delete b_;
}
private:
my_int* a_;
my_int* b_;
};
int main() {
auto a = unsafe_class(1, 2);
}
Spot the bug
demo557-bad.cpp
#include <exception>
#include <memory>
class my_int {
public:
my_int(int const i)
: i_{i} {
if (i == 2) {
throw std::exception();
}
}
private:
int i_;
};
class safe_class {
public:
safe_class(int a, int b)
: a_(std::make_unique<my_int>(a))
, b_(std::make_unique<my_int>(b))
{}
private:
std::unique_ptr<my_int> a_;
std::unique_ptr<my_int> b_;
};
int main() {
auto a = safe_class(1, 2);
}
demo558-partial1.cpp