Colderleo's blog Colderleo's blog
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)

Colder Leo

热爱代码的打工人
首页
Linux
C++
Python
前端
工具软件
mysql
索引
关于
GitHub (opens new window)
  • bug定位的一些情形
  • c++性能调优,可能的情况
  • total-编程知识点集锦
  • hpc_common.hpp
  • memory order 内存模型
  • 类型推导之auto-template-decltype
  • 完美转发forward源码分析
  • 左值和右值,右值引用、重载 std-move,引用折叠
  • cmake用法
  • alignas、alignof、sizeof实现内存对齐分配
  • 通过宏定义控制debug打印
  • 程序耗时性能测试
  • 线程池开源项目阅读
  • C++类中包含没有默认构造函数的成员
  • C++可变参数模板
  • C++属性继承 public protected private
  • C++智能指针
  • C++导出so的方法,以及extern C 注意事项
  • 四种spin lock
  • condition_variable和unique_lock
  • dpdk、kernel bypass
  • 智能网卡solarflare、Mellanox、X10等
  • 汇编寄存器和常见指令
  • c++ 类的静态成员变量未定义
  • C++获取类成员函数地址
  • preload示例
  • C++异常安全和RAII
    • 异常处理时析构函数仍会执行。
    • 异常的基本使用
    • 用RAII处理异常安全
  • C++11单例模式
  • C++歪门邪道
  • boost-program-option用法
  • c++17通过模板获取类成员的个数
  • 通过模板实现结构体成员拷贝-按成员名匹配
  • STL学习路径
  • boost库安装使用方法
  • C++文件读写
  • linux下socket通信demo,server和client
  • makefile写法
  • RxCpp
  • C++
gaoliu
2022-04-24
目录

C++异常安全和RAII

# 异常处理时析构函数仍会执行。

// #define _GNU_SOURCE
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <string>
#include <iostream>

using namespace std;

class CTest {
public:
    CTest() {
        printf("CTest construct\n");
    }
    ~CTest() {
        printf("~CTest deconstruct\n");
    }
};

int main() {
    try {
        {
            CTest a;
            throw 1;
            printf("after throw\n");
        }
    }
    catch(int t) {
        printf("catch int t=%d\n", t);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34

输出:

CTest construct
~CTest deconstruct
catch int t=1
1
2
3

可见 CTest a 仍然执行了析构函数,并不会因为throw异常而不执行析构。

# 异常的基本使用

参考 https://blog.csdn.net/ccc369639963/article/details/122905277 (opens new window)

在 C++ 中,我们使用 throw 关键字来显式地抛出异常,它的用法为:

throw exceptionData;

exceptionData 是“异常数据”的意思,它可以包含任意的信息,完全有程序员决定。exceptionData 可以是 int、float、bool 等基本类型,也可以是指针、数组、字符串、结构体、类等聚合类型,请看下面的例子:

char str[] = "http://c.biancheng.net";
char *pstr = str;
class Base{public: int mm=123;};
Base obj;

throw 100;  //int 类型
throw str;  //数组类型
throw pstr;  //指针类型
throw obj;  //对象类型

try{
  throw obj;
} catch (Base b){
  printf("catch Base, b.mm=%d\n", b.mm);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

throw的对象直接catch用就行了,没有任何特殊之处。下面自定义一个 HpcException 类

// #define _GNU_SOURCE
#include <unistd.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <string>
#include <iostream>

using namespace std;

// max msglen 4096;
class HpcException: std::exception {
public:
    template<typename... Args>
    explicit HpcException(const char* format, Args... args) throw() {
        constexpr int msglen = 4096;
        char buf[msglen];
        std::snprintf(buf, msglen, format, args...);
        what_.assign(buf);
    }
    const char* what() const throw()
    {
        return what_.c_str();
    }
private:
    std::string what_;
};



int main() {
    try {
        int aa = 5;
        throw HpcException("some thing wrong: aa=%d", aa);
    }
    catch(HpcException e) {
        printf("catch exception: %s\n", e.what());
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

# 用RAII处理异常安全

参考: https://www.cnblogs.com/mavaL/articles/2515381.html (opens new window)

假设需要一段下面逻辑的代码:

void User::AddFriend(User& newFriend)
{
    friends_.push_back(&newFriend);//操作1
    try
    {
        pDB_->AddFriend(GetName(), newFriend.GetName()); //如果该语句异常,则撤销操作1
    }
    catch (...)
    {
        friends_.pop_back();//撤销操作1
        throw;
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13

上面的操作很繁琐,而且有多个撤销操作时,代码会非常复杂。

此时可以通过ScopeGuard来实现,方便快捷,逻辑清晰:

void User::AddFriend(User& newFriend)
{
    friends_.push_back(&newFriend);
    ScopeGuard guard = MakeObjGuard(friends_, &UserCont::pop_back); //创建一个guard对象, 绑定撤销函数, guard析构时会调用该撤销函数
    pDB_->AddFriend(GetName(), newFriend.GetName());

    guard.Dismiss();//只有执行了Dismiss, guard在析构时才不会执行撤销函数
}
1
2
3
4
5
6
7
8

注意在实现ScopeGuard类时,调用撤销函数时要加上try catch(...),因为析构函数中不应该抛出异常。

编辑 (opens new window)
上次更新: 2023/05/07, 17:27:54
preload示例
C++11单例模式

← preload示例 C++11单例模式→

最近更新
01
通过模板实现结构体成员拷贝-按成员名匹配
05-07
02
c++17通过模板获取类成员的个数
05-01
03
avx-sse切换惩罚
04-30
更多文章>
Theme by Vdoing | Copyright © 2019-2023 Colder Leo | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×