1、term53:Pay attention to compiler warnings
许多程序员习惯性地忽略编译器警告。他们认为,毕竟,如果问题很严重,编译器应该给一个错误信息而非警告信息,不是吗?这种想法对其他语言或许相对无害,但在C++,则会出现非常严重的问题。例如:
class B {
public:
virtual void f() const;
};
class D: public B {
public:
virtual void f();
};
完整可编译的函数如下:
#include <iostream>
class B {
public:
virtual void f() const {
std::cout << "B::f() const" << std::endl;
}
};
class D: public B {
public:
virtual void f() {
std::cout << "D::f()" << std::endl;
}
};
int main() {
D d;
d.f(); // 调用 D 类中的非 const 成员函数 f()
return 0;
}
这里希望以D::f重新定义virtual函数B::f,但其中有个错误:B中的f是个const成员函数,而在D中它未被声明为const。我手上的一个编译器于是这样说话了:
warning: D::f() hides virtual B::f()
太多经验不足的程序员对这个信息的反应是:“噢,当然,D::f遮掩了B::f,那正是想象中该有的事!”错,这个编译器试图告诉你声明于B中的f并未在D中被重新声明,而是被整个遮掩了(条款33描述为什么会这样)。如果忽略这个警告,几乎肯定导致错误的程序行为,然后是许多调试行为,只为了找出编译器其实早就侦测出来并告诉你的事情。
一旦从某个特定编译器的警告信息获得经验,你将学会了解,不同的信息意味什么——那往往和它们“看起来”的意义十分不同!尽管一般认为,写出一个在最高警告级别下也无任何警告信息的程序最是理想,然而一旦有了上述的经验和对警告信息的深刻理解,你倒是可以选择忽略某些警告信息。不管怎样说,在你打发某个警告信息之前,请定你了解它意图说出的精确意义。这很重要。
记住,警告信息天生和编译器相依,不同的编译器有不同的警告标准。所以草率编程然后依赖编译器为你指出错误,并不可取。例如上述发生“函数遮掩”的代码就可能通过另一个编译器,连半句抱怨和抗议也没有。
- 请记住严肃对待编译器发出的警告信息。努力在你的编译器的最高警告级别下争取“无任何警告”的荣誉。
- 不要过度依赖编译器的报警能力,因为不同的编译器对待事情的态度并不相同。一旦移植到另一个编译器上,你原本依赖的警告信息有可能消失。
2、面试相关
(1)语法错误:
请描述一个常见的C++语法错误,并解释如何修复它。
编译器提示某个语句末尾缺少分号,你应该怎么做?
(2)类型不匹配:
当编译器报错指出类型不匹配时,通常意味着什么?
如果你尝试将一个整数赋值给一个浮点变量,会发生什么?
(3)未定义的标识符:
什么是未定义的标识符错误?它通常是由什么引起的?
如果编译器提示某个函数或变量未定义,你应该如何排查?
(4)头文件和库文件问题:
如果编译器找不到某个头文件,可能的原因是什么?
如何确保编译器能够找到外部库文件?
(5)拼写和大小写问题:
在C++中,为什么拼写和大小写很重要?
如果一个变量名的大小写在代码中不一致,会导致什么问题?
(6)链接错误:
什么是链接错误?它与编译错误有什么不同?
当链接器提示找不到某个函数的定义时,可能的原因是什么?
(7)预处理指令问题:
#include 预处理指令的作用是什么?
如果忘记包含某个必要的头文件,可能会导致什么问题?
(8)多文件项目中的编译问题:
在多文件项目中,如何确保所有文件都被正确编译?
如果一个文件依赖于另一个文件的定义,但后者没有被编译,会发生什么?
(9)编译器版本和设置问题:
不同版本的编译器可能会产生不同的结果吗?
如何检查和设置编译器的优化级别和警告级别?
(10)解决编译器报警的通用策略:
当编译器报警时,你通常会采取哪些步骤来定位和解决问题?
有没有一些通用的工具或技巧可以帮助你更快地解决编译问题?
3、总结
天堂有路你不走,地狱无门你自来。
4、参考
4.1 《More Effective C++》