Skip to content

Commit 2357288

Browse files
feat: support mermaid codes render; advanced topic of pointer semantic (#28)
* fix as to the lightweight load mermiad * fix quality check
1 parent 47d37f2 commit 2357288

70 files changed

Lines changed: 3807 additions & 517 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
---
1919

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

2424
## 这是什么项目
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
cmake_minimum_required(VERSION 3.14)
2+
project(pointer-semantics-demo LANGUAGES CXX)
3+
4+
set(CMAKE_CXX_STANDARD 17)
5+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
6+
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
7+
8+
set(COMMON_FLAGS -Wall -Wextra -Wpedantic -g)
9+
10+
# 文章 01:Borrowed<T> 和 ObserverPtr<T>
11+
add_executable(test_borrowed_observer test_borrowed_observer.cpp)
12+
target_compile_options(test_borrowed_observer PRIVATE ${COMMON_FLAGS})
13+
14+
# 文章 02:UnsafeWeakPtr UB 演示
15+
add_executable(test_unsafe_ub test_unsafe_weak_ptr_ub.cpp)
16+
target_compile_options(test_unsafe_ub PRIVATE ${COMMON_FLAGS})
17+
18+
# 文章 02:UnsafeWeakPtr UB 演示(ASan 版本)
19+
add_executable(test_unsafe_ub_asan test_unsafe_weak_ptr_ub.cpp)
20+
target_compile_options(test_unsafe_ub_asan PRIVATE ${COMMON_FLAGS} -fsanitize=address,undefined)
21+
target_link_options(test_unsafe_ub_asan PRIVATE -fsanitize=address,undefined)
22+
23+
# 文章 03:SimpleWeakPtr 安全失效检测
24+
add_executable(test_simple_wp test_simple_weak_ptr.cpp)
25+
target_compile_options(test_simple_wp PRIVATE ${COMMON_FLAGS})
26+
27+
# 文章 04:Chrome-like WeakPtr 生命周期验证
28+
add_executable(test_lifetime test_lifetime_verification.cpp)
29+
target_compile_options(test_lifetime PRIVATE ${COMMON_FLAGS})
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include <cassert>
4+
#include <type_traits>
5+
6+
template <typename T> class Borrowed {
7+
public:
8+
explicit Borrowed(T& ref) noexcept : ptr_(&ref) {}
9+
10+
Borrowed(T&&) = delete;
11+
12+
Borrowed(std::nullptr_t) = delete;
13+
14+
explicit Borrowed(T* ptr) noexcept : ptr_(ptr) {
15+
assert(ptr != nullptr && "Borrowed<T> requires a non-null pointer");
16+
}
17+
18+
Borrowed(const Borrowed&) = default;
19+
Borrowed& operator=(const Borrowed&) = default;
20+
Borrowed(Borrowed&&) = default;
21+
Borrowed& operator=(Borrowed&&) = default;
22+
23+
T& get() const noexcept { return *ptr_; }
24+
T* operator->() const noexcept { return ptr_; }
25+
T& operator*() const noexcept { return *ptr_; }
26+
27+
template <typename U> operator Borrowed<const U>() const noexcept {
28+
return Borrowed<const U>(*ptr_);
29+
}
30+
31+
private:
32+
T* ptr_;
33+
};
34+
35+
template <typename T> Borrowed<T> borrow(T& ref) noexcept {
36+
return Borrowed<T>(ref);
37+
}
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#pragma once
2+
3+
#include <cstddef>
4+
5+
template <typename T> class ObserverPtr {
6+
public:
7+
ObserverPtr() noexcept : ptr_(nullptr) {}
8+
ObserverPtr(std::nullptr_t) noexcept : ptr_(nullptr) {}
9+
explicit ObserverPtr(T* ptr) noexcept : ptr_(ptr) {}
10+
11+
ObserverPtr(const ObserverPtr&) = default;
12+
ObserverPtr& operator=(const ObserverPtr&) = default;
13+
ObserverPtr(ObserverPtr&&) = default;
14+
ObserverPtr& operator=(ObserverPtr&&) = default;
15+
16+
void reset(T* ptr = nullptr) noexcept { ptr_ = ptr; }
17+
18+
T* release() noexcept {
19+
T* old = ptr_;
20+
ptr_ = nullptr;
21+
return old;
22+
}
23+
24+
T* get() const noexcept { return ptr_; }
25+
T& operator*() const noexcept { return *ptr_; }
26+
T* operator->() const noexcept { return ptr_; }
27+
28+
explicit operator bool() const noexcept { return ptr_ != nullptr; }
29+
30+
void swap(ObserverPtr& other) noexcept {
31+
T* tmp = ptr_;
32+
ptr_ = other.ptr_;
33+
other.ptr_ = tmp;
34+
}
35+
36+
private:
37+
T* ptr_;
38+
};
39+
40+
template <typename T, typename U>
41+
bool operator==(const ObserverPtr<T>& a, const ObserverPtr<U>& b) noexcept {
42+
return a.get() == b.get();
43+
}
44+
45+
template <typename T> bool operator==(const ObserverPtr<T>& a, std::nullptr_t) noexcept {
46+
return !a;
47+
}
48+
49+
template <typename T> ObserverPtr<T> make_observer(T* ptr) noexcept {
50+
return ObserverPtr<T>(ptr);
51+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#pragma once
2+
3+
#include <atomic>
4+
#include <memory>
5+
6+
struct AtomicFlag {
7+
std::atomic<bool> alive{true};
8+
9+
void invalidate() { alive.store(false, std::memory_order_release); }
10+
bool is_alive() const { return alive.load(std::memory_order_acquire); }
11+
};
12+
13+
template <typename T> class SimpleWeakPtr {
14+
public:
15+
SimpleWeakPtr() = default;
16+
17+
SimpleWeakPtr(T* ptr, std::shared_ptr<AtomicFlag> flag) : ptr_(ptr), flag_(std::move(flag)) {}
18+
19+
bool is_valid() const { return flag_ && flag_->is_alive(); }
20+
21+
T* get() const {
22+
if (is_valid()) {
23+
return ptr_;
24+
}
25+
return nullptr;
26+
}
27+
28+
T& operator*() const { return *get(); }
29+
T* operator->() const { return get(); }
30+
explicit operator bool() const { return get() != nullptr; }
31+
32+
private:
33+
T* ptr_ = nullptr;
34+
std::shared_ptr<AtomicFlag> flag_;
35+
};
36+
37+
template <typename T> class SimpleWeakPtrFactory {
38+
public:
39+
explicit SimpleWeakPtrFactory(T* owner)
40+
: owner_(owner), flag_(std::make_shared<AtomicFlag>()) {}
41+
42+
SimpleWeakPtrFactory(const SimpleWeakPtrFactory&) = delete;
43+
SimpleWeakPtrFactory& operator=(const SimpleWeakPtrFactory&) = delete;
44+
45+
SimpleWeakPtr<T> get_weak_ptr() { return SimpleWeakPtr<T>(owner_, flag_); }
46+
47+
void invalidate() {
48+
if (flag_) {
49+
flag_->invalidate();
50+
}
51+
}
52+
53+
~SimpleWeakPtrFactory() { invalidate(); }
54+
55+
private:
56+
T* owner_;
57+
std::shared_ptr<AtomicFlag> flag_;
58+
};
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// 文章 01 测试:Borrowed<T> 和 ObserverPtr<T> 基本用法
2+
// 编译:g++ -std=c++17 -Wall -Wextra -g -o test_borrowed_observer test_borrowed_observer.cpp
3+
4+
#include "borrowed.h"
5+
#include "observer_ptr.h"
6+
#include <iostream>
7+
#include <string>
8+
#include <vector>
9+
10+
void test_borrowed() {
11+
std::cout << "=== Borrowed<T> tests ===\n";
12+
13+
int x = 42;
14+
auto b = borrow(x);
15+
std::cout << " borrow(x) = " << b.get() << " (expect 42)\n";
16+
17+
// 从指针构造(非空)
18+
Borrowed<int> bp(&x);
19+
std::cout << " Borrowed<int>(&x) = " << bp.get() << " (expect 42)\n";
20+
21+
// const 转换
22+
Borrowed<const int> bc = bp;
23+
std::cout << " const conversion: " << bc.get() << " (expect 42)\n";
24+
25+
// 函数参数用法
26+
auto process = [](Borrowed<const std::vector<int>> data) { return data.get().size(); };
27+
std::vector<int> v{1, 2, 3};
28+
std::cout << " process(borrow(v)) = " << process(borrow(v)) << " (expect 3)\n";
29+
30+
std::cout << " sizeof(Borrowed<int>) = " << sizeof(Borrowed<int>) << " (expect 8)\n\n";
31+
}
32+
33+
void test_observer_ptr() {
34+
std::cout << "=== ObserverPtr<T> tests ===\n";
35+
36+
int a = 10, b = 20;
37+
auto obs = make_observer(&a);
38+
std::cout << " make_observer(&a): " << *obs << " (expect 10)\n";
39+
40+
// operator bool
41+
ObserverPtr<int> null_obs;
42+
std::cout << " default constructed: " << (null_obs ? "non-null" : "null")
43+
<< " (expect null)\n";
44+
std::cout << " with value: " << (obs ? "non-null" : "null") << " (expect non-null)\n";
45+
46+
// reset and release
47+
obs.reset(&b);
48+
std::cout << " after reset(&b): " << *obs << " (expect 20)\n";
49+
int* released = obs.release();
50+
std::cout << " after release: obs=" << (obs ? "non-null" : "null") << " released=" << *released
51+
<< " (expect null, 20)\n";
52+
53+
std::cout << " sizeof(ObserverPtr<int>) = " << sizeof(ObserverPtr<int>) << " (expect 8)\n\n";
54+
}
55+
56+
int main() {
57+
test_borrowed();
58+
test_observer_ptr();
59+
return 0;
60+
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
// 生命周期验证测试:Chrome-like WeakPtr vs UnsafeWeakPtr
2+
// 编译:g++ -std=c++17 -O0 -g -o test_lifetime test_lifetime_verification.cpp
3+
4+
#include "weak_ptr_factory.h"
5+
#include <iostream>
6+
#include <memory>
7+
8+
// ===== Chrome-like WeakPtr 测试 =====
9+
10+
class Session {
11+
public:
12+
explicit Session(int id) : id_(id) { std::cout << "Session(" << id_ << ") constructed\n"; }
13+
14+
~Session() { std::cout << "Session(" << id_ << ") destroyed\n"; }
15+
16+
WeakPtr<Session> get_weak_ptr() { return factory_.get_weak_ptr(); }
17+
18+
void do_work() const { std::cout << "Session(" << id_ << ") doing work\n"; }
19+
20+
int id() const { return id_; }
21+
22+
private:
23+
int id_;
24+
// Factory 放在最后——确保在其他成员析构之前 invalidate
25+
WeakPtrFactory<Session> factory_{this};
26+
};
27+
28+
void test_weak_ptr_survives_owner() {
29+
std::cout << "=== Test: WeakPtr survives Owner ===\n";
30+
31+
WeakPtr<Session> weak = [] {
32+
auto s = std::make_unique<Session>(42);
33+
auto w = s->get_weak_ptr();
34+
std::cout << " Before destroy: valid = " << std::boolalpha << w.is_valid() << "\n";
35+
return w;
36+
// Session 在这里析构
37+
}();
38+
39+
std::cout << " After destroy: valid = " << std::boolalpha << weak.is_valid() << "\n";
40+
std::cout << " get() returns: " << (weak.get() ? "non-null (BAD)" : "nullptr (GOOD)")
41+
<< "\n\n";
42+
}
43+
44+
void test_multiple_weak_ptrs() {
45+
std::cout << "=== Test: Multiple WeakPtrs ===\n";
46+
47+
auto s = std::make_unique<Session>(99);
48+
auto w1 = s->get_weak_ptr();
49+
auto w2 = s->get_weak_ptr();
50+
auto w3 = w1;
51+
52+
std::cout << " All valid: " << w1.is_valid() << " " << w2.is_valid() << " " << w3.is_valid()
53+
<< "\n";
54+
55+
s.reset();
56+
57+
std::cout << " After destroy: " << w1.is_valid() << " " << w2.is_valid() << " "
58+
<< w3.is_valid() << "\n\n";
59+
}
60+
61+
void test_weak_ptr_in_callback() {
62+
std::cout << "=== Test: WeakPtr in simulated callback ===\n";
63+
64+
// 模拟异步回调场景
65+
WeakPtr<Session> weak;
66+
67+
{
68+
auto s = std::make_unique<Session>(7);
69+
weak = s->get_weak_ptr();
70+
71+
// 模拟回调执行时对象还活着
72+
if (auto* self = weak.get()) {
73+
self->do_work(); // 安全
74+
}
75+
}
76+
77+
// 模拟回调执行时对象已销毁
78+
if (auto* self = weak.get()) {
79+
self->do_work(); // 不会执行——get() 返回 nullptr
80+
} else {
81+
std::cout << " Callback skipped: object already destroyed (GOOD)\n\n";
82+
}
83+
}
84+
85+
int main() {
86+
test_weak_ptr_survives_owner();
87+
test_multiple_weak_ptrs();
88+
test_weak_ptr_in_callback();
89+
return 0;
90+
}

0 commit comments

Comments
 (0)