这条帖子主要是记录一些在使用C++的时候踩过的坑,不断更新!
编译阶段
编译阶段的坑主要来源于语法,这一阶段的错误基本是个IDE都会带提示,但是针对这部分的问题还是进行针对性记录;
- 警告:在有返回值的函数中,控制流程到达函数尾
缺少return,主要是考虑到如果遇到if条件之外的条件,那就没有返回值了,所以会报警告。
- reference to non-static member function must be called
翻译成中文就是:对非静态成员函数的引用必须被调用;但其实报这个错的场景多种多样,其中一种情况就是之前刷LeetCode遇到的坑;
在
Solution类中定义了一个成员函数用来做sort排序函数的谓词,结果出现了这个报错;原因其实比较简单,因为
sort需要知道这个比较函数的地址才能进行调用,而类的成员函数的调用一定都是隐含了一个this指针来定位到它的位置,但sort显然并非类的成员函数,所以他找不到这个用来比较的成员函数;解决方案有两种:
- 根据报错的提示,在类外部调
sort,然后传谓词时,利用类的对象访问,这样是没问题的; - 将比较函数定义成
static的,static不存在this指针的说法,可以直接调用;
- 根据报错的提示,在类外部调
链接阶段
链接阶段遇到的错误真的就需要认真思考了,亲身遇到的第一个例子是关于模板的使用;
- 链接阶段出现报错提示:
1 2 3
main.o: In function `main': main.cpp:(.text+0x34): undefined reference to `int func<int>(int const&, int const&)' collect2: error: ld returned 1 exit status
一般而言,我们在C++中会将头文件的声明和定义分离,这是一种很普遍的做法,也正是这种做法,导致了在使用模板的情形下出现该链接错误提示;
看个例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
// add.h直接对函数模板进行了定义 template <typename T> T add(const T &a, const T &b) { return a + b; } // main.cpp #include "add.h" int main() { int i = add(1, 1); return 0; }
这个例子的头文件包含了函数的声明和定义,这样做当然肯定是没错的,顺利编译链接通过;
然后我们改一下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
// add.h只有函数的声明 template <typename T> T add(const T &a, const T &b); // add.cpp中定义头文件声明的函数 #include "add.h" template <typename T> T add(const T &a, const T &b) { return a + b; } // main.cpp #include "add.h" int main() { int i = add(1, 1); return 0; }
这个时候在链接的过程中则会出现上述的报错;
因为对于编译器而言,处理函数模板时,他只负责读取与该模板有关的源文件,换句话说,即便
main函数将模板特例化为了int,也没人告诉编译器要去编译add.cpp;那应该怎么解决这个问题呢?改造编译器,重写
gcc,要么就是,在头文件中,告诉编译器,我后面要特例化int类型,现在请你去找到针对这个特例化的定义,然后编译他,这个时候就相当于有人告诉编译器要去编译add.cpp了;这样是可以解决问题,但是你
main函数每调用一种类型就得往头文件加一种类型,太繁琐了,还不如将函数的声明和定义放在一个头文件中;所以从这里可以看到,计算机中有很多约定俗成的习惯,但是这些习惯不能奉为教条!
这样会不会导致一个新的问题:定义放在一个头文件,会不会出现重定义呢?