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):
- RPATH 使用编译选项-r指定
- LD_LIBRARY_PATH
- /etc/ld.so.cache
- /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++没有规范顶