ch14/buffer5.hpp40 lines
// buffer5.hpp: stn::Buffer with the rule of five
#pragma once
#include <algorithm>
#include <cstddef>
#include <utility>
namespace stn {
class Buffer {
public:
explicit Buffer(std::size_t n)
: data_(new double[n]{}), size_(n) {}
~Buffer() { delete[] data_; } // 1
Buffer(const Buffer& other) // 2
: data_(new double[other.size_]), size_(other.size_) {
std::copy(other.data_, other.data_ + size_, data_);
}
Buffer& operator=(const Buffer& other) { // 3
Buffer tmp(other); // copy first; may throw
swap(tmp); // then swap; cannot throw
return *this; // tmp frees the old array
}
Buffer(Buffer&& other) noexcept // 4
: data_(std::exchange(other.data_, nullptr)),
size_(std::exchange(other.size_, 0)) {}
Buffer& operator=(Buffer&& other) noexcept { // 5
Buffer tmp(std::move(other));
swap(tmp);
return *this;
}
void swap(Buffer& other) noexcept {
std::swap(data_, other.data_);
std::swap(size_, other.size_);
}
double& operator[](std::size_t i) { return data_[i]; }
std::size_t size() const { return size_; }
private:
double* data_; // owner of the heap array, or nullptr
std::size_t size_;
};
} // namespace stn
ch14/buffer5_test.cpp25 lines
// buffer5_test.cpp: exercise copy, self-assignment and move
#include "buffer5.hpp"
#include <cstdio>
#include <utility>
int main() {
stn::Buffer a(4);
a[0] = 21.75;
stn::Buffer b = a; // copy ctor: deep copy
b[0] = -3.5;
std::printf("a[0]=%.2f b[0]=%.2f\n", a[0], b[0]);
stn::Buffer& same = a;
a = same; // self-assignment via alias
std::printf("after a = same: a[0]=%.2f size=%zu\n",
a[0], a.size());
stn::Buffer c = std::move(a); // move ctor: steal
std::printf("c[0]=%.2f c.size=%zu a.size=%zu\n",
c[0], c.size(), a.size());
b = std::move(c); // move assign
std::printf("b[0]=%.2f c.size=%zu\n", b[0], c.size());
return 0;
}
ch14/copy_vs_move.cpp35 lines
// copy_vs_move.cpp: time a deep copy against a move
#include "buffer5.hpp"
#include <chrono>
#include <cstdio>
#include <utility>
using Clock = std::chrono::steady_clock;
static double us_since(Clock::time_point t0) {
return std::chrono::duration<double, std::micro>(Clock::now() - t0)
.count();
}
int main() {
const int reps = 20;
std::printf("%10s %12s %12s\n", "doubles", "copy (us)", "move (us)");
for (std::size_t n = 1000; n <= 10000000; n *= 10) {
stn::Buffer src(n);
src[n - 1] = 1.0;
double copy_us = 0.0, move_us = 0.0, check = 0.0;
for (int r = 0; r < reps; ++r) {
auto t0 = Clock::now();
stn::Buffer c = src; // deep copy
copy_us += us_since(t0);
t0 = Clock::now();
stn::Buffer m = std::move(c); // pointer steal
move_us += us_since(t0);
check += m[n - 1]; // keep the work alive
}
std::printf("%10zu %12.2f %12.3f\n", n, copy_us / reps,
move_us / reps);
if (check != reps) return 1;
}
return 0;
}
ch14/cycle_weak.cpp44 lines
// cycle_weak.cpp: a weak_ptr back-pointer breaks the cycle
#include <cstdio>
#include <memory>
#include <vector>
struct Station;
struct Sensor {
int id;
std::weak_ptr<Station> home; // observes; does not own
explicit Sensor(int i) : id(i) {}
~Sensor() { std::printf("~Sensor %d\n", id); }
void report() const {
if (auto st = home.lock()) { // a shared_ptr, or empty
std::printf("sensor %d: station alive\n", id);
} else {
std::printf("sensor %d: station gone\n", id);
}
}
};
struct Station {
std::vector<std::shared_ptr<Sensor>> sensors;
~Station() { std::printf("~Station\n"); }
};
std::shared_ptr<Sensor> build_station() {
auto st = std::make_shared<Station>();
for (int id = 1001; id <= 1002; ++id) {
auto s = std::make_shared<Sensor>(id);
s->home = st; // sensor -> station
st->sensors.push_back(s); // station -> sensor
}
std::printf("station use_count = %ld\n", st.use_count());
st->sensors[0]->report();
return st->sensors[0]; // caller shares sensor 1001
} // st dies; count drops to 0
int main() {
auto kept = build_station();
std::printf("back in main\n");
kept->report();
return 0;
}
ch14/mixed_delete.cpp26 lines
// mixed_delete.cpp: a raw delete on memory a unique_ptr owns
#include <cstdint>
#include <cstdio>
#include <memory>
namespace stn {
struct Reading {
std::uint32_t sensor_id;
std::int64_t time_s;
double value;
std::uint8_t flags;
};
} // namespace stn
void print_and_release(stn::Reading* r) { // agent's helper
std::printf("%u at %lld: %.2f\n", r->sensor_id,
static_cast<long long>(r->time_s), r->value);
delete r; // "clean up when done"
}
int main() {
auto r = std::make_unique<stn::Reading>(
stn::Reading{1001, 60, 21.75, 0x01});
print_and_release(r.get());
return 0; // ~unique_ptr deletes again
}
ch14/observer.cpp20 lines
// observer.cpp: owners and a raw observer
#include <cstdio>
#include <memory>
#include <utility>
#include <vector>
struct Sensor { int id; double last; };
double latest(const Sensor* s) { // observer
return s ? s->last : 0.0; // never deletes
}
int main() {
std::vector<std::unique_ptr<Sensor>> owned; // the owners
auto s = std::make_unique<Sensor>(Sensor{1001, 21.75});
owned.push_back(std::move(s));
const Sensor* watch = owned[0].get(); // borrow
std::printf("%.2f\n", latest(watch)); // 21.75
return 0;
}
ch14/reading_log.hpp40 lines
// reading_log.hpp: the agent-written class to audit
#pragma once
#include <cstdint>
#include <cstring>
namespace stn {
struct Reading { std::uint32_t sensor_id; std::int64_t time_s;
double value; std::uint8_t flags; };
class ReadingLog {
public:
ReadingLog(const char* name, std::size_t cap)
: name_(new char[std::strlen(name) + 1]),
items_(new Reading[cap]), count_(0), cap_(cap) {
std::strcpy(name_, name);
}
~ReadingLog() { delete[] name_; delete items_; }
ReadingLog(const ReadingLog& o)
: name_(o.name_), items_(new Reading[o.cap_]),
count_(o.count_), cap_(o.cap_) {
for (std::size_t i = 0; i < count_; ++i)
items_[i] = o.items_[i];
}
ReadingLog& operator=(const ReadingLog& o) {
delete[] items_;
items_ = new Reading[o.cap_];
for (std::size_t i = 0; i < o.count_; ++i)
items_[i] = o.items_[i];
count_ = o.count_; cap_ = o.cap_;
return *this;
}
bool add(const Reading& r) {
if (count_ == cap_) return false;
items_[count_++] = r; return true;
}
std::size_t count() const { return count_; }
const Reading& at(std::size_t i) const { return items_[i]; }
private:
char* name_; Reading* items_;
std::size_t count_, cap_;
};
} // namespace stn
ch14/self_assign_bad.cpp34 lines
// self_assign_bad.cpp: a copy assignment that frees first
#include <algorithm>
#include <cstddef>
#include <cstdio>
namespace stn {
class Buffer {
public:
explicit Buffer(std::size_t n)
: data_(new double[n]{}), size_(n) {}
~Buffer() { delete[] data_; }
Buffer& operator=(const Buffer& other) {
delete[] data_; // free the old array
double* fresh = new double[other.size_];
std::copy(other.data_, other.data_ + other.size_, fresh);
data_ = fresh;
size_ = other.size_;
return *this;
}
double& operator[](std::size_t i) { return data_[i]; }
private:
double* data_;
std::size_t size_;
};
} // namespace stn
int main() {
stn::Buffer a(4);
a[0] = 21.75;
stn::Buffer& same = a;
a = same; // self-assignment via alias
std::printf("a[0] = %.2f\n", a[0]);
return 0;
}