面试中的C++问题(1)

虽然大部分面试都是问项目,然后一道代码题,但还是有少量公司很喜欢问“基础的”技术问题,比如C++、体系结构之类。因此开个新系列,记录一下面试里遇到的一些技术类问题。有一些很简单,也有一些可能也没那么常见。

explicit

很基础的问题,用于避免隐式类型转换。遇到过的一个坑是图形学光线追踪大作业的框架,里面Color类构造函数RGB三个值都给了默认值然后没加explicit,导致3.0f * Color(...)时会把3.0f隐式转化成Color...

数组初始化默认值

1
int a[8] = {1, 2, 3};

那么没有显式写出来的值都是0.

char类型转换

1
2
char a = -1;
int b = a;

b > 0是否成立。显然不成立,char是有符号的。

0, NULL, nullptr

这个问题没有那么显然。在C里,NULL定义为

1
#define NULL ((void *) 0)

因此NULL在C中可以安全地当成空指针使用。

但C++没那么轻松,因为C++不允许void *隐式转换成其他类型的指针,所以不能沿用C的做法;只能简单地定义为0:

1
2
3
4
5
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *) 0)
#endif

但C++支持同名函数重载,如果一个函数定义为

1
2
void func(int a);
void func(int *p);

那么func(NULL)就会导致二义性问题:预期调用指针版本的函数,但实际上参数为整形,调用了整形版本的函数。

为了解决这个问题,C++ 11引入了空指针字面量nullptr,其类型为std::nullptr_t,可以隐式类型转换为任意类型指针或类成员指针。在一些C++ 11之后的实现中,NULL定义为nullptr

延伸:类成员指针不是指针

类成员指针实际上是偏置量offset,而不是指向某一内存地址的指针。参考:Pointers to Member Functions.

如何在执行main函数前做一些自定义行为?

一个trick:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

class Foo {
public:
Foo() {
// do something...
}
};

Foo foo;

int main() {
// ...
return 0;
}

全局变量在main之前初始化,因此只要在自定义类型的构造函数里安排自定义行为,然后定义一个全局变量对象即可。

一个想法:或许这里用static Foo foo;更好。

模板函数能否在.cpp里实现,编译后成.o后再链接?

不行。本科写图形学大作业时踩过的坑,当时乔总就告诉我应该直接写在.hpp头文件里。

原理是template并没有定义任何东西,只有特化后确定了类型信息,编译器才能分配空间进而编译成.o,否则无法编译。

延伸:避免模板重复

据说可以用extern,没有尝试过。