
“Execute-Around Pointer” 是一个相对少见但极具设计美感的 C++ 惯用法,最初收录于《More C++ Idioms》wikibook 中。它体现了 C++ 语言在资源管理和行为注入(aspect-oriented behavior injection)方面的高可塑性。这个模式非常适合封装资源访问、日志、线程锁、事务控制等需要在调用目标函数前后执行固定动作的场景。
🧠 意图(Intent) 用一句话概括这个惯用法:
封装某个对象,使得对它的每次操作(函数调用)前后,自动执行一些附加操作。
你可以把它看作是一种AOP(面向切面编程)在 C++ 中的简化实现方式。常见用法包括:
- 在访问某对象时自动加锁 / 解锁(线程同步)
- 在访问某资源时自动记录日志(日志注入)
- 在事务开始前/结束后自动提交或回滚(数据库事务)
🌟 类比:RAII 是作用于“生命周期”,而 Execute-Around Pointer 是作用于“每次调用” RAII(Resource Acquisition Is Initialization)模式会在构造 / 析构时执行特定逻辑:
{
std::lock_guard<std::mutex> lock(mtx); // 构造时加锁,析构时自动释放
// 临界区代码
}
而 Execute-Around Pointer 则是对每次成员函数调用前后做处理,而不仅限于构造/析构
📦 实现方式(简单版本) 基本思想是写一个代理类,重载 operator->() 或 operator*(),使得在访问内部对象成员函数时,自动包裹前后操作。
示例:自动加锁访问共享资源
#include <iostream>
#include <mutex>
#include <memory>
class SharedResource {
public:
void doWork() {
std::cout << "Working on shared resource\n";
}
};
// Execute-Around Pointer
template<typename T>
class LockedPtr {
public:
LockedPtr(std::shared_ptr<T> ptr, std::mutex& mtx)
: m_ptr(ptr), m_mtx(mtx) {
m_mtx.lock(); // 前操作:加锁
}
~LockedPtr() {
m_mtx.unlock(); // 后操作:解锁
}
T* operator->() {
return m_ptr.get(); // 转发成员调用
}
private:
std::shared_ptr<T> m_ptr;
std::mutex& m_mtx;
};
// 工厂函数简化调用
template<typename T>
LockedPtr<T> make_locked(std::shared_ptr<T> ptr, std::mutex& mtx) {
return LockedPtr<T>(ptr, mtx);
}
使用方式如下:
int main() {
auto resource = std::make_shared<SharedResource>();
std::mutex mtx;
{
auto locked = make_locked(resource, mtx);
locked->doWork(); // 自动加锁/解锁
}
return 0;
}
输出:
Working on shared resource
🔍 关键点分析 特征 说明 封装行为 在 operator-> 里只允许在作用域中安全访问对象 透明调用 用户写 ptr->method(),无需关心锁机制 调用开销小 只包裹函数调用,不产生额外动态开销 高可扩展性 可以支持日志、事务、性能统计等用途
🧩 类似场景延伸 应用场景 Execute-Around 示例 数据库事务管理 自动 begin / commit / rollback 日志系统 每次函数调用记录日志 安全检查 调用前验证权限,调用后清理 性能监控 记录函数执行耗时 临时状态设置 例如切换 OpenGL 状态或设置线程本地参数 🛡️ 进阶技巧:结合 Lambda 或策略类 如果想让前后处理行为更加灵活,可以改写为策略注入或 lambda 包裹:
template<typename T, typename Pre, typename Post>
class AroundPtr {
public:
AroundPtr(std::shared_ptr<T> ptr, Pre pre, Post post)
: m_ptr(ptr), m_post(post) {
pre();
}
~AroundPtr() {
m_post();
}
T* operator->() {
return m_ptr.get();
}
private:
std::shared_ptr<T> m_ptr;
Post m_post;
};
✅ 总结 Execute-Around Pointer 是一种结合了 RAII 和代理思想的惯用法,它的价值在于:
抽象并自动化执行一些访问前后需要重复写的逻辑; 让使用者写出简洁、结构清晰、可维护的代码; 避免逻辑遗漏(比如忘记释放锁); 具有良好的通用性,是某种“轻量级 AOP”。
如果你感兴趣,我可以再演示一个用这个惯用法实现“数据库事务”的完整例子,或者聊聊它和 C++20 coroutine 的一些有趣组合 😄 要不要来一个?
好的,来一个经典的 数据库事务(Database Transaction)场景,我们将使用 Execute-Around Pointer 惯用法自动控制事务的 begin() / commit() / rollback(),确保异常或错误时能够正确地回滚事务,而不需要用户手动去记得写这些流程控制逻辑。
💡 目标 用法像这样 👇:
{
auto tx = make_transactional(db);
tx->updateData();
tx->insertRow();
// tx.commit() happens automatically on success
}
如果在作用域中出现异常或逻辑失败,自动回滚事务。
🧱 模拟数据库类 先写一个简单的伪数据库类(演示用):
#include <iostream>
#include <memory>
#include <stdexcept>
class Database {
public:
void beginTransaction() {
std::cout << "[DB] BEGIN TRANSACTION\n";
}
void commitTransaction() {
std::cout << "[DB] COMMIT TRANSACTION\n";
}
void rollbackTransaction() {
std::cout << "[DB] ROLLBACK TRANSACTION\n";
}
void updateData() {
std::cout << "[DB] Updating data...\n";
}
void insertRow() {
std::cout << "[DB] Inserting row...\n";
}
};
🧠 Execute-Around Pointer 实现:TransactionalPtr
template<typename T>
class TransactionalPtr {
public:
TransactionalPtr(std::shared_ptr<T> db)
: db_(db), committed_(false) {
db_->beginTransaction(); // 自动开启事务
}
~TransactionalPtr() {
if (!committed_) {
db_->rollbackTransaction(); // 自动回滚事务
}
}
T* operator->() {
return db_.get(); // 转发成员调用
}
void commit() {
db_->commitTransaction(); // 用户显式调用 commit
committed_ = true;
}
private:
std::shared_ptr<T> db_;
bool committed_;
};
template<typename T>
TransactionalPtr<T> make_transactional(std::shared_ptr<T> db) {
return TransactionalPtr<T>(db);
}
🧪 示例用法:测试正常情况和异常情况
int main() {
auto db = std::make_shared<Database>();
std::cout << "=== [Transaction 1: Normal Commit] ===\n";
{
auto tx = make_transactional(db);
tx->updateData();
tx->insertRow();
tx.commit(); // 显式提交
}
std::cout << "\n=== [Transaction 2: Exception -> Rollback] ===\n";
try {
auto tx = make_transactional(db);
tx->updateData();
throw std::runtime_error("Oops, something went wrong!");
tx.commit(); // 不会执行
} catch (const std::exception& ex) {
std::cout << "[EXCEPTION] " << ex.what() << "\n";
}
return 0;
}
✅ 输出结果
=== [Transaction 1: Normal Commit] ===
[DB] BEGIN TRANSACTION
[DB] Updating data...
[DB] Inserting row...
[DB] COMMIT TRANSACTION
=== [Transaction 2: Exception -> Rollback] ===
[DB] BEGIN TRANSACTION
[DB] Updating data...
[DB] ROLLBACK TRANSACTION
[EXCEPTION] Oops, something went wrong!
🎯 你学到了什么 事务控制用 TransactionalPtr 来自动管理; 默认自动回滚; 用户可选显式调用 .commit(); 无需记得写 try-catch 或 rollback,异常安全; 代码整洁,逻辑集中在事务作用域中,符合 C++ 的资源即行为的哲学。
需要我帮你扩展为更复杂场景(比如嵌套事务、RAII commit、日志记录)吗?或者改写为可配置策略版本?这惯用法空间挺大的~
这个惯用法确实确实漂亮,扩展性,可用范围如此广泛 今天先了解到这里,慢慢消化一下

评论