C++ Core Guideline 笔记02

I.1 Make interface explicit

不好的示例->

int round(double d)
{
    return (round_up) ? ceil(d) : d;    // don't: "invisible" dependency
}

这个例子我主要理解是这样的,函数名是round但是控制逻辑中有在调用时非常可能被忽略的全局变量round_up。我理解round_up应该出现在参数列表里。但是这种设计非常有可能出现在类的成员函数里,设想如下代码片段(非原文)

class A{
int round(double d) {
	return (round_up) ? ceil(d) : d;
}

round_up = true;
};

round()的调用点,我们还是要检查成员变量round_up的值才能期待round()给出正确的行为。原文最后有一个看似这种的guideline,就是如果函数比较简单,那么它不应该执行基于全局变量(namespace级别的变量)的逻辑。

I.4 Make interfaces precisely and strongly typed

作者强调了函数参数最好要typed,

void draw_rectangle(Point top_left, Point bottom_right);
void draw_rectangle(Point top_left, Size height_width);

draw_rectangle(p, Point{10, 20});  // two corners
draw_rectangle(p, Size{10, 20});   // one corner and a (height, width) pair

所有上述例子中,当参数直接使用数字时,很难推断出参数的含义。作者推荐使用enum作为boolean flag使用。

enable_lamp_options(lamp_option::on | lamp_option::animate_state_transitions);

所以,当一个函数接受超过两个bool变量作为参数时,这个函数的interface可能需要重新设计。作者再次强调参数的数值单位的重要性。在CppCon2017 Bjarne Stroustrup上做的报告有特殊讲到数值单位引起的bug导致一个NASA的火星卫星没有进入预定轨道,科研人员15年的研究付之东流。

I.5 State preconditions

推荐了GSLExpects()函数

I.6 Prefer Expects() for expressing preconditions

再次推荐使用Expects(),但是C++20估计都没有标准化。

I.7 State postconditions

下面代码为了确保res作为整形不会溢出。这种情况在进行整数运算时可能会出现,例如对RGB图像进行处理时,大部分时间数据对象可能是uint8_t类型,数值在0-255之间。

int area(int height, int width)
{
    auto res = height * width;
    Ensures(res > 0);
    return res;
}

另一个例子

void f()    // better
{
    char buffer[MAX];
    // ...
    memset(buffer, 0, sizeof(buffer));
    Ensures(buffer[0] == 0);
}

此实例中,不加入Ensures()时,编译器优化阶段可能会remove掉memset()
此外原文列出了一个多线程mutex锁的示例

void manipulate(Record& r)    // best
{
    lock_guard<mutex> _ {m};
    // ...
}

改示例确保m将在函数结束时释放,无论是否发生Exception。

I.9 If an interfaceis a template, document its parameters using concepts.

使用concept在编译期间对模板参数进行检查。特指出GCC6.1之后都对concept进行了支持。

I.10 Use exceptions to signal a failure to perfomr a required task

作者认为对于定义好的error,应该抛出异常而不是利用返回值表示状态。特别指出performance 并不是拒绝使用exception的好理由。若可以今早检查异常,可以提升critical code的performance。

I.11 Never transfer ownership by a raw pointer or reference

这里强调了rvalue 和move semantics。

I.12 Declare a pointer that must not be null as not_null

Wow, that’s nice.

int length(const char* p);            // it is not clear whether length(nullptr) is valid

length(nullptr);                      // OK?

int length(not_null<const char*> p);  // better: we can assume that p cannot be nullptr

int length(const char* p);            // we must assume that p can be nullptr

not_null定义在GSL中。

I.13 Do not pass an array as a single pointer

如下的代码可能产生多种错误

void copy_n(const T* p, T* q, int n); // copy from [p:p+n) to [q:q+n)

包括q的大小不足n,p的内容少于n。使用下述代码会更好

void copy(span<const T> r, span<T> r2); // copy r to r2

I.22 Avoid complex initialization of global objects

原文这里编号一下跳到了I.22。原文指出全局变量的初始化次序是不确定的,所以尽量不要使用。全局变量初始化依赖函数应该是constexpr

I.23 Keep the number of function arguments low

当函数的参数过多时,非常肯恩意味着“one function, one responsibility”的规则被打破。同时,过多的参数说明这些参数应当被抽象成type。
作者认为,4个参数以上的,都算是参数数量太多。

I.24 Avoid adjacent unrelated parameters of the same type

意思很简单,参数表里,如果两个参数的类型完全一致,但是参数次序不能调换,那么这种参数表容易被用错。
考虑将参数表定义成一个struct,这样在调用函数时需要用变量名指定函数的每一个参数,降低了发生错误的风险。但是这样做代码边长了。有点像python的keyword argument。

I.25 Prefer abstract classes as interfaces to class hierarchies

意思也比较简单,不定义base class,而只用abstract class。在abstract class中,没有成员变量,所有借口都是=0的虚函数。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值