前面整理了两篇博客(C++ 基础知识 面试题(一),C++ 基础知识 面试题(二))
但发现这样整理很低效,所以还是打算做成问答题的形式。
Part 1是题目, Part2是答案。
Part 1
1.const修饰引用时,作用是什么?用于什么场景?
2.const修饰成员函数时,作用是什么?
3.const常量和宏定义有哪些区别?
4.static修饰普通变量时存储在哪里?特点是什么?
5.static修饰普通函数时,作用的什么?
6.static修饰成员变量时,什么时候可以在类内初始化?
7.static修饰成员函数时,可以访问类内所有成员吗?
8.static修饰成员变量和成员函数时有什么共同点?
9.this指针是什么?
10.inline内联函数是什么?什么情况下会默认为内联函数?
11.内联的优点和缺点是是什么?
12.虚函数可以是内联函数吗?
13.创建和删除子类对象时,是如何调用构造函数和析构函数的?
14.父类的析构函数为什么必须是虚函数?
15.volatile关键字声明的变量有什么特点?应用场景有哪些?
16.常量可以用volatile关键字修饰吗?指针可以用volatile关键字修饰吗?
17.assert()是什么?可以关闭吗?
18.用extern修饰变量,作用是什么?
19. extern "C" 的作用是什么?
20.当定义了一个结构体 struct Apple{...}, 声明这个结构体的时候,可以直接使用Apple a;而不是struct Apple a;吗?
Part2
1.const修饰引用时,作用是什么?用于什么场景?
一般用于形参,这样可以避免拷贝,同时函数无法对形参进行修改。
2.const修饰成员函数时,作用是什么?
作用是成员函数不能修改成员变量。
3.const常量和宏定义有哪些区别?
const常量是常量声明,由编译器处理,有类型安全检查,系统会分配内存,保存在数据段,一旦声明不能取消。
宏定义是字符替换,由预处理器处理,没有类型安全检查,系统不会分配内存,保存在代码段,可以用#undef取消。
4.static修饰普通变量时存储在哪里?特点是什么?
static普通变量存储在静态存储区。
特点是仅在第一次调用时初始化,并在程序的整个生命周期都保持初始化时的值。
5.static修饰普通函数时,作用的什么?
作用是表明该静态函数只能在定义该函数的文件中使用,防止与其他文件中的函数重名。
6.static修饰成员变量时,什么时候可以在类内初始化?
一般情况下,static修饰的成员变量只能在类外初始化。
除非该变量同时也是常量或者枚举类型。
7.static修饰成员函数时,可以访问类内所有成员吗?
静态成员函数只能访问静态成员。
8.static修饰成员变量和成员函数时有什么共同点?
静态成员变量和静态成员函数都是属于类的,而不是属于对象的。
都只有一个副本,只会被分配一次内存。
无需创建对象,直接使用类名就能调用。
9.this指针是什么?
this指针是隐含在每一个非静态成员函数中的特殊指针,指向调用该成员函数的对象。
10.inline内联函数是什么?什么情况下会默认为内联函数?
内联函数会在调用该函数的位置直接展开函数体。
在类内定义成员函数时,除虚函数以外的成员函数都会被默认为内联函数。
当在类外定义成员函数时,成员函数会被默认为非内联函数。
11.内联函数的优点和缺点是是什么?
优点是:
- 节省开销;
- 和宏定义相比,有类型检查,可以调用类的成员变量,在运行时可以调试。
缺点是:
- 代码膨胀,如果内联函数使用频率高且函数体复杂,就会造成代码膨胀;
- 如果函数库中用到了内联函数,在对函数库进行升级后,需要重新编译使用该函数库的代(因为用到内联函数的代码需要展开内联函数的函数体)。如果没有用到内联函数,那么就不需要重新编译所有使用该函数库的代码,只需要重新链接该函数库。
- 是否内联,由编译器决定。即便显示使用了inline关键字,编译器也不一定会对函数内联。
12.虚函数可以是内联函数吗?
虚函数可以是内联函数。
但只有在不表现多态特性时,才会生效。
- 譬如虚函数是通过类的具体对象来调用的:
- A a = new A();
- a.virtualFunc();
当虚函数表现多态特性了,即便定义虚函数时使用了inline关键字,内联也不会生效。因为内联是在编译器,而多态特性需要在运行期才知道调用哪个类的虚函数。
- 譬如使用指针或者引用来调用虚函数:
- A* a = new B();
- a.virtualFunc();
13.创建和删除子类对象时,是如何调用构造函数和析构函数的?
创建子类对象时,一般现在子类构造函数中显式调用父类构造函数,然后再执行子类构造函数。
删除子类对象时,一般会先调用子类析构函数,再自动调用父类析构函数。
14.父类的析构函数为什么必须是虚函数?
当父类指针指向一个子类对象时,删除该对象,如果父类的析构函数不是虚函数,就会只调用父类的析构函数,不会调用子类的析构函数,造成内存泄漏。
如果父类的析构函数是虚函数,那么当删除该对象时,就会根据对象的实际类型取调用析构函数,这样子类对象就会得到正确释放。
15.volatile关键字声明的变量有什么特点?应用场景有哪些?
该变量的值有可能会在程序执行期间改变,所以编译器不能优化该变量(比如放入CPU寄存器中),而是只能从内存中读取。
因为使用volatile关键字会影响效率,所以最好是在有需要的时候才使用,比如硬件交互时、多线程编程时。
16.常量可以用volatile关键字修饰吗?指针可以用volatile关键字修饰吗?
可以。可以。
17.assert()是什么?可以关闭吗?
是宏。表示断言。如果assert()内的判断条件值为false,那么就会结束程序。
可以在#include<cassert>前协商#define DEBUG 来关闭断言。
18.用extern修饰变量,作用是什么?
表示在当前文件中,只是声明了改变量。编译器需要去别的文件中找到该变量的定义。
19. extern "C" 的作用是什么?
因为在C++中引入了函数重载的机制,所以编译器会对函数进行名称修饰,将参数类型也加到函数名的后面。而C语言没有这个特性。
所以在C++中使用C代码时,为了正确编译C代码,可以使用extern "C"{}来包括C代码,这样编译器就知道要用C语言的规则来编译这段代码。
20.当定义了一个结构体 struct Apple{...}, 声明这个结构体的时候,可以直接使用Apple a;而不是struct Apple a;吗?
当不存在一个同名的Apple()函数时,可以直接使用Apple a;来声明这个结构体。
但当存在一个c同名的Apple()函数时,只能使用struct Apple a;来声明这个结构体。