《从缺陷中学习c/c++》摘要 刘浙新登著

char 转int时,高位拓展问题

  • 会根据最高位而不是符号位进行拓展

int 转char 时的数据丢失

  • 系统默认的char 可能是unsigned char 或者signed char
  • getchar()返回的是int 使用 "!="进行判断时,若使用下式需要小心
while((char c=getchar())!=EOF){...}

signed -> unsigned

  • sizeof()与负数的比较

临时变量溢出

  • long = int * int时 等式右边先保存为int,然后强制转换到long
  • 需要每个值先强制转换成long再计算

std::cin与getline()混用

  • cin不会主动删除换行符,若后续使用getline()会跳过

2编译问题

2.1 动态库加载

  • 动态链接库加载顺序(ldd):
    1. RPATH 使用编译选项-r指定
    2. LD_LIBRARY_PATH
    3. /etc/ld.so.cache
    4. /lib和/usr/lib
      注:so前使用export LD_LIBRARY_PATH

2.2 使用命名空间,防止冲突

2.3 模板编译时的变量查找

  • 分为两个阶段:非依赖名称;依赖名称
  • 由于当前编译器没有实现依赖名称的查找
  • 之前的编译器由于没有实现非依赖名称查找,所有变量名都在实例化过程中进行
  • 注:解决的方式是将模板中的非依赖名称转成依赖名称

2.4 单定义原则(ODR)

  • #define _GLIBCXX_DEBUG 编译结果与标准c++略有差别 要放在被调用库的头文件

2.5 编译器优化

  • -Q2优化时,不允许两个变量访问同一片内存
  • -fno-strict-aliasing 可以避免上面的优化

3 库函数

3.1 sprintf()dst小于src长度时会出错

  • 使用snprintf()

3.2 snprinf()

  • 第三个字符串中有格式化语句时可能出错
  • 该函数有4个参数形式,以上情形将若没有第四个参数就可能造成,读取异常数据
  • 解决方法是写全参数,第三个参数"%s"
  • snprinf()的返回值是src字符串的大小,而不是考培成功的
char buf[10]=""
char src[10]="hello %s"
snprintf(buf,sizeof(buf),src)

3.3 字符串拷贝不全

  • 当src种包含"\0"时 strcpy()可能拷贝不全
  • 使用memcpy()

3.4 sting c_str()方法出错

const char *ptr=string.c_str()
当string.append("zz')时,原先指向的,c_str()失效成为野指针

3.5string[]操作符出错

  • 只用[]改变string储存的值不会使string.size发生改变,因此总是处于empty状态

3.6 strncpy()

  • strcpy()结束复制后都会给dst加上’\0’
  • strncpy不会自己加

3.7 memcpy()要先memset()清空dst

  • 否则会有奇怪的数据

3.8 string.find()

  • 与string::npos 比较判断是否搜索到

3.9 stringstream 的数据清空

  • stringstream输入数据,输出到字符串后仍会有内存泄漏
  • 使用stringstream.clear()不能清空缓存
  • 使用string.str("")来清除

3.10 strptime()

  • 不会初始化传进来的tm,因此使用前需要初始化
  • struct tm info={0}

3.11 feof()判断数据读完

  • feof()检测的是文件的结束标志是否被置位
  • fget()在读到最后一行时将结束标志置位

3.12 vector insert()

  • insert()在某位置前插入元素,并指向刚插入的元素

3.13 multiset erase()

  • 表示删除所有等于值的元素

3.14 容器类 erase()

  • 关联式容器没有在删除后指向下一个有效值的方法,虽然VS实现了,但是gcc没有实现,慎用

3.15 getopt()待测试

3.16 errno

  • 运行正常时不会重置,出错时会重置
  • 因此不能作为运行正常的标志

3.17 strdup()

  • 注意返回的字符串需要手动释放

4 文件处理

忘记关闭文件,socket文件描述符

getline()使用不当陷入死循环

  • 一次读入足够数据时会将输入流状态设置为failbit

  • 而在判断输入流状态为failbit 时,会返回空字符串

  • 若处于判断文件是否结束的循环中会形成死循环

  • 解决方法:每次读取后重置输入流状态

  • 在读取到文件尾时,设置输入流状态为eofbit

  • 读完后再次使用getline()可能读不到数据

5 类和对象

5.1 对象浅复制

  • 当类包含指向动态内存的指针变量时,若没有提供赋值构造函数,浅拷贝后可能造成造成两个指针共享内存,某一类析构后,另一指针就成为野指针,造成崩溃
  • 解决方法:重载赋值操作,使为每个指针申请内存

5.2 赋值操作符重载

  • 若赋值操作符的返回值不是引用,返回值会调用拷贝构造函数
  • 当构造函数使用了赋值重载函数
  • 于是产生循环调用
  • 解决:将赋值返回值改为引用

5.3 拷贝构造函数不能模板化

  • 模板化的构造函数不会被当作拷贝构造函数使用

5.4 析构函数不能抛出异常

  • 析构函数中抛出异常无法被捕获,会导致程序崩溃

5.5 构造函数中抛出异常可能导致内存泄漏

  • 构造函数中抛出异常不会调用析构函数

5.6 多态性未生效

  • 实现多态性的关键是虚函数

5.7 成员函数被隐藏

  • 重载

    • 同i各类
    • 函数名相同
    • 参数不同
  • 覆盖

    • 范围不同(基类和派生类)
    • 函数名相同
    • 有virtual
    • 参数相同
  • 隐藏(基类和派生类)

    • 同名不同参,都会隐藏
    • 同名同参,无virual会被隐藏
  • 因此派生类中有基类的同名函数时,应该全部重写重载函数或者使用显示声明基类名字空间作用域

5.8 匿名对象引起的内存泄漏

  • new 之后都需要释放内存

5.9 基类非虚析构函数引发内存泄漏

  • 基类若没有定义虚构函数,则会自动生成默认的非虚析构函数
  • 当派生类通过一个基类指针删除时,如果这个基类的虚构函数是非虚的,则析构行为是未知的。子类的析构函数不会被调用
  • 解决方法:将基类的析构函数定义为虚函数

5.10 删除void* 引发内存泄漏

  • c++标准明确说明,针对void*指针调用delete
  • 在调用delete释放内存前会调用析构函数,若对象为void*析构函数不能正常调用

5.11 STL容器不会自动释放指针指向的对象

5.12 静态成员类内初始化

  • 类内初始化是无效操作
  • 例外:整形const static(与字符型)可以在类定义体内初始化

5.13 union做为类的成员时需要构造函数

  • union可以有构造/析构函数,但不能有静态变量/方法
  • 使用union时,需要防止union的异常访问,可以提供一个标识符来标识该union的状态,给union提供一组统一的接口

5.14 一个const对象只能使用const类型的函数

5.15 用memset 初始化一个类 不建议

5.16 dynamic_cast 转换失败返回NULL

  • dynamic_cast 在执行类型转换时。先检查是否转换成功;若指针转换失败,返回0;若引用转换失败,则抛出bad_cast异常
  • 父类向子类转换时,父类空间大于子类,因此会转换失败

6 内存使用

6.1 数组传递时的sizeof

  • 数组作为形参进行转递时,会退化成指针

6.2 编程风格

  • 谁申请谁释放

6.3 函数中途退出未释放内存

6.4 二维数组内存泄漏

  • 逐行逐列删除

6.5 返回值是个引用时

  • 临时内存变量不能返回

7 线程

7.1 string的append()非线程安全

7.2 两个线程锁之间谨慎中途退出

  • 否则可能造成某一线程被阻塞的问题(旧的)

7.3 union结构体在线程

  • 对于位域操作并不保证是原子操作,依赖于目标机器和编译器
  • 若要保证线程安全,需要自己添加措施

7.4 多线程写文件导致内容被覆盖

  • 若采用先lseek()到文件末尾,再写入文件,可能造成文件内容丢失
  • 可以设置文件打开的模式为O_APPEND

7.5 线程未释放

  • pthread_create()创建的线程,默认是join.即需要等到pthread_join()才会释放资源

8 内存优化

8.1 尽量少用即时计算数组的尺寸数量等

8.2 vector的内存不能用clear()进行回收

  • 只是删掉了内部储存的元素,调用了析构函数,并没有压缩空间
  • 可以用swap():vector().swap(l_target)

8.3 calloc()高版本(2.5)比低版本(2.3.4)下速度低一倍

  • calloc()与malloc()的区别是calloc()会将申请的内存初始化为0
  • 如果不需要以上功能,可以使用malloc()

9 其他问题

9.1 中文截断出现乱码

  • 字符串中包含宽字节字符时可能造成乱码,UTF-8不定长字符

  • 注意考虑数据来源的编码格式,利用成熟的库进行处理

9.2 不确定的函数参数赋值

  • int add(int x,int y )函数以add(a,a++)方式调用时,结果不确定
  • 无法确定参数的顺序,c++没有规范顶
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值