文章目录
问题摘录自《从缺陷中学习C/C++》
1.基础问题
1.1 运算符优先级
//to get 2*n+1
int func(int n)
{
return n<<1 + 1;
}
- 后果
上述代码中的函数func本意是期望计算2n+1,但程序实际运行结果是4n。
- 分析
这段代码使用左移1位来代替乘以2的运算,是很好的方法,但是编程者弄错了运算符“<<”和“+”的优先级。C/C++语言规定运算符“+”的优先级高于运算符“<<”,因此,上述语句“return n<<1+1”等同于“returnn<<(1+1)”,所以,会先进行加法运算,再进行左移运算,得到结果4*n
- 处理解决
int func(int n)
{
return (n<<1)+1;
}
2.编译问题
3.库函数问题
4.文件处理问题
5.类和对象问题
1) 对象的浅复制
class IntList
{
public:
static const int SIZE = 10;
int *Items;
int numItems;
int arraySize;
public:
IntList()
{
Items = new int[SIZE];
numItems = 0;
arraySize = SIZE;
}
~IntList()
{
cout << "delete" << endl;
delete Items;
}
};
void do_some_process(IntList *list)
{
IntList tmp;
// do something else
*list = tmp;
}
int main()
{
IntList list;
cout << "Items : " << *list.Items << endl;
do_some_process(&list);
cout << "Items : " << list.Items[0] << endl;
return 0;
}
- 问题
程序运行时出现段错误
- 分析
1)如果类中没有对“=”操作符重载或没有提供赋值构造函数,那么对象间的复制只是浅复制(shallow copy),浅复制的意思就是C++只会对对象中的每个成员使用赋值运算符。当类很简单的时候(如没有动态分配内存的情况),浅复制不会出问题。但是如果其中一个类成员变量为指针变量,并且指向动态分配的空间,那么在赋值之后,被赋值的对象的指针变量将会指向原对象动态分配的空间,因此原对象被析构之后,被赋值对象的指针就变成悬挂指针。
2)上述代码中,do_some_process 函数中两个对象之间的赋值操作*list = tmp只是将对象tmp的内存内容复制给对象list,因此,两个对象的指针成员Item都指向了同一片地址。当do_some_process函数退出时,因tmp是函数内临时对象,它的生命周期也结束,这时会调用析构函数释放掉tmp所占用的内存,包括Item所指向的地址,导致list成员Item成为一个野指针,一旦操作这个野指针就可能会造成程序崩溃。
- 改正方法
// 在类IntList中重载“=”操作符,如下
IntList & operator=(const IntList &l)
{
this->numItems = l.numItems;
this->arraySize = l.arraySize;
memcpy(this->Items, l.Items, sizeof(int) * this->numItems);
}
2) 构造函数中的操作符重载造成死循环
- 问题代码
class MyClass
{
private:
int data;
public:
MyClass(void)
{
data = 0;
}
MyClass(const MyClass &i_class)
{
cout << "construct MyClass" << endl;
*this = i_class;
}
MyClass operator = (const MyClass &i_class)
{
Cout << "operate=" << endl;
Data = i_class.data;
return *this;
}
void put(const int value)
{
data = value;
}
int get(){
return data;
}
};
int main()
{
cout << "main" << endl;
MyClass first;
first.put(10);
MyClass second(first);
Cout << "second get " << second.get() << endl;
return 0;
}
- 现象&后果
程序编译没有问题,但是执行时陷入死循环
- Bug分析
1)在 C++中,如果一个函数返回值(非引用)时,会生成一个匿名的临时变量并将函数返回值赋值给匿名的临时变量;如果函数返回引用,则不会生成临时变量。例如:int func()函数的返回值为 int型,在执行 int n = func()语句时会首先生成一个匿名的临时变量,并将函数 func()的返回值赋值给匿名的临时变量,然后再将匿名的临时变量值赋值给 n。这里有两次赋值的操作。int &func()函数的返回为引用,在执行 int n = func()语句时,会把返回引用直接赋值给 n,这里只有一次赋值的操作。
2)在上段代码中,MyClass 类定义了两个构造函数和一个操作符重载函数,在构造函数MyClass(const MyClass &i_class)中,两个对象赋值时会调用操作符重载函数。调用操作符重载函数后会生成临时变量,并把操作符重载函数的返回值赋值给临时变量,这个过程会再次调用构造函数MyClass(const MyClass &i_class)和操作符重载函数……,从而导致反复调用构造函数及操作符重载函数,程序陷入死循环。
- 解决方法
解决这个问题,只需把操作符重载函数的返回值类型改成返回引用类型即可。
class MyClass
{
private:
int data;
public:
MyClass(void)
{
data = 0;
}
MyClass(const MyClass &i_class)
{
Cout << "construct MyClass" << endl;
*this = i_class;
}
MyClass& operator = (const MyClass &i_class)
{
Cout << "operate=" << endl;
Data = i_class.data;
return *this;
}
void put(const int value)
{
data = value;
}
int get(){
return data;
}
};
int main()
{
cout << "main" << endl;
MyClass first;
first.put(10);
MyClass second(first);
cout << "second get " << second.get() << endl;
return 0;
}
6.内存使用问题
7.多线程问题
8.性能问题
9.其他问题
9.1 中文截断成乱码
- 代码
const int MAX_LEN = 1024*1024;
string line,str;
while(getline(cin,line))
{
str = line;
if(line.length>MAX_LEN)
{
str = line.substr(0,MAX_LEN-1);//截断
std::cout<<str<<std::endl;
}
}
- 后果
上面代码的目的是对输入字符串做长度判断,当输入字符串过长时,会做截断处理。在程序运行时发现,当输入字符串中包含有汉字等宽字节字符时,截断操作可能会造成乱码。
- 原因
上面代码中,当输入的数据的长度大于1MB时,会被截断成1MB大小。但如果输入数据使用UTF-8编码,这种截断方式就有可能造成乱码。UTF-8是一种不定长编码,通常情况下,英文字母、数字和普通字符占用一个字节,中文字符占用 3个字节。因此,若输入的数据有中文,且刚好从中文的3个字节中间截断,那么再以UTF-8对数据做解码时就会失败,从而造成乱码
- 处理方案
截断后,对数据做编码识别,若非预期的编码或识别失败,说明有被截断的嫌疑,则往后多取一位或者往前少取一位,再进行识别直到成功为止。编码的识别比较复杂,可以使用第三方比较成熟的库,如著名的开源软件ICU库
- 建议
涉及字符串截断的操作,要考虑数据来源的编码格式,尤其是像UTF-8这种非定长的编码。
10.其他知识补充:
1)C++中包含C的代码
#ifdef __cplusplus
extern "C"
{
#endif
...
#ifdef __cplusplus
}
#endif
- 如果将Lua作为C代码编译出来后又要在C++中使用,那么可以引入lua.hpp来替代lua.h,定义如下:
extern "C"{
#include "lua.h"
}
11.其他
1)C++:switch提示“控制传输跳过的实例化”
- 解决方法:
解决方法很简单,将case下的语句加上"{}"即可,也就是写成
switch (m_nCardType) {
case MODBUS485_MASTER:{
CModBus485MasterDlg MDlg;
MDlg.DoModal();
break;
}
default:
break;
}
2)定义自己的max和min函数
#define max(a,b) ((a) > (b) ? (a) : (b))
#define min(a,b) ((a) < (b) ? (a) : (b))
3)结构体前置申明未定义问题
误 1 error C2079: “ud_x”使用未定义的 struct“ud”
结构体前置申明时,定义的变量只能是指针类型 如 struct_x *x;
如果是对象则编译器无法判读结构体大小导致提示使用未定义。
上面都是个人了解,不保证完全准确,仅供参考。