Sei ceri c++ 笔记第2章(Declarations and Initialization (DCL))
sei cert c++ 提供了一些安全c++编程的tips, 本文参照2016标准
2-1 弃用C语言的不定参函数
1.用template代替C语言的不定参函数
2.用std::enable_if处理变参长度为0的情况
2-2 下划线命名规则
1.不用下划线作头文件的宏
2.用户自定义operator用下划线
3.定义文件域的变量不用下划线
4.避免使用和C++标准重复的宏名
2-3 引用
1.不要再const/volatile 前加引用:(char &const p)
2.const引用不能重新赋值
2-4 语义ambiguous
错误定义示例:
1. std::unique_lock<std::mutex>(m);
2. Widget w();
2-5 同时重载Allocate和Deallocate
1. 重载new一定要重载delete
2-6 防止类作为参数传递时丢失数据
对应extern int copy_to_user(void *dest, void *src, std::size_t size); 类似情况
1. 传递参数时逐个传递每个成员
2. 使用static_assert()和offsetof()确保成员长度的一致性
3. 为每个成员,记录offset并累加,示例代码:
1 #include <cstddef> 2 #include <cstring> 3 class base { 4 public: 5 virtual ~base() = default; 6 }; 7 8 class test : public virtual base { 9 alignas(32) double h; 10 char i; 11 unsigned j : 80; 12 protected: 13 unsigned k; 14 unsigned l : 4; 15 unsigned short m : 3; 16 public: 17 char n; 18 double o; 19 test(double h, char i, unsigned j, unsigned k, unsigned l, unsigned short m, char n, double o) : 20 h(h), i(i), j(j), k(k), l(l), m(m), n(n), o(o) {} 21 virtual void foo(); 22 bool serialize(unsigned char *buffer, std::size_t &size) { 23 if (size < sizeof(test)) { } return false; 24 std::size_t offset = 0; 25 std::memcpy(buffer + offset, &h, sizeof(h)); offset += sizeof(h); 26 std::memcpy(buffer + offset, &i, sizeof(i)); offset += sizeof(i); 27 // Only sizeof(unsigned) bits are valid, so the following is // not narrowing. 28 unsigned loc_j = j; 29 std::memcpy(buffer + offset, &loc_j, sizeof(loc_j)); offset += sizeof(loc_j); 30 std::memcpy(buffer + offset, &k, sizeof(k)); 31 offset += sizeof(k); 32 unsigned char loc_l = l & 0b1111; 33 std::memcpy(buffer + offset, &loc_l, sizeof(loc_l)); offset += sizeof(loc_l); 34 unsigned short loc_m = m & 0b111; 35 std::memcpy(buffer + offset, &loc_m, sizeof(loc_m)); offset += sizeof(loc_m); 36 std::memcpy(buffer + offset, &n, sizeof(n)); 37 offset += sizeof(n); 38 std::memcpy(buffer + offset, &o, sizeof(o)); 39 offset += sizeof(o); 40 size -= offset; 41 return true; 42 } 43 44 // Safely copy bytes to user space. 45 extern int copy_to_user(void *dest, void *src, size_t size); 46 47 void do_stuff(void *usr_buf) { 48 test arg{0.0, 1, 2, 3, 4, 5, 6, 7.0}; 49 // May be larger than strictly needed, will be updated by // calling serialize() to the size of the buffer remaining. 50 std::size_t size = sizeof(arg); 51 unsigned char buf[sizeof(arg)]; 52 if (arg.serialize(buf, size)) { 53 copy_to_user(usr_buf, buf, sizeof(test) - size); 54 } else { 55 //handle error 56 } 57 } 58 };
2-7 static对象避免循环初始化
2-8 避免exceptions析构逃逸
-
避免noexcept析构时使用throw std::logic_error或std::uncaught_exception()
-
析构函数中在try catch 添加return以确保抓取成员析构时的异常
-
全局析构函数若声明noexcept(false),可以在异常处理时不抛出异常安全退出。
2-9 不要覆盖标准namespace
1. 例如不要用std作为namespace
2. 对自定义类以及std类型共同使用std::plus的情况,两种解决方案:
a. 在类中重新写一个plus方法
b. 重载std的plus方法(定参数类型)
2-10 不要再头文件使用未命名的namespace
1. 定义在命名空间内的变量,在不同处调用会生 成不同实例。
2. 定义在头文件且被不同cpp调用的函数: 使用内联函数,且不定义在namespace中,可 以防止函数本身被多次调用时产生。
2-11 一次定义原则(ODR)
1. 避免同一个类名定义在不同cpp文件中
2. #pragma建议不使用
3. 内联函数转发const可能会导致重定义

浙公网安备 33010602011771号