一面阿里广告营销部门的引擎架构团队
面的是研发工程师C/C++
没回答上来的问题
C++的几种构造函数
- 默认构造函数
- 普通构造函数
- 复制构造函数(拷贝构造函数)
- 赋值构造函数(转换构造函数)
C++类成员的初始化顺序是按照初始化列表的顺序进行初始化的吗
是严格按照成员变量的声明顺序进行初始化的。
1
2
3
4class Node {
int a, b;
Node(int aa, int bb) : b(bb), a(aa) {}
};在这种情况下,是先初始化 $a$ ,再初始化 $b$ 。
复制构造函数为什么参数必须是引用类型的
如果拷贝构造函数中的参数不是一个引用,即形如
CClass(const CClass c_class),那么就相当于采用了传值的方式,而传值的方式会调用该类的拷贝构造函数,从而造成无穷递归地调用拷贝构造函数。因此拷贝构造函数的参数必须是一个引用。写代码的时候需要
new一个数组,但是我忘记了如何使用new进行申请一段连续的空间。1
2
3
4
5
6
7int *a = new int;
int *b = new int(4);//赋值为4
int *c = new int[5];//申请长度为5个int的空间
delete a;
delete b;
delete[] c;
问的问题
语言方面
- 对C++中
static关键字的理解 - 对C++中各种构造函数的理解
- 对C++中类的继承的理解
- 以及多态的用法
- 对C++中
操作系统方面
关于虚拟内存从上到下排序啥的?(不太记得了)
我当时回答是还没学到这一块。
关于多线程同时改变一个变量?(好像是)
我的回答是使用互斥锁,然后问我还有什么方法,我说可能还有信号,但是我只用过互斥锁。
关于一个场景,多个人需要从一个地方进行读和写,如何实现多个人同时进行读,并且有人写的时候没有人读。
我的回答是使用信号,比如
semaP和semaV这种的信号调用。就是在cs162课程中第9节课讲到的具体例子。接着对这个场景的实现进行了详细的描述。
现场编程
给了一个场景,如果要实现一个
string类,使用的是一段连续的空间,并且只有两个变量:这段连续空间的首地址、长度。写一个构造函数。
写一个复制构造函数。
刚开始在用
new进行申请空间,但是写着写着忘了怎么用new申请一段连续的空间了,就跟面试官说忘了怎么使用new了,因为最近使用的一直是malloc进行申请空间,然后面试官就让我用malloc进行实现了,并且还提醒了我使用配套的函数进行空间的释放。然后写完这部分之后问我有没有什么bug,然后给我说了一个场景,
a = a的时候会出问题,会把原本的空间给释放掉。写一个赋值构造函数。
我当时不记得赋值构造函数是什么了,就没写。
然后问我为什么写复制构造函数的时候必须使用引用变量,我没回答出来。
我当时写的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30class my_string {
private:
char *st;
int len;
public:
my_string() {
st = nullptr;
len = 0;
}
my_string(mystring &s) const {
if(this->st == s.get_st())
return;
if(st != nullptr)
free(st);
// this->st = new char[s.get_len()];
this->st = (char*)malloc(sizeof(char) * s.get_len());
this->len = s.get_len();
// strcpy(this->st, s.get_st());
for(int i = 0; i < len; ++i) {
this->st[i] = s.get_st()[i];
}
}
char* get_st() {
return st;
}
int get_len() {
return len;
}
};给出两个有序的单向链表,把这两个链表合并成为一个链表,并保持有序。
提醒了我如果一个链表已经结束,就可以直接
break掉了。当时写的
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57class Node {
public:
int val;
Node *next;
};
Node *merge(Node *a, Node *b) {
if(a == nullptr) {
if(b == nullptr) {
return nullptr;
}
return b;
}
else if(b == nullptr) {
return a;
}
Node *ret, *now;
if(a->val < b->val) {
now = ret = a;
a = a->next;
}
else {
now = ret = b;
b = b->next;
}
while((a != nullptr) || (b != nullptr)) {
if(a == nullptr) {
now->next = b;
break;
// now = b;
// b = b->next;
// continue;
}
else if(b == nullptr) {
now->next = a;
break;
// now = a;
// a = a->next;
// continue;
}
if(a->val < b->val) {
now->next = a;
now = a;
a = a->next;
}
else {
now->next = b;
now = b;
b = b->next;
}
}
return ret;
}问了一下关于二叉树的几种遍历方式,然后说还有一种遍历方式是按层来遍历,然后我简单说了一下实现方法,说还没有写过这种的,然后就让我现场实现一下。
这一部分没有什么问题。
当时写的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30class tree_node {
public:
int val;
tree_node *left, *right;
}
void func(tree_node *root) {
if(root == nullptr)
return;
queue<tree_node*> q;
q.emplace(root);
while(!q.empty()) {
tree_node *now = q.front();
q.pop();
printf("%d ", now->val);
if(now->left != nullptr) {
q.emplace(now->left);
}
if(now->right != nullptr) {
q.emplace(now->right);
}
}
printf("\n");
return;
}
总结
最后面试官给我介绍了他们部门和他们团队主要是做什么的,然后问了我之后是否要考研,然后是问我还有什么问题没有,让我进行提问。