C++踩过的坑

noexcept函数抛出异常

原本设计函数的时候明确该函数不会抛出异常,所以给函数加上了noexcept属性,但修改代码之后需要抛出异常,比如:

void func() noexcept {
    ...
    throw except();
}

即使像这种很明显的语法矛盾,有的编译器的版本并不会发出警告,于是进行测试和实际代码运行的时候在外层使用try catch块总是捕获不到异常,这一点可能会浪费你很多时间。

NULL匹配指针类型还是整型

在C++的世界中字面值0用来表示空指针,所以0可以当作所有指针类型的字面值。为了让语义更明确引入了NULL宏定义:

#undef NULL
#ifdef __cplusplus
    #define NULL 0
#else
    #define NULL ((void *)0)
#endif

这说明了在C++中,NULL是被替换为0的(在一些实现中NULL可能被定义为0L),这是因为C++不允许void指针隐式转换为其它类型指针,但是允许0作为各指针类型的字面值常量。
下面的场景可能会报二义性的问题:

void func(char *str) {
    printf("char *");
}

void func(int n) {
    printf("int");
}

int main() {
    func(NULL);

    return 0;
}

这里假设NULL被定义为OL,所以long型转换为intchar*存在二义性。

most vexing parse

C++'s most vexing parse 是 Scott Meyers 在其名著《Effective STL》中创造的一个术语。
初始化对象的时候会被当作函数的声明:

struct B {
    explicit B(int x){}
};

struct A {
    A (B const& b){}
    void doSomething(){}
};

int main() {    
    int x = 42;

    A a(B(x));
    
    a.doSomething();
}

这里A a(B(x));会被当作函数的声明,返回类型是A,参数类型是Bx是参数名。
在C++11中,可以用统一初始化语法,A a(B{x})来消除二义性。

printf类型不匹配

首先需要知道,当变量作为不定参数列表的参数时,charshort等类型会默认提升为int类型,float类型会默认提升到double类型,当printf函数中描述符和参数类型不匹配时,可能会造成读取非法内存:

int n = 0;
printf("%lld", n);

上述代码会读取非法内存,有的运行环境会直接报segment fault错误停止程序运行,但对于标准来说是未定义行为,需要避免。

全局变量初始化/销毁顺序

对于出现在同一个编译单元内的全局变量来说,它们初始化的顺序与他们声明的顺序是一致的(销毁的顺序则反过来),而对于不同编译单元间的全局变量,c++ 标准并没有明确规定它们之间的初始化(销毁)顺序应该怎样,因此实现上完全由编译器自己决定,一个比较普遍的认识是:不同编译单元间的全局变量的初始化顺序是不固定的,哪怕对同一个编译器,同一份代码来说,任意两次编译的结果都有可能不一样。

posted @ 2020-11-19 09:17  HachikoT  阅读(117)  评论(0编辑  收藏  举报