Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
---

<!-- COVERAGE_START -->
![English Coverage](https://img.shields.io/badge/en_coverage-100%25-green.svg) 403/403 docs translated
![English Coverage](https://img.shields.io/badge/en_coverage-98%25-green.svg) 403/411 docs translated
<!-- COVERAGE_END -->

## 这是什么项目
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
cmake_minimum_required(VERSION 3.14)
project(pointer-semantics-demo LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

set(COMMON_FLAGS -Wall -Wextra -Wpedantic -g)

# 文章 01:Borrowed<T> 和 ObserverPtr<T>
add_executable(test_borrowed_observer test_borrowed_observer.cpp)
target_compile_options(test_borrowed_observer PRIVATE ${COMMON_FLAGS})

# 文章 02:UnsafeWeakPtr UB 演示
add_executable(test_unsafe_ub test_unsafe_weak_ptr_ub.cpp)
target_compile_options(test_unsafe_ub PRIVATE ${COMMON_FLAGS})

# 文章 02:UnsafeWeakPtr UB 演示(ASan 版本)
add_executable(test_unsafe_ub_asan test_unsafe_weak_ptr_ub.cpp)
target_compile_options(test_unsafe_ub_asan PRIVATE ${COMMON_FLAGS} -fsanitize=address,undefined)
target_link_options(test_unsafe_ub_asan PRIVATE -fsanitize=address,undefined)

# 文章 03:SimpleWeakPtr 安全失效检测
add_executable(test_simple_wp test_simple_weak_ptr.cpp)
target_compile_options(test_simple_wp PRIVATE ${COMMON_FLAGS})

# 文章 04:Chrome-like WeakPtr 生命周期验证
add_executable(test_lifetime test_lifetime_verification.cpp)
target_compile_options(test_lifetime PRIVATE ${COMMON_FLAGS})
37 changes: 37 additions & 0 deletions code/volumn_codes/vol8/cpp-deep-dives/pointer-semantics/borrowed.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#pragma once

#include <cassert>
#include <type_traits>

template <typename T> class Borrowed {
public:
explicit Borrowed(T& ref) noexcept : ptr_(&ref) {}

Borrowed(T&&) = delete;

Borrowed(std::nullptr_t) = delete;

explicit Borrowed(T* ptr) noexcept : ptr_(ptr) {
assert(ptr != nullptr && "Borrowed<T> requires a non-null pointer");
}

Borrowed(const Borrowed&) = default;
Borrowed& operator=(const Borrowed&) = default;
Borrowed(Borrowed&&) = default;
Borrowed& operator=(Borrowed&&) = default;

T& get() const noexcept { return *ptr_; }
T* operator->() const noexcept { return ptr_; }
T& operator*() const noexcept { return *ptr_; }

template <typename U> operator Borrowed<const U>() const noexcept {
return Borrowed<const U>(*ptr_);
}

private:
T* ptr_;
};

template <typename T> Borrowed<T> borrow(T& ref) noexcept {
return Borrowed<T>(ref);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#pragma once

#include <cstddef>

template <typename T> class ObserverPtr {
public:
ObserverPtr() noexcept : ptr_(nullptr) {}
ObserverPtr(std::nullptr_t) noexcept : ptr_(nullptr) {}
explicit ObserverPtr(T* ptr) noexcept : ptr_(ptr) {}

ObserverPtr(const ObserverPtr&) = default;
ObserverPtr& operator=(const ObserverPtr&) = default;
ObserverPtr(ObserverPtr&&) = default;
ObserverPtr& operator=(ObserverPtr&&) = default;

void reset(T* ptr = nullptr) noexcept { ptr_ = ptr; }

T* release() noexcept {
T* old = ptr_;
ptr_ = nullptr;
return old;
}

T* get() const noexcept { return ptr_; }
T& operator*() const noexcept { return *ptr_; }
T* operator->() const noexcept { return ptr_; }

explicit operator bool() const noexcept { return ptr_ != nullptr; }

void swap(ObserverPtr& other) noexcept {
T* tmp = ptr_;
ptr_ = other.ptr_;
other.ptr_ = tmp;
}

private:
T* ptr_;
};

template <typename T, typename U>
bool operator==(const ObserverPtr<T>& a, const ObserverPtr<U>& b) noexcept {
return a.get() == b.get();
}

template <typename T> bool operator==(const ObserverPtr<T>& a, std::nullptr_t) noexcept {
return !a;
}

template <typename T> ObserverPtr<T> make_observer(T* ptr) noexcept {
return ObserverPtr<T>(ptr);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#pragma once

#include <atomic>
#include <memory>

struct AtomicFlag {
std::atomic<bool> alive{true};

void invalidate() { alive.store(false, std::memory_order_release); }
bool is_alive() const { return alive.load(std::memory_order_acquire); }
};

template <typename T> class SimpleWeakPtr {
public:
SimpleWeakPtr() = default;

SimpleWeakPtr(T* ptr, std::shared_ptr<AtomicFlag> flag) : ptr_(ptr), flag_(std::move(flag)) {}

bool is_valid() const { return flag_ && flag_->is_alive(); }

T* get() const {
if (is_valid()) {
return ptr_;
}
return nullptr;
}

T& operator*() const { return *get(); }
T* operator->() const { return get(); }
explicit operator bool() const { return get() != nullptr; }

private:
T* ptr_ = nullptr;
std::shared_ptr<AtomicFlag> flag_;
};

template <typename T> class SimpleWeakPtrFactory {
public:
explicit SimpleWeakPtrFactory(T* owner)
: owner_(owner), flag_(std::make_shared<AtomicFlag>()) {}

SimpleWeakPtrFactory(const SimpleWeakPtrFactory&) = delete;
SimpleWeakPtrFactory& operator=(const SimpleWeakPtrFactory&) = delete;

SimpleWeakPtr<T> get_weak_ptr() { return SimpleWeakPtr<T>(owner_, flag_); }

void invalidate() {
if (flag_) {
flag_->invalidate();
}
}

~SimpleWeakPtrFactory() { invalidate(); }

private:
T* owner_;
std::shared_ptr<AtomicFlag> flag_;
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// 文章 01 测试:Borrowed<T> 和 ObserverPtr<T> 基本用法
// 编译:g++ -std=c++17 -Wall -Wextra -g -o test_borrowed_observer test_borrowed_observer.cpp

#include "borrowed.h"
#include "observer_ptr.h"
#include <iostream>
#include <string>
#include <vector>

void test_borrowed() {
std::cout << "=== Borrowed<T> tests ===\n";

int x = 42;
auto b = borrow(x);
std::cout << " borrow(x) = " << b.get() << " (expect 42)\n";

// 从指针构造(非空)
Borrowed<int> bp(&x);
std::cout << " Borrowed<int>(&x) = " << bp.get() << " (expect 42)\n";

// const 转换
Borrowed<const int> bc = bp;
std::cout << " const conversion: " << bc.get() << " (expect 42)\n";

// 函数参数用法
auto process = [](Borrowed<const std::vector<int>> data) { return data.get().size(); };
std::vector<int> v{1, 2, 3};
std::cout << " process(borrow(v)) = " << process(borrow(v)) << " (expect 3)\n";

std::cout << " sizeof(Borrowed<int>) = " << sizeof(Borrowed<int>) << " (expect 8)\n\n";
}

void test_observer_ptr() {
std::cout << "=== ObserverPtr<T> tests ===\n";

int a = 10, b = 20;
auto obs = make_observer(&a);
std::cout << " make_observer(&a): " << *obs << " (expect 10)\n";

// operator bool
ObserverPtr<int> null_obs;
std::cout << " default constructed: " << (null_obs ? "non-null" : "null")
<< " (expect null)\n";
std::cout << " with value: " << (obs ? "non-null" : "null") << " (expect non-null)\n";

// reset and release
obs.reset(&b);
std::cout << " after reset(&b): " << *obs << " (expect 20)\n";
int* released = obs.release();
std::cout << " after release: obs=" << (obs ? "non-null" : "null") << " released=" << *released
<< " (expect null, 20)\n";

std::cout << " sizeof(ObserverPtr<int>) = " << sizeof(ObserverPtr<int>) << " (expect 8)\n\n";
}

int main() {
test_borrowed();
test_observer_ptr();
return 0;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
// 生命周期验证测试:Chrome-like WeakPtr vs UnsafeWeakPtr
// 编译:g++ -std=c++17 -O0 -g -o test_lifetime test_lifetime_verification.cpp

#include "weak_ptr_factory.h"
#include <iostream>
#include <memory>

// ===== Chrome-like WeakPtr 测试 =====

class Session {
public:
explicit Session(int id) : id_(id) { std::cout << "Session(" << id_ << ") constructed\n"; }

~Session() { std::cout << "Session(" << id_ << ") destroyed\n"; }

WeakPtr<Session> get_weak_ptr() { return factory_.get_weak_ptr(); }

void do_work() const { std::cout << "Session(" << id_ << ") doing work\n"; }

int id() const { return id_; }

private:
int id_;
// Factory 放在最后——确保在其他成员析构之前 invalidate
WeakPtrFactory<Session> factory_{this};
};

void test_weak_ptr_survives_owner() {
std::cout << "=== Test: WeakPtr survives Owner ===\n";

WeakPtr<Session> weak = [] {
auto s = std::make_unique<Session>(42);
auto w = s->get_weak_ptr();
std::cout << " Before destroy: valid = " << std::boolalpha << w.is_valid() << "\n";
return w;
// Session 在这里析构
}();

std::cout << " After destroy: valid = " << std::boolalpha << weak.is_valid() << "\n";
std::cout << " get() returns: " << (weak.get() ? "non-null (BAD)" : "nullptr (GOOD)")
<< "\n\n";
}

void test_multiple_weak_ptrs() {
std::cout << "=== Test: Multiple WeakPtrs ===\n";

auto s = std::make_unique<Session>(99);
auto w1 = s->get_weak_ptr();
auto w2 = s->get_weak_ptr();
auto w3 = w1;

std::cout << " All valid: " << w1.is_valid() << " " << w2.is_valid() << " " << w3.is_valid()
<< "\n";

s.reset();

std::cout << " After destroy: " << w1.is_valid() << " " << w2.is_valid() << " "
<< w3.is_valid() << "\n\n";
}

void test_weak_ptr_in_callback() {
std::cout << "=== Test: WeakPtr in simulated callback ===\n";

// 模拟异步回调场景
WeakPtr<Session> weak;

{
auto s = std::make_unique<Session>(7);
weak = s->get_weak_ptr();

// 模拟回调执行时对象还活着
if (auto* self = weak.get()) {
self->do_work(); // 安全
}
}

// 模拟回调执行时对象已销毁
if (auto* self = weak.get()) {
self->do_work(); // 不会执行——get() 返回 nullptr
} else {
std::cout << " Callback skipped: object already destroyed (GOOD)\n\n";
}
}

int main() {
test_weak_ptr_survives_owner();
test_multiple_weak_ptrs();
test_weak_ptr_in_callback();
return 0;
}
Loading
Loading