赞
踩
该文章只针对面试时面试官提问如何回答的更全更好,看此文章不能学会相应语言,也没有知识点相关代码。如果知识点本身不会,背诵此文章可能能让你找到一份工作,但不能让你持续的干下去。还是需要自身精通对应知识点。
该文章适合有C++基础的朋友阅读,主要是针对本科应届生。收录了二十道近几年非常经典的C++面试题。C语言部分参考我之前发的博文。
工作几年的朋友面试时很少会遇到这些问题,更多问的是之前工作的具体项目,以及专项知识点的深度,不会再有这些基础问题。
C和C++的主要区别在于C++是C的扩展,C++是完全兼容C语言的,且C++更加适合于面向对象的思想,而C语言更时候面向过程编程思想。
C语言想实现面向对象通常可以通过结构体和函数指针来模拟类的方法。
1. 封装:封装是将数据和操作数据的函数绑定在一起的过程,形成一个称为“对象”的整体。封装的好处是可以隐藏对象的内部实现细节,只提供有限的接口供外部访问,这样可以提高代码的安全性和可维护性。
2.继承:继承是从已有的类派生出新的类的过程,新的类会继承原有类的属性和方法。继承的好处是可以实现代码的重用,提高代码的效率。
3.多态:多态是指同一个接口可以有多种不同的实现方式。多态的好处是可以提高代码的灵活性和可扩展性。
高内聚低耦合是一种编程思想(或者说规范),它就要求开发人员在面向对象编程的过程中尽可能做到:类的内部功能关联可以很复杂,但对外接口尽量单一;且类的封装中应该尽可能减少对外部对象的依赖,一个类只实现某类或某个方法。
在C语言中,struct默认为公有权限,且没有权限修饰符,只能通过特殊技巧模拟私有成员,此外不能定义成员函数,但可以使用函数指针。
C++中,struct默认为共有权限,其它功能与class一致,可以使用权限修饰符,可以定义成员函数。
C++中,class默认为私有权限。
它们都是动态管理内存的入口,负责申请与释放堆区资源
宏函数是在预处理阶段进行的代码替换。
内联函数在编译器阶段是直接复制“镶嵌”到主函数中去的,就是将内联函数的代码直接放在内联函数的位置上,所以没有指令跳转,指令按顺序执行。内联函数运行速度比常规函数稍快,但代价是需要占用更多的内存。
内联函数是真正的函数。
引用的本质是指针常量,引用可以做到的事都可以使用指针做,但引用更安全,更简洁。而指针更灵活,更通用。在使用时有以下区别
1. 语法:
- 引用使用&符号声明,指针使用*符号声明。
- 引用在声明时必须初始化,而指针可以先声明后赋值。
2. 操作:
- 引用在声明后不能再引用其他对象,指针可以在运行时指向不同的对象。
- 引用不需要解引用操作符*,指针需要通过*来访问所指向的对象。
3. 空值:
- 引用不能指向空值,指针可以指向空值(nullptr)。
4. 传递方式:
- 通过引用传递参数时,会直接操作原始数据,而指针传递参数时需要通过指针操作符*来访问原始数据。
5. 大小:
- 引用在内存中通常被实现为指针,但引用本身不占用额外的内存空间,而指针需要额外的内存空间来存储地址。
const关键字用于声明常量,表示该变量的数值在程序执行期间不能被修改。
在C语言中的用处参考 C语言常见面试题 其中第三章、第五章、第九章
const在C++中可以用来修饰成员变量,被修饰的成员变量只能在初始化列表中进行初始化,无法被修改。
const还可以用来修饰成员函数,主要目的是防止成员函数修改成员变量的值,但可以修改静态成员变量,只能调用const修饰的成员函数或静态成员函数,const修饰成员函数本质上修饰的是this指针,所以const修饰的成员函数可以和同名非const成员函数构造函数重载。
在C语言中的用处参考 C语言常见面试题 其中第十章
在C++中static可用于修饰成员变量,被修饰的成员变量需要在类内声明,类外初始化。并包含以下特点:
static修饰成员函数有以下特点:
何时使用static关键字?(大多面试会深入问具体使用位置)
1)一切不需要实例化(创建对象)就可以确定行为方式的函数都应该设计为静态的。例如:圆类中计算面积和周长的函数;简单工厂中创建对象的函数。
2)通过其它类型对象转换成自己类型对象一般使用静态成员函数。例如:Qt中图片类型转换函数;不同类型字符串格式转换函数。
3)单例的设计。
调用时机(不考虑存在移动构造的情况):
注意:针对上述第三种情况,C++标准允许一种(编译器)实现省略创建一个只是为了初始化另一个同类型对象的临时对象。指定这个参数(-fno-elide-constructors)将关闭这种优化。(优化方式是建立一个对象引用绑定到返回的优化,可以省略两次调用拷贝构造函数)
深拷贝与浅拷贝的区别:
默认的拷贝构造函数为浅拷贝。针对指针对象,只拷贝指针存储的地址。而深拷贝会开辟新的内存,拷贝内存里的数据。
如图类B与类C继承类A,而类D多继承与类B与类C,此时会引发数据冗余与二义性的问题。
数据冗余:在创建 D 类的对象时,类A 的构造函数将会调用两次,相当于创建两个类A对象
二义性:A类中成员变量,可以通过B和C去访问,此时会存在两个同种含义的变量
注意:在虚继承的设计中,如果类B或类C在初始化列表中初始化了类A,此时类B与类C的初始化列表中针对类A的初始化会失效,会调用类A的无参构造函数,若想人为选择类A的构造函数,需要在类D的初始化列表中去选择类A的构造函数初始化
多态指同种行为,不同的对象有不同的表现。
在C++中多态分为静态多态(编译多态)与动态多态(运行多态)两种,实现静态多态可以通过函数重载或泛型编程,实现动态多态需要用到继承与虚函数。
函数重载指在同一作用域,相同的函数名,不同的参数列表构成重载函数,在调用函数时,编译器会根据传参类型选择要调用的函数。
泛型编程需要创建对应的模板,针对特殊类型也可以对模板进行显式实例化。
动态多态的实现的基础是子类重新实现父类的虚函数,调用时为父类指针指向子类对象,通过该指针调用虚函数。
根据语法规则,将父类的析构函数设置成虚函数,那么子类的析构函数会自动变为虚函数。
当父类指针指向子类对象的时候,如果析构函数不是虚函数,则不会发生动态多态(运行时多态),而导致只会调用父类的析构函数,只会释放掉对象中父类的资源,此时子类对象资源没有释放,从而导致内存泄漏。
扩展:动态多态的实现原理由虚函数表实现,一旦类中引入了虚函数,在程序编译期间会创建虚函数表,表中每一项数据都是虚函数的入口地址,为了将对象与虚函数表关联起来,编译器会在对象中会增加一个指针成员用于存储虚函数表的位置,基类的指针指向派生类对象时就是通过虚函数表的指针来找到实际应该调用的函数。
基类与派生类都维护自己的虚函数表,虚函数表位于只读数据段(.rodata),如果派生类重写基类的虚函数,则虚函数表存储的是派生类的函数的地址,没有重写的虚函数则保存的是基类的虚函数表
回答问题时建议带上虚函数表原理,可以大幅提升面试成功率
派生类重新实现基类的虚函数,要求函数名、参数、返回值都必须相同,基类中该函数必须要有virtual关键字修饰,重写函数的权限访问限定符可以不同,通过父类指针或引用指向子类对象,再去调用该函数。主要目的是用于实现动态多态。原理是使用虚函数表
同一作用域中的函数名相同,参数不同的多个函数间构成重载。主要目的是提高的易用性,减少函数名数量,同时提高程序的可读性,原理是编译器会将重载函数设置成不同的函数名,根据参数类型与个数进行匹配
扩展问题:在 C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”?
C++语言支持函数重载,C 语言不支持函数重载。函数被 C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:void foo(int x, int y);
该函数被C编译器编译后在库中的名字为 _foo,而C++编译器则会产生像_foo_int_int 之类的名字。
C++提供了 C 连接交换指定符号 extern“C”来解决名字匹配问题。
父类与子类有同名函数,调用的时候总是调用子类的函数,此时父类成员函数被隐藏。主要目的是一般子类继承过来的函数不适合子类,或者需要扩展 ,则需要重写父类的函数。两个函数的返回值和参数可以相同也可以不同。
泛型编程指编写不依赖具体数据类型的程序,目的是将程序尽可能通用,将算法从数据结构中抽象出来,成为通用算法。
C++模板是一种泛型编程的工具,允许程序员编写通用的类或函数,以便在不同数据类型下进行重复使用。模板的主要目的是实现代码重用和提高代码的灵活性。
用法:
函数模板:通过函数模板可以编写通用的函数,支持多种数据类型。
- template <typename T>
- T add(T a, T b) {
- return a + b;
- }
类模板:通过类模板可以编写通用的类,支持多种数据类型。
- template <typename T>
- class Pair {
- public:
- T first, second;
- Pair(T a, T b) : first(a), second(b) {}
- };
vector与list都属于STL中的序列式容器(顺序容器),元素以严格的线性形式组织起来,每个元素都有固定位置。
vector本质是动态数组,随机存取任何元素都能在常数事件完成,在尾端增删元素性能高。增加数据时大致按以下流程:
建立空间->填充数据->重建更大空间->复制原空间数据->删除原空间->添加新数据
list本质是双向循环链表,在任何位置增删元素都能在常数时间完成,但随机访问偏慢。
主要区别在于vector支持下标操作,而list不支持,vector多用于存储已知长度(模糊范围也可)且经常随机访问的数据,list主要用于存储长度未知且经常增删数据而少量随机访问。
迭代器是一种检查容器内元素并遍历元素的数据类型,迭代器的核心作用为使算法独立于容器类型。标准库为每一种标准容器定义了一种迭代器类型,而极少数容器支持下标操作访问容器元素。
由于一些对容器的操作如删除元素或移动元素会修改容器的内在状态,会使原本指向被移动元素的迭代器失效,也可能使其他迭代器失效。使用无效的迭代器是没有意义的,可能会导致和使用空指针相同的问题,所以使用迭代器时,需要特别留意哪些操作会使迭代器失效,使用无效迭代器会导致严重的运行错误。
所谓智能指针就是智能/自动化的管理指针所指向的动态资源的释放。它是一个类模板,有类似指针的功能,对*和->运算符进行了重载。
实现原理:
智能指针用一个类描述,这个类中有一个指针成员(一个引用计数成员),构造函数中初始化指针成员指向对象(初始化引用计数成员的值为1),析构函数中删除指针成员指向的对象(将引用计数的值自减,如果减到0的时候,删除指针成员指向的对象)
下列代码为shared_ptr的基础功能实现,只实现了最基本的功能。
- #ifndef MYSHAREDPTR_H
- #define MYSHAREDPTR_H
-
- #include <iostream>
- #include <string>
-
- using namespace std;
-
- template <typename T>
-
- class MySharedPtr
- {
- private:
- T* ptr;
- long *countRef;
-
- protected:
- void release(void)
- {
- (*countRef) --;
- if(*countRef == 0) {
- if(ptr != NULL) {
- delete ptr;
- }
-
- delete countRef;
- }
- }
-
- public:
- explicit MySharedPtr(T* _ptr = NULL) : ptr(_ptr), countRef(new long(1))
- {
- ;
- }
-
- MySharedPtr(const MySharedPtr<T> &other) : ptr(other.ptr), countRef(other.countRef)
- {
- (*countRef) ++;
- }
-
- ~MySharedPtr()
- {
- release();
- }
-
- MySharedPtr<T> operator =(const MySharedPtr<T> &other)
- {
- if(this != &other) {
- release();
- ptr = other.ptr;
- countRef = other.countRef;
- (*countRef) ++;
- }
- }
-
- T& operator *(void)
- {
- return *ptr;
- }
-
- T* operator ->(void)
- {
- return ptr;
- }
-
- T* getPtr(void)
- {
- return ptr;
- }
-
- int getCounRef(void)
- {
- return *countRef;
- }
- };
- #endif // MYSHAREDPTR_H
扩展:智能指针并非线程安全,如果想让他线程安全可以选择继承该类,重写对应功能,增加互斥锁使之线程安全。另外shared_ptr在相互引用的情况下会出现故障,从而需要引入weak_ptr协助shared_ptr工作
该类型只有一个对象,不能再额外创建一个新的对象
实现步骤:
饿汉式与懒汉式:饿汉式单例在第2步时直接new出该对象,懒汉式单例在第2步时令指针等于NULL,只有在第一次用到类实例的时候才会去实例化
懒汉式线程不安全问题:两个线程同时调用获取单例的静态函数时可能造成内存泄漏,故需要在该函数中增加互斥锁,但是在已创建此对象时再加锁会影响程序性能,所以在加锁前需要再次判断是否已经创建出对象。
如何自动释放单例:可以使用智能指针管理对象的内存,将智能指针定义在静态区即可;也可以在单例中创建一个内部类,内部类的析构函数中释放单例,内部类对象创建在静态区,那么在程序结束时则自动释放掉单例。
注意:其它常考的设计模式还有工厂、策略模式、代理模式、适配器模式、观察者模式、组合模式,建议学习其原理,并能绘制UML类图
注:特性不止这些,只列举了相对常见部分,加粗的部分尤为重要
编写该文章目的主要为想从事相关工作的同学找到一份好的工作,以上题目在面试中经常出现,如果有在外面试的朋友发现有更常见更经典的题目也可以私信告知,后续也会更新到博客当中。
如果有朋友想系统的学习嵌入式相关知识,从事相关的行业,可以私信我,有一些经典的电子档书籍资料和开源网课学习链接。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。