编译运行:clang++ -std=c++26 -Wall -Wextra -pthread 测试_第23章_异常安全.cpp -o t && ./t(需先 cd 测试/)
// 第23章测试:异常安全与 RAII 深入
#include <print>
#include <string>
#include <vector>
#include <memory>
#include <stdexcept>
int failures = 0;
#define CHECK(expr) \
do { \
if (!(expr)) { \
++failures; \
std::println("FAIL 第{}行: {}", __LINE__, #expr); \
} \
} while (0)
// 23.3 银行转账(先验证后提交 → 强保证)
class Account {
public:
explicit Account(double b) : balance_(b) {}
void withdraw(double amt) { balance_ -= amt; } // 不检查(调用方先验证)
void deposit(double amt) { balance_ += amt; }
double balance() const { return balance_; }
private:
double balance_ = 0;
};
void transfer_safe(Account& a, Account& b, double amt) {
if (a.balance() < amt) throw std::runtime_error("余额不足");
// 所有可能失败的检查都已前置 → 下面不再抛异常 → 强保证
a.withdraw(amt);
b.deposit(amt);
}
// 23.4 copy-and-swap
class Widget {
public:
Widget() = default;
explicit Widget(int n) : data_(n, 7) {}
Widget(const Widget&) = default;
Widget& operator=(Widget other) {
swap(other);
return *this;
}
void swap(Widget& o) noexcept { data_.swap(o.data_); }
std::size_t size() const { return data_.size(); }
int first() const { return data_.front(); }
private:
std::vector<int> data_;
};
// 23.5 RAII 事务(模拟数据库)
class FakeDb {
public:
bool in_tx() const { return tx_open_; }
void begin() { tx_open_ = true; committed_ = false; }
void insert(const std::string& s) { log_.push_back(s); }
void commit() { committed_ = true; tx_open_ = false; }
void rollback() { if (tx_open_) { log_.clear(); tx_open_ = false; } }
std::size_t log_size() const { return log_.size(); }
private:
bool tx_open_ = false;
bool committed_ = false;
std::vector<std::string> log_;
};
class Transaction {
public:
explicit Transaction(FakeDb& db) : db_(db) { db_.begin(); }
~Transaction() {
if (!committed_) db_.rollback(); // 忘了 commit → 自动回滚
}
void commit() { committed_ = true; db_.commit(); }
private:
FakeDb& db_;
bool committed_ = false;
};
// 23.9 SafeStack
template <typename T>
class SafeStack {
public:
void push(const T& v) { data_.push_back(v); } // vector 自带强保证
void pop() {
if (data_.empty()) throw std::runtime_error("空栈");
data_.pop_back();
}
const T& top() const {
if (data_.empty()) throw std::runtime_error("空栈");
return data_.back();
}
std::size_t size() const { return data_.size(); }
private:
std::vector<T> data_;
};
int main() {
// 23.3 强保证转账
Account a(100), b(50);
transfer_safe(a, b, 30); // 成功
CHECK(a.balance() == 70 && b.balance() == 80);
bool threw = false;
try {
transfer_safe(a, b, 500); // 余额不足
} catch (const std::runtime_error&) { threw = true; }
CHECK(threw);
CHECK(a.balance() == 70 && b.balance() == 80); // 状态没变:强保证!
// 23.4 copy-and-swap
Widget w1(3);
Widget w2;
w2 = w1; // 拷贝赋值
CHECK(w2.size() == 3 && w2.first() == 7);
w2 = Widget(5); // 临时对象赋值
CHECK(w2.size() == 5);
// 23.5 RAII 事务
FakeDb db;
{
Transaction tx(db);
db.insert("record1");
// 不 commit → 析构自动回滚
}
CHECK(db.log_size() == 0); // 回滚了
{
Transaction tx(db);
db.insert("record2");
tx.commit();
}
CHECK(db.log_size() == 1); // 提交保留
// 23.9 SafeStack 异常路径
SafeStack<int> st;
CHECK(st.size() == 0);
try {
st.pop(); // 空栈 → 抛异常
CHECK(false);
} catch (const std::runtime_error&) { CHECK(true); }
st.push(1);
st.push(2);
CHECK(st.size() == 2);
CHECK(st.top() == 2);
st.pop();
CHECK(st.top() == 1);
// 23.10 构造失败不泄漏(成员 RAII 自动清理)
struct Exploder {
struct Sub { ~Sub() { /* 资源释放 */ } };
std::unique_ptr<int> mem = std::make_unique<int>(1);
Sub sub;
Exploder() { throw std::runtime_error("构造失败"); }
};
try { Exploder e; (void)e; CHECK(false); }
catch (const std::runtime_error&) { CHECK(true); }
// 构造失败后,mem 与 sub 正常析构(内存无泄漏)
if (failures == 0) std::println("全部通过");
return failures;
}