pdfium 中 retain_ptr.h 介绍与使用
摘要 RetainPtr 是一个基于引用计数的智能指针系统,实现了类似 scoped_refptr 的功能。其核心特点包括: 使用 std::unique_ptr 管理指针,但自定义删除器调用 Release() 而非 delete 实现了完整的构造/析构、拷贝/移动语义,支持类型转换 提供了 Reset()、Leak()/Unleak() 等指针管理方法 重载了各种操作符,使智能指针使用更自然
·
📚 RetainPtr 智能指针系统详解
文章目录
// Copyright 2016 The PDFium Authors
// BSD开源协议声明
#ifndef CORE_FXCRT_RETAIN_PTR_H_
#define CORE_FXCRT_RETAIN_PTR_H_
#include <stdint.h>
#include <cstddef>
#include <functional>
#include <memory>
#include <type_traits>
#include <utility>
//#include "core/fxcrt/check.h"
//#include "core/fxcrt/compiler_specific.h"
#include "check.h"
#include "compiler_specific.h"
namespace fxcrt {
// 用于std::unique_ptr的自定义删除器,调用Release()而非delete
template <class T>
struct ReleaseDeleter {
inline void operator()(T* ptr) const { ptr->Release(); }
};
// 类似于base库的scoped_refptr,实现引用计数的智能指针
template <class T>
class /*TRIVIAL_ABI*/ RetainPtr {
public:
// 默认构造函数
RetainPtr() noexcept = default;
// 允许隐式转换nullptr
// NOLINTNEXTLINE(runtime/explicit)
RetainPtr(std::nullptr_t ptr) {}
// 显式构造函数,接受原生指针
explicit RetainPtr(T* pObj) noexcept : m_pObj(pObj) {
if (m_pObj)
m_pObj->Retain(); // 增加引用计数
}
// 拷贝构造函数
RetainPtr(const RetainPtr& that) noexcept : RetainPtr(that.Get()) {}
// 移动构造函数,转移所有权后原指针置空
RetainPtr(RetainPtr&& that) noexcept { Unleak(that.Leak()); }
// 类型转换的拷贝构造函数
template <class U,
typename = typename std::enable_if<
std::is_convertible<U*, T*>::value>::type>
RetainPtr(const RetainPtr<U>& that) : RetainPtr(that.Get()) {}
// 类型转换的移动构造函数
template <class U,
typename = typename std::enable_if<
std::is_convertible<U*, T*>::value>::type>
RetainPtr(RetainPtr<U>&& that) noexcept {
Unleak(that.Leak());
}
// 赋值nullptr操作符
RetainPtr& operator=(std::nullptr_t) noexcept {
Reset();
return *this;
}
// 拷贝赋值操作符
RetainPtr& operator=(const RetainPtr& that) {
if (*this != that)
Reset(that.Get());
return *this;
}
// 移动赋值操作符
RetainPtr& operator=(RetainPtr&& that) noexcept {
Unleak(that.Leak());
return *this;
}
// 类型转换的拷贝赋值操作符
template <class U,
typename = typename std::enable_if<
std::is_convertible<U*, T*>::value>::type>
RetainPtr& operator=(const RetainPtr<U>& that) {
if (*this != that)
Reset(that.Get());
return *this;
}
// 类型转换的移动赋值操作符
template <class U,
typename = typename std::enable_if<
std::is_convertible<U*, T*>::value>::type>
RetainPtr& operator=(RetainPtr<U>&& that) noexcept {
Unleak(that.Leak());
return *this;
}
// 析构函数
~RetainPtr() = default;
// 类型转换方法
template <class U>
U* AsRaw() const {
return static_cast<U*>(Get());
}
// 转换为另一种类型的RetainPtr
template <class U>
RetainPtr<U> As() const {
return RetainPtr<U>(AsRaw<U>());
}
// 重置指针
void Reset(T* obj = nullptr) {
if (obj)
obj->Retain(); // 新对象增加引用计数
m_pObj.reset(obj); // 旧对象自动释放
}
// 隐式转换为原生指针
operator T* () const noexcept { return Get(); }
// 获取原生指针
T* Get() const noexcept { return m_pObj.get(); }
// 交换两个智能指针
void Swap(RetainPtr& that) { m_pObj.swap(that.m_pObj); }
// 泄漏指针所有权
T* Leak() { return m_pObj.release(); }
// 接收泄漏的指针
void Unleak(T* ptr) { m_pObj.reset(ptr); }
// 比较操作符
bool operator==(const RetainPtr& that) const { return Get() == that.Get(); }
bool operator!=(const RetainPtr& that) const { return !(*this == that); }
template <typename U>
bool operator==(const U& that) const {
return Get() == that;
}
template <typename U>
bool operator!=(const U& that) const {
return !(*this == that);
}
// 小于比较操作符
bool operator<(const RetainPtr& that) const {
return std::less<T*>()(Get(), that.Get());
}
// bool转换操作符
explicit operator bool() const { return !!m_pObj; }
// 解引用操作符
T& operator*() const { return *m_pObj; }
// 成员访问操作符
T* operator->() const { return m_pObj.get(); }
private:
// 使用unique_ptr管理指针,自定义删除器
std::unique_ptr<T, ReleaseDeleter<T>> m_pObj;
};
// 可被RetainPtr管理的基类,实现引用计数
class Retainable {
public:
Retainable() = default;
// 检查是否只有一个引用
bool HasOneRef() const { return m_nRefCount == 1; }
protected:
// 虚析构函数
virtual ~Retainable() = default;
private:
// 禁止拷贝构造和赋值
Retainable(const Retainable& that) = delete;
Retainable& operator=(const Retainable& that) = delete;
// 友元声明,允许RetainPtr访问私有成员
template <typename U>
friend struct ReleaseDeleter;
template <typename U>
friend class RetainPtr;
// 增加引用计数
void Retain() const {
++m_nRefCount;
CHECK(m_nRefCount > 0); // 防止溢出
}
// 减少引用计数,当计数为0时删除对象
void Release() const {
CHECK(m_nRefCount > 0);
if (--m_nRefCount == 0)
delete this;
}
// 引用计数器,使用无符号类型
mutable uintptr_t m_nRefCount = 0;
static_assert(std::is_unsigned<decltype(m_nRefCount)>::value,
"m_nRefCount必须是无符号类型,以确保Retain()中的溢出检查正常工作");
};
} // namespace fxcrt
// 导出常用类型
using fxcrt::ReleaseDeleter;
using fxcrt::Retainable;
using fxcrt::RetainPtr;
namespace pdfium {
// 类似std::make_unique的工厂函数,创建RetainPtr
template <typename T, typename... Args>
RetainPtr<T> MakeRetain(Args&&... args) {
return RetainPtr<T>(new T(std::forward<Args>(args)...));
}
// 包装原生指针为RetainPtr
template <typename T>
RetainPtr<T> WrapRetain(T* that) {
return RetainPtr<T>(that);
}
} // namespace pdfium
// 宏定义,允许通过MakeRetain构造私有构造函数的类
#define CONSTRUCT_VIA_MAKE_RETAIN \
template <typename T, typename... Args> \
friend RetainPtr<T> pdfium::MakeRetain(Args&&... args)
#endif // CORE_FXCRT_RETAIN_PTR_H_
🏗️ 整体架构
RetainPtr
是一个基于引用计数的智能指针系统,主要由以下几个部分组成:
组件 | 作用 | 特点 |
---|---|---|
RetainPtr<T> |
智能指针主体 | 管理对象生命周期,自动处理引用计数 |
Retainable |
可被管理的基类 | 提供引用计数基础设施 |
ReleaseDeleter<T> |
自定义删除器 | 调用 Release() 而非 delete |
MakeRetain() |
工厂函数 | 创建对象并包装为 RetainPtr |
📋 Retainable 基类详解
核心定义
class Retainable {
public:
Retainable() = default;
bool HasOneRef() const { return m_nRefCount == 1; }
protected:
virtual ~Retainable() = default;
private:
Retainable(const Retainable&) = delete;
Retainable& operator=(const Retainable&) = delete;
// 友元声明,允许智能指针访问私有方法
template <typename U> friend struct ReleaseDeleter;
template <typename U> friend class RetainPtr;
void Retain() const {
++m_nRefCount;
CHECK(m_nRefCount > 0); // 溢出检查
}
void Release() const {
CHECK(m_nRefCount > 0);
if (--m_nRefCount == 0)
delete this;
}
mutable uintptr_t m_nRefCount = 0;
};
🔍 关键特性
特性 | 说明 | 重要性 |
---|---|---|
虚析构函数 | 确保正确析构派生类 | ⭐⭐⭐⭐⭐ |
删除拷贝语义 | 防止意外的对象复制 | ⭐⭐⭐⭐ |
mutable 引用计数 | 允许在 const 对象上修改计数 | ⭐⭐⭐⭐ |
溢出检查 | 防止引用计数回绕 | ⭐⭐⭐ |
无符号计数类型 | 确保正确的算术行为 | ⭐⭐⭐ |
🎯 ReleaseDeleter 自定义删除器
实现代码
template <class T>
struct ReleaseDeleter {
inline void operator()(T* ptr) const { ptr->Release(); }
};
🔧 工作原理
场景 | 行为 | 结果 |
---|---|---|
std::unique_ptr 析构 |
调用 ptr->Release() |
减少引用计数,可能删除对象 |
引用计数 > 1 | Release() 减少计数 |
对象继续存在 |
引用计数 == 1 | Release() 减少到 0 |
调用 delete this |
🚀 RetainPtr 核心类详解
🏗️ 内部结构
template <class T>
class RetainPtr {
private:
std::unique_ptr<T, ReleaseDeleter<T>> m_pObj;
public:
// 各种构造函数和操作符...
};
📊 构造函数汇总
构造函数 | 语法示例 | 引用计数变化 | 说明 |
---|---|---|---|
默认构造 | RetainPtr<Doc> ptr; |
无 | 创建空指针 |
原生指针构造 | RetainPtr<Doc> ptr(new Doc); |
+1 | 显式构造,增加计数 |
拷贝构造 | RetainPtr<Doc> ptr2(ptr1); |
+1 | 共享所有权 |
移动构造 | RetainPtr<Doc> ptr2(std::move(ptr1)); |
无 | 转移所有权 |
类型转换构造 | RetainPtr<Base> base(derivedPtr); |
+1 | 向上转型 |
⚙️ 关键方法详解
1. Reset() - 重置指针所有权
void Reset(T* obj = nullptr) {
if (obj)
obj->Retain(); // 新对象增加计数
m_pObj.reset(obj); // 旧对象自动释放
}
使用场景:
auto doc = MakeRetain<Document>();
doc.Reset(new Document("New")); // 释放旧对象,管理新对象
doc.Reset(); // 释放当前对象,变为空指针
2. Leak() / Unleak() - 手动所有权管理
T* Leak() { return m_pObj.release(); } // 释放所有权,不减少计数
void Unleak(T* ptr) { m_pObj.reset(ptr); } // 接管所有权,不增加计数
典型用法:
auto doc = MakeRetain<Document>();
Document* raw = doc.Leak(); // doc 变为空,raw 需要手动管理
// ... 使用 raw ...
doc.Unleak(raw); // 重新接管所有权
3. 类型转换方法
// 静态类型转换
template <class U>
U* AsRaw() const { return static_cast<U*>(Get()); }
// 安全类型转换
template <class U>
RetainPtr<U> As() const { return RetainPtr<U>(AsRaw<U>()); }
使用示例:
class Derived : public Base { /*...*/ };
RetainPtr<Base> base = MakeRetain<Derived>();
RetainPtr<Derived> derived = base.As<Derived>(); // 安全向下转型
🔄 赋值操作符行为
操作符 | 等效代码 | 引用计数变化 |
---|---|---|
ptr = nullptr |
ptr.Reset() |
减少原对象计数 |
ptr1 = ptr2 |
ptr1.Reset(ptr2.Get()) |
原对象-1,新对象+1 |
ptr1 = std::move(ptr2) |
ptr1.Unleak(ptr2.Leak()) |
无变化,所有权转移 |
🏭 MakeRetain 工厂函数
核心实现
template <typename T, typename... Args>
RetainPtr<T> MakeRetain(Args&&... args) {
return RetainPtr<T>(new T(std::forward<Args>(args)...));
}
🎯 CONSTRUCT_VIA_MAKE_RETAIN 宏
#define CONSTRUCT_VIA_MAKE_RETAIN \
template <typename T, typename... Args> \
friend RetainPtr<T> pdfium::MakeRetain(Args&&... args)
使用方式:
class SecretDoc : public Retainable {
private:
CONSTRUCT_VIA_MAKE_RETAIN; // 允许 MakeRetain 访问私有构造函数
SecretDoc() {} // 私有构造函数
};
💡 最佳实践和常见模式
1. 函数参数传递
// 推荐:const 引用传递(不增加计数)
void ReadDocument(const RetainPtr<Document>& doc);
// 需要共享所有权时:值传递(增加计数)
void StoreDocument(RetainPtr<Document> doc);
// 不推荐:原始指针传递(失去所有权管理)
void RiskyFunction(Document* doc);
2. 容器使用
// 在 vector 中存储智能指针
std::vector<RetainPtr<Document>> documents;
// 添加文档
documents.push_back(MakeRetain<Document>("Doc1"));
documents.emplace_back(new Document("Doc2"));
// 安全遍历
for (const auto& doc : documents) {
doc->Process();
}
3. 多态使用
class Shape : public Retainable {
public:
virtual void Draw() = 0;
};
class Circle : public Shape {
public:
void Draw() override { /*...*/ }
};
// 多态容器
std::vector<RetainPtr<Shape>> shapes;
shapes.push_back(MakeRetain<Circle>());
4. 循环引用处理
class Node : public Retainable {
public:
void SetParent(const RetainPtr<Node>& parent) {
m_parent = parent; // 强引用 → 可能循环引用
}
void SetWeakParent(const RetainPtr<Node>& parent) {
m_weakParent = parent.Get(); // 弱引用打破循环
}
private:
RetainPtr<Node> m_parent; // 强引用
Node* m_weakParent = nullptr; // 弱引用
};
⚠️ 注意事项和陷阱
陷阱 | 问题描述 | 解决方案 |
---|---|---|
循环引用 | 对象相互持有强引用,无法释放 | 使用弱引用或原始指针 |
混用 new/Delete | 手动管理 RetainPtr 管理的对象 | 始终使用 MakeRetain |
跨 DLL 边界 | 不同模块中的 new/delete 不匹配 | 确保统一的分配器 |
线程安全 | 非原子操作引用计数 | 需要外部同步机制 |
🎯 完整使用示例
// 定义可保留对象
class DatabaseConnection : public Retainable {
public:
static RetainPtr<DatabaseConnection> Create(const std::string& url) {
return MakeRetain<DatabaseConnection>(url);
}
void Query(const std::string& sql) {
std::cout << "Executing: " << sql << std::endl;
}
~DatabaseConnection() override {
std::cout << "Connection closed" << std::endl;
}
private:
CONSTRUCT_VIA_MAKE_RETAIN;
DatabaseConnection(const std::string& url) : m_url(url) {
std::cout << "Connection opened to: " << url << std::endl;
}
std::string m_url;
};
// 使用示例
void Demo() {
// 创建连接
auto conn = DatabaseConnection::Create("mysql://localhost");
// 传递到函数
ExecuteQuery(conn);
// 在容器中存储
std::vector<RetainPtr<DatabaseConnection>> connections;
connections.push_back(conn);
// 类型转换示例
RetainPtr<Retainable> base = conn.As<Retainable>();
}
这个全面的介绍应该帮助你深入理解 RetainPtr
系统的设计理念、实现细节和最佳实践用法。
创建方式的限制
错误代码如下
#pragma once
#include "retain_ptr.h"
#include <iostream>
#include <vector>
#include <string>
using std::string;
// 示例1: 继承自 Retainable 的类
class Document : public fxcrt::Retainable {
public:
explicit Document(const std::string& title) : m_title(title) {
std::cout << "Document created: " << m_title << std::endl;
}
~Document() override {
std::cout << "Document destroyed: " << m_title << std::endl;
}
void SetTitle(const std::string& title) { m_title = title; }
const std::string& GetTitle() const { return m_title; }
void Print() const {
std::cout << "Document: " << m_title
<< " (Refs: " << (HasOneRef() ? "1" : ">1") << ")" << std::endl;
}
private:
std::string m_title;
};
// 示例2: 另一个可保留的类
class Page : public fxcrt::Retainable {
public:
explicit Page(int number) : m_number(number) {
std::cout << "Page created: " << m_number << std::endl;
}
~Page() override {
std::cout << "Page destroyed: " << m_number << std::endl;
}
int GetNumber() const { return m_number; }
private:
int m_number;
};
// 示例3: 使用私有构造函数的类
class SecretDocument : public fxcrt::Retainable {
public:
static fxcrt::RetainPtr<SecretDocument> Create(const std::string& content) {
return fxcrt::MakeRetain<SecretDocument>(content);
}
const std::string& GetContent() const { return m_content; }
void Reveal() const {
std::cout << "Secret content: " << m_content << std::endl;
}
private:
CONSTRUCT_VIA_MAKE_RETAIN;
explicit SecretDocument(const std::string& content) : m_content(content) {
std::cout << "SecretDocument created" << std::endl;
}
~SecretDocument() override {
std::cout << "SecretDocument destroyed" << std::endl;
}
std::string m_content;
};
// 函数示例:演示参数传递和返回值
fxcrt::RetainPtr<Document> CreateDocument(const std::string& title) {
return fxcrt::MakeRetain<Document>(title);
}
void ProcessDocument(const fxcrt::RetainPtr<Document>& doc) {
if (doc) {
std::cout << "Processing: " << doc->GetTitle() << std::endl;
doc->Print();
}
}
void ModifyDocument(fxcrt::RetainPtr<Document> doc) {
if (doc) {
doc->SetTitle("Modified " + doc->GetTitle());
}
}
// 演示容器使用
void DemonstrateContainers() {
std::vector<fxcrt::RetainPtr<Page>> pages;
// 添加页面
for (int i = 1; i <= 3; ++i) {
pages.push_back(fxcrt::MakeRetain<Page>(i));
}
std::cout << "Created " << pages.size() << " pages" << std::endl;
}
int main() {
std::cout << "=== 开始 RetainPtr 演示 ===" << std::endl;
// 1. 基本用法
{
std::cout << "\n1. 基本创建和销毁:" << std::endl;
auto doc = fxcrt::MakeRetain<Document>("Test Document");
doc->Print();
} // doc 超出作用域,自动销毁
// 2. 多引用计数
{
std::cout << "\n2. 多引用计数演示:" << std::endl;
auto original = fxcrt::MakeRetain<Document>("Shared Document");
original->Print();
// 创建另一个引用
fxcrt::RetainPtr<Document> copy = original;
copy->Print(); // 引用计数应为2
// 重置一个引用
copy.Reset();
original->Print(); // 引用计数回到1
}
// 3. 函数参数和返回值
{
std::cout << "\n3. 函数传递演示:" << std::endl;
auto doc = CreateDocument("Function Document");
ProcessDocument(doc);
ModifyDocument(doc); // 值传递,创建新引用
ProcessDocument(doc); // 原文档未改变
}
// 4. 类型转换
{
std::cout << "\n4. 类型转换演示:" << std::endl;
auto doc = fxcrt::MakeRetain<Document>("Cast Document");
// 隐式转换为原始指针
Document* rawPtr = doc;
std::cout << "Raw pointer: " << rawPtr->GetTitle() << std::endl;
// 显式获取原始指针
Document* explicitPtr = doc.Get();
std::cout << "Explicit pointer: " << explicitPtr->GetTitle() << std::endl;
}
// 5. 比较操作
{
std::cout << "\n5. 比较操作演示:" << std::endl;
auto doc1 = fxcrt::MakeRetain<Document>("Doc 1");
auto doc2 = fxcrt::MakeRetain<Document>("Doc 2");
auto doc1Copy = doc1;
std::cout << "doc1 == doc1Copy: " << (doc1 == doc1Copy) << std::endl;
std::cout << "doc1 == doc2: " << (doc1 == doc2) << std::endl;
std::cout << "doc1 != nullptr: " << (doc1 != nullptr) << std::endl;
}
// 6. 移动语义
{
std::cout << "\n6. 移动语义演示:" << std::endl;
auto doc = fxcrt::MakeRetain<Document>("Movable Document");
// 移动构造
fxcrt::RetainPtr<Document> movedDoc = std::move(doc);
std::cout << "After move, original is null: " << (doc == nullptr) << std::endl;
movedDoc->Print();
// 移动赋值
doc = std::move(movedDoc);
std::cout << "After move back, moved is null: " << (movedDoc == nullptr) << std::endl;
doc->Print();
}
// 7. 私有构造函数类
{
std::cout << "\n7. 私有构造函数演示:" << std::endl;
auto secret = SecretDocument::Create("Top Secret");
secret->Reveal();
}
// 8. 容器使用
{
std::cout << "\n8. 容器使用演示:" << std::endl;
DemonstrateContainers();
}
// 9. 复杂场景:对象关系
{
/*std::cout << "\n9. 对象关系演示:" << std::endl;
class Book : public fxcrt::Retainable {
public:
static fxcrt::RetainPtr<Book> Create(const std::string& title) {
return fxcrt::MakeRetain<Book>(title);
}
void AddChapter(const fxcrt::RetainPtr<Document>& chapter) {
m_chapters.push_back(chapter);
}
void Print() const {
std::cout << "Book: " << m_title
<< " (" << m_chapters.size() << " chapters)" << std::endl;
for (const auto& chapter : m_chapters) {
std::cout << " - " << chapter->GetTitle() << std::endl;
}
}
private:
CONSTRUCT_VIA_MAKE_RETAIN;
explicit Book(const std::string& title) : m_title(title) {}
std::string m_title;
std::vector<fxcrt::RetainPtr<Document>> m_chapters;
};
auto book = Book::Create("My Book");
book->AddChapter(fxcrt::MakeRetain<Document>("Chapter 1"));
book->AddChapter(fxcrt::MakeRetain<Document>("Chapter 2"));
book->Print();*/
}
std::cout << "\n=== RetainPtr 演示结束 ===" << std::endl;
return 0;
}
🔍 RetainPtr 构造函数限制分析
你遇到的错误是因为 RetainPtr
系统有严格的访问控制机制。让我详细解释为什么直接使用构造函数是错误的:
🚫 问题根源:CONSTRUCT_VIA_MAKE_RETAIN 宏的限制
宏定义分析
#define CONSTRUCT_VIA_MAKE_RETAIN \
template <typename T, typename... Args> \
friend RetainPtr<T> pdfium::MakeRetain(Args&&... args)
这个宏只授予了 pdfium::MakeRetain
函数模板访问私有构造函数的权限,但没有授予其他任何函数或类访问权限。
错误场景分析
class Book : public fxcrt::Retainable {
private:
CONSTRUCT_VIA_MAKE_RETAIN;
explicit Book(const std::string& title) : m_title(title) {} // 私有构造函数
};
// 以下都是错误的:
auto book1 = new Book("My Book"); // ❌ 错误:构造函数私有
auto book2 = Book("My Book"); // ❌ 错误:构造函数私有
auto book3 = fxcrt::RetainPtr<Book>(new Book("My Book")); // ❌ 错误:构造函数私有
✅ 唯一正确的创建方式
1. 使用静态工厂方法(推荐)
class Book : public fxcrt::Retainable {
public:
static fxcrt::RetainPtr<Book> Create(const std::string& title) {
return fxcrt::MakeRetain<Book>(title); // ✅ 正确:通过友元函数访问
}
private:
CONSTRUCT_VIA_MAKE_RETAIN;
explicit Book(const std::string& title) : m_title(title) {}
};
// 使用:
auto book = Book::Create("My Book"); // ✅ 正确
2. 直接使用 MakeRetain(如果可见)
auto book = fxcrt::MakeRetain<Book>("My Book"); // ✅ 正确(但需要MakeRetain可见)
🛠️ 修复你的代码
修改 Book 类的定义:
class Book : public fxcrt::Retainable {
public:
// 添加静态工厂方法
static fxcrt::RetainPtr<Book> Create(const std::string& title) {
return fxcrt::MakeRetain<Book>(title);
}
void AddChapter(const fxcrt::RetainPtr<Document>& chapter) {
m_chapters.push_back(chapter);
}
void Print() const {
std::cout << "Book: " << m_title
<< " (" << m_chapters.size() << " chapters)" << std::endl;
for (const auto& chapter : m_chapters) {
std::cout << " - " << chapter->GetTitle() << std::endl;
}
}
private:
CONSTRUCT_VIA_MAKE_RETAIN;
explicit Book(const std::string& title) : m_title(title) {}
std::string m_title;
std::vector<fxcrt::RetainPtr<Document>> m_chapters;
};
修改使用代码:
// 9. 复杂场景:对象关系
{
std::cout << "\n9. 对象关系演示:" << std::endl;
// 使用工厂方法创建,而不是直接构造
auto book = Book::Create("My Book");
book->AddChapter(fxcrt::MakeRetain<Document>("Chapter 1"));
book->AddChapter(fxcrt::MakeRetain<Document>("Chapter 2"));
book->Print();
}
📋 访问权限总结表
创建方式 | 访问权限 | 是否可行 | 说明 |
---|---|---|---|
new Book() |
需要访问私有构造函数 | ❌ 失败 | 构造函数私有 |
Book::Create() |
通过友元 MakeRetain 访问 |
✅ 成功 | 推荐方式 |
MakeRetain<Book>() |
友元函数直接访问 | ✅ 成功 | 需要函数可见 |
RetainPtr<Book>(new Book()) |
需要访问私有构造函数 | ❌ 失败 | 双重错误 |
🎯 设计意图理解
这种设计模式有几个重要目的:
- 强制使用智能指针:防止用户使用原始指针或栈对象
- 控制对象创建:确保所有对象都被
RetainPtr
正确管理 - 接口清晰:通过工厂方法提供明确的创建接口
- 防止误用:避免手动管理内存和潜在的内存泄漏
💡 完整修正后的代码
// 9. 复杂场景:对象关系
{
std::cout << "\n9. 对象关系演示:" << std::endl;
class Book : public fxcrt::Retainable {
public:
static fxcrt::RetainPtr<Book> Create(const std::string& title) {
return fxcrt::MakeRetain<Book>(title);
}
void AddChapter(const fxcrt::RetainPtr<Document>& chapter) {
m_chapters.push_back(chapter);
}
void Print() const {
std::cout << "Book: " << m_title
<< " (" << m_chapters.size() << " chapters)" << std::endl;
for (const auto& chapter : m_chapters) {
std::cout << " - " << chapter->GetTitle() << std::endl;
}
}
private:
CONSTRUCT_VIA_MAKE_RETAIN;
explicit Book(const std::string& title) : m_title(title) {}
std::string m_title;
std::vector<fxcrt::RetainPtr<Document>> m_chapters;
};
auto book = Book::Create("My Book");
book->AddChapter(fxcrt::MakeRetain<Document>("Chapter 1"));
book->AddChapter(fxcrt::MakeRetain<Document>("Chapter 2"));
book->Print();
}
这种设计模式确保了类型安全性和内存安全性,是大型C++项目中常见的最佳实践。
修正的完整代码(正确构造对象)
#pragma once
#include "retain_ptr.h"
#include <iostream>
#include <vector>
#include <string>
using std::string;
// 示例1: 继承自 Retainable 的类
class Document : public fxcrt::Retainable {
public:
explicit Document(const std::string& title) : m_title(title) {
std::cout << "Document created: " << m_title << std::endl;
}
~Document() override {
std::cout << "Document destroyed: " << m_title << std::endl;
}
void SetTitle(const std::string& title) { m_title = title; }
const std::string& GetTitle() const { return m_title; }
void Print() const {
std::cout << "Document: " << m_title
<< " (Refs: " << (HasOneRef() ? "1" : ">1") << ")" << std::endl;
}
private:
std::string m_title;
};
// 示例2: 另一个可保留的类
class Page : public fxcrt::Retainable {
public:
explicit Page(int number) : m_number(number) {
std::cout << "Page created: " << m_number << std::endl;
}
~Page() override {
std::cout << "Page destroyed: " << m_number << std::endl;
}
int GetNumber() const { return m_number; }
private:
int m_number;
};
// 示例3: 使用私有构造函数的类
class SecretDocument : public fxcrt::Retainable {
public:
static fxcrt::RetainPtr<SecretDocument> Create(const std::string& content) {
return pdfium::MakeRetain<SecretDocument>(content);
}
const std::string& GetContent() const { return m_content; }
void Reveal() const {
std::cout << "Secret content: " << m_content << std::endl;
}
private:
CONSTRUCT_VIA_MAKE_RETAIN;
explicit SecretDocument(const std::string& content) : m_content(content) {
std::cout << "SecretDocument created" << std::endl;
}
~SecretDocument() override {
std::cout << "SecretDocument destroyed" << std::endl;
}
std::string m_content;
};
// 函数示例:演示参数传递和返回值
fxcrt::RetainPtr<Document> CreateDocument(const std::string& title) {
return pdfium::MakeRetain<Document>(title);
}
void ProcessDocument(const fxcrt::RetainPtr<Document>& doc) {
if (doc) {
std::cout << "Processing: " << doc->GetTitle() << std::endl;
doc->Print();
}
}
void ModifyDocument(fxcrt::RetainPtr<Document> doc) {
if (doc) {
doc->SetTitle("Modified " + doc->GetTitle());
}
}
// 演示容器使用
void DemonstrateContainers() {
std::vector<fxcrt::RetainPtr<Page>> pages;
// 添加页面
for (int i = 1; i <= 3; ++i) {
pages.push_back(pdfium::MakeRetain<Page>(i));
}
std::cout << "Created " << pages.size() << " pages" << std::endl;
}
class Book : public fxcrt::Retainable {
public:
static fxcrt::RetainPtr<Book> Create(const std::string& title) {
return pdfium::MakeRetain<Book>(title);
}
void AddChapter(const fxcrt::RetainPtr<Document>& chapter) {
m_chapters.push_back(chapter);
}
void Print() const {
std::cout << "Book: " << m_title
<< " (" << m_chapters.size() << " chapters)" << std::endl;
for (const auto& chapter : m_chapters) {
std::cout << " - " << chapter->GetTitle() << std::endl;
}
}
private:
CONSTRUCT_VIA_MAKE_RETAIN;
explicit Book(const std::string& title) : m_title(title) {}
std::string m_title;
std::vector<fxcrt::RetainPtr<Document>> m_chapters;
};
int main() {
std::cout << "=== 开始 RetainPtr 演示 ===" << std::endl;
// 1. 基本用法
{
std::cout << "\n1. 基本创建和销毁:" << std::endl;
auto doc = pdfium::MakeRetain<Document>("Test Document");
doc->Print();
} // doc 超出作用域,自动销毁
// 2. 多引用计数
{
std::cout << "\n2. 多引用计数演示:" << std::endl;
auto original = pdfium::MakeRetain<Document>("Shared Document");
original->Print();
// 创建另一个引用
fxcrt::RetainPtr<Document> copy = original;
copy->Print(); // 引用计数应为2
// 重置一个引用
copy.Reset();
original->Print(); // 引用计数回到1
}
// 3. 函数参数和返回值
{
std::cout << "\n3. 函数传递演示:" << std::endl;
auto doc = CreateDocument("Function Document");
ProcessDocument(doc);
ModifyDocument(doc); // 值传递,创建新引用
ProcessDocument(doc); // 原文档未改变
}
// 4. 类型转换
{
std::cout << "\n4. 类型转换演示:" << std::endl;
auto doc = pdfium::MakeRetain<Document>("Cast Document");
// 隐式转换为原始指针
Document* rawPtr = doc;
std::cout << "Raw pointer: " << rawPtr->GetTitle() << std::endl;
// 显式获取原始指针
Document* explicitPtr = doc.Get();
std::cout << "Explicit pointer: " << explicitPtr->GetTitle() << std::endl;
}
// 5. 比较操作
{
std::cout << "\n5. 比较操作演示:" << std::endl;
auto doc1 = pdfium::MakeRetain<Document>("Doc 1");
auto doc2 = pdfium::MakeRetain<Document>("Doc 2");
auto doc1Copy = doc1;
std::cout << "doc1 == doc1Copy: " << (doc1 == doc1Copy) << std::endl;
std::cout << "doc1 == doc2: " << (doc1 == doc2) << std::endl;
std::cout << "doc1 != nullptr: " << (doc1 != nullptr) << std::endl;
}
// 6. 移动语义
{
std::cout << "\n6. 移动语义演示:" << std::endl;
auto doc = pdfium::MakeRetain<Document>("Movable Document");
// 移动构造
fxcrt::RetainPtr<Document> movedDoc = std::move(doc);
std::cout << "After move, original is null: " << (doc == nullptr) << std::endl;
movedDoc->Print();
// 移动赋值
doc = std::move(movedDoc);
std::cout << "After move back, moved is null: " << (movedDoc == nullptr) << std::endl;
doc->Print();
}
// 7. 私有构造函数类
{
std::cout << "\n7. 私有构造函数演示:" << std::endl;
auto secret = SecretDocument::Create("Top Secret");
secret->Reveal();
}
// 8. 容器使用
{
std::cout << "\n8. 容器使用演示:" << std::endl;
DemonstrateContainers();
}
// 9. 复杂场景:对象关系
{
std::cout << "\n9. 对象关系演示:" << std::endl;
auto book = Book::Create("My Book");
book->AddChapter(pdfium::MakeRetain<Document>("Chapter 1"));
book->AddChapter(pdfium::MakeRetain<Document>("Chapter 2"));
book->Print();
}
std::cout << "\n=== RetainPtr 演示结束 ===" << std::endl;
return 0;
}
更多推荐
所有评论(0)