C++面试题

说明:本文题目来源于牛客网,答案分析来源个人,仅为参考

fengjingtu

题目

1、(不定项选择)引用与指针有什么区别?

A、指针是一个实体,而引用仅是个别名
B、指针没有 const,引用有 const;
C、引用不能为空,指针可以为空;
D、“sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的E变量或对象的地址)的大小;
E、从内存分配上看:程序为引用变量分配内存区域,而指针不需要分配内存区域
F、指针和引用的自增(++)运算,意义一样


2、如果是类 B 在类 A 的基础上构造,那么就称( )。

A、类 A 为基类、父类或超类,类 B 为派生类、子类或友元类

B、类 A 为基类、父类或超类,类 B 为派生类或子类

C、类 A 为派生类、子类或友元类,类 B 为基类、父类或超类

D、类 A 为派生类或子类类,B 为基类、父类或超类


3、下面程序的输出是()
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
class A
{
public:
void foo()
{
printf("1");
}
virtual void fun()
{
printf("2");
}
};
class B: public A
{
public:
void foo()
{
printf("3");
}
void fun()
{
printf("4");
}
};
int main(void)
{
A a;
B b;
A *p = &a;
p->foo();
p->fun();
p = &b;
p->foo();
p->fun();
A *ptr = (A *)&b;
ptr->foo();
ptr->fun();
return 0;
}

A、121434

B、121414

C、121232

D、123434

4、STL中的unordered_map和priority_queue使用的底层数据结构分别是什么?()

A、rbtree,queue

B、hashtable,heap

C、rbtree,heap

D、hashtable,queue

new、delete、malloc、free关系

new和malloc在动态开辟空间的时候使用,delete和free在释放动态开辟的空间时候使用,new和delete对应,malloc和free对应。

new和delete是运算符,malloc和free是函数。

new一个类的时候,会调用构造函数,delete一个类的时候调用析构函数。malloc和free不会调用构造函数和析构函数,原因是:malloc和free是库函数不是运算符,他们不再编译器的控制权限之内。不能够把构造和析构的任务强加于他们。

对于非内部数据类型的对象而言,malloc和free是不够用的。

delete和delete[]

delete只会调用一次析构函数,而delete[]会调用每一个成员的析构函数。delete和new配合,delete[]和new[]配合。当delete操作符用于数组时,它为每一个数组成员调用析构函数。

子类析构的时候要调用父类的析构函数吗

析构函数的调用顺序与构造函数的调用顺序相反,构造时先调用父类的构造函数,然后调用子类的构造函数,析构时候先调用子类的析构函数,再调用父类的析构函数。

多态,虚函数,纯虚函数

多态:对于不同的对象接收相同的消息时候产生不同的动作,C++的多态性具体体现在运行和编译两个方面:在程序运行时的多态通过集成和虚函数来体现,在程序编译时候的多态性体现在函数和运算符的重载上。

虚函数:在基类中,用关键字virtual修饰的函数,就是虚函数。允许在派生类中对基类的虚函数重新定义。

纯虚函数:在基类中没有定义,只有声明的虚函数,就是纯虚函数,含有虚函数的基类称为抽象类,抽象类不能被实例化。抽象类可以定义引用和指针。

什么是引用,声明和使用引用要注意什么问题

引用就是某个变量的别名,对应的操作与直接操作变量的效果相同。声明一个引用的时候,必须对他进行初始化,使用时,不能把引用作为其他变量的别名,不可以再被赋值。不能建立数组的引用。

将引用作为函数参数有哪儿些特点

传递引用和传递指针的效果是一样的,形参是实参的一个别名。

因为不需要创建新的对象,在内存空间,和拷贝构造函数方面效率高。

在什么时候需要使用常引用

在希望传递给函数的数据在函数中不要被改变的时候,就应该使用常引用。

答案与解析:

1 2 3 4
ACD B B B
1、答案:ACD。

解析:指针和引用的区别,ACD都是正确的。
B:指针有const,引用没有const
E:程序为指针变量分配内存区域,而引用不需要分配内存区域
F:指针和引用的自增++意义不同,如果a[0]++,对指针来说是指向a[1],对引用来说是数值加1.

2、答案:B。

友元类即外部类访问类的私有成员,没有继承结构。

3、答案:B

指针的调用取决于指针的类型。如果定义的是派生类指针,则该基类成员不可见(隐藏),但是若为基类指针,该基类成员仍然是可见的啊!因为此处的p和ptr均为基类指针,只是分别指向了基类和派生类对象,所以调用foo()的时候仍然是基类的成员。

但是如果定义个派生类指针pb,如下:

B *pb=&b;

pb->foo();

这时只会调用派生类的foo(),虽然B继承自A,但是基类的foo()会被隐藏。

但是一旦foo()里有参数的时候,你就会大吃一惊!

假设A中为void foo(float a),B中为void foo(int a):

做如下调用:

B *pb=&b;

pb->foo(3.14);

到底会调用谁?你可能会想: 首先foo()成 员不是虚函数,但是B继承A,B中有两个foo(),调用foo(3.14)时根据参数类型应该匹配基类的void foo(float)成员。

然而并不是!

因为触发了隐藏机制,基类的void foo(float)会被隐藏,所以即使你调用foo(3.14)仍然只会调用派生类的void foo(int)成员。

你的惊讶正好解释了隐藏机制存在的意义。

(PS:牛客网上C/C++专项训练上有专门一道题考察这种情况,当时解释里提出隐藏机制时大多数人也是一脸懵逼)

总结:

1.判断要点:如果不是重载也不是覆盖,派生类和基类中一旦出现同名函数,一定触发隐藏机制(这是个简便判断技巧,你可以考虑除去重载和覆盖的任何同名函数情况,一定满足隐藏机制触发的两条规则)。

2.隐藏触发的结果:指针对成员的函数调用取决于指针类型。

若本身是基类指针(不管指向基类还是派生类)则仍然调用基类成员(不会牵扯到派生类,此处是隐藏,和多态没关系,按第1点已说明隐藏的触发可以首先排除覆盖,也就是多态问题);

若本身是派生类指针,这时你就会看到隐藏的威力!此时不是简单地继承基类的成员,然后根据参数匹配调用,而是隐藏基类成员,只会调用派生类成员。

4、答案:B

unordered_map:是所谓的哈希map,很容易就选了hashtable

priority_queue:是所谓的优先级队列,说白了就是一个二叉堆,所以底层应该是用heap实现,并非名字中的queue

map: map内部实现了一个红黑树,该结构具有自动排序的功能,因此map内部的所有元素都是有序的,红黑树的每一个节点都代表着map的一个元素,因此,对于map进行的查找,删除,添加等一系列的操作都相当于是对红黑树进行这样的操作,故红黑树的效率决定了map的效率。