当前位置: 首页>后端>正文

c++面试题

0、c++11新特性

auto关键字:编译器可以根据变量的初始值自动推导出其型,这在编写模板代码或涉及复杂类型时特别有用。然而,需要注意的是,auto不能用于函数传参以及数组类型的推导。

nullptr:这是一个新的空指针常量,用于替代C++98中的NULL。与NULL相比,nullptr具有更强的类型安全性,可以避免一些潜在的错误。

lambda表达式:lambda表达式允许定义匿名函数对象,可以捕获其所在作用域的变量,并用于各种需要函数对象的场合,如算法、事件处理等。

右值引用和移动语义:C++11引入了右值引用的概念,并基于它实现了移动语义。这可以消除不必要的对象拷贝,提高性能,特别是在处理临时对象或大型对象时。

智能指针:C++11新增了std::shared_ptr、std::unique_ptr和std::weak_ptr等类型的智能指针,用于自动管理动态分配的内存,减少内存泄漏的风险。

范围for循环:新的for循环语法简化了对容器或数组的遍历,使得代码更加简洁易读。

初始化列表:可以使用初始化列表来对类进行初始化,提高了初始化的效率和安全性。

override和final关键字:override用于明确表示某个成员函数是重写基类的虚函数,而final用于禁止某个类被继承或某个虚函数被重写。

1、new 和 malloc 的区别

- new 是操作符,而 malloc 是函数;

- 使用 new 操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而 malloc 则需要显式地指出所需内存的尺寸;

- new 分配失败的时候会直接抛出异常,malloc 分配失败会返回 NULL;

- 对于非简单类型,new 在分配内存后,会调用构造函数,而 malloc 不会;

- new 分配成功后会返回对应类型的指针,而 malloc 分配成功后会返回 void? * 类型;

- malloc 可以分配任意字节,new 只能分配实例所占内存的整数倍数大小;

- new 可以被重载,而 malloc 不能被重载;

- new 操作符从自由存储区上分配内存空间,而 malloc 从堆上动态分配内存;

- 使用 malloc 分配的内存后,如果在使用过程中发现内存不足,可以使用 realloc 函数进行内存重新分配实现内存的扩充,new 没有这样直观的配套设施来扩充内存。

2、面向对象的三大特征

面向对象的三大特征是:封装、继承、多态。

1. 封装 将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。 C++通过 private、protected、public 三个关键字来控制成员变量和成员函数的访问权限。封装的好处:隐藏实现细节,提供公共的访问方式;提高了代码的复用性;提高了安全性。

2. 继承 C++最重要的特征是代码重用,通过继承机制可以利用已有的数据类型来定义新的数据类型,新的类不仅拥有旧类的成员,还拥有新定义的成员。继承的好处:提高代码的复用性;提高代码的拓展性;是多态的前提。

3. 多态 在面向对象中,多态是指通过基类的指针或者引用,在运行时动态调用实际绑定对象函数的行为。多态是在程序运行时根据基类的引用(指针)指向的对象来确定自己具体该调用哪一个类的虚函数。当父类指针(引用)指向 父类对象时,就调用父类中定义的虚函数;即当父类指针(引用)指向 子类对象时,就调用子类中定义的虚函数。多态性改善了代码的可读性和组织性,同时也使创建的程序具有可扩展性。

3、浅拷贝和深拷贝

1. 浅拷贝 浅拷贝又称为值拷贝,将源对象的值拷贝到目标对象中,如果对象中有某个成员是指针类型数据,并且是在堆区创建,则使用浅拷贝仅仅拷贝的是这个指针变量的值,也就是在目标对象中该指针类型数据和源对象中的该成员指向的是同一块堆空间。这样会带来一个问题,就是在析构函数中释放该堆区数据,会被释放多次。默认的拷贝构造函数和默认的赋值运算符重载函数都是浅拷贝。

?2. 深拷贝 深拷贝在拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中去,这样指针成员就指向了不同的内存位置。并且里面的内容是一样的,这样不但达到了拷贝的目的,还不会出现问题,两个对象先后去调用析构函数,分别释放自己指针成员所指向的内存。即为每次增加一个指针,便申请一块新的内存,并让这个指针指向新的内存,深拷贝情况下,不会出现重复释放同一块内存的错误。

4、 C++ 中的多态

多态分为静态多态和动态多态。

1. 静态多态 是编译器在编译期间完成的,编译器会根据实参类型来选择调用合适的函数,如果有合适的函数就调用,没有的话就会发出警告或者报错。静态多态有函数重载、运算符重载、泛型编程等。

2. 动态多态 是在程序运行时根据基类的引用(指针)指向的对象来确定自己具体该调用哪一个类的虚函数。当父类指针(引用)指向 父类对象时,就调用父类中定义的虚函数;即当父类指针(引用)指向 子类对象时,就调用子类中定义的虚函数。动态多态的实现原理 当类中声明虚函数时,编译器会在类中生成一个虚函数表,虚函数表是一个存储类虚函数指针的数据结构, 虚函数表是由编译器自动生成与维护的。virtual 成员函数会被编译器放入虚函数表中,存在虚函数时,每个对象中都有一个指向虚函数表的指针(vptr 指针)。在多态调用时, vptr 指针就会根据这个对象在对应类的虚函数表中查找被调用的函数,从而找到函数的入口地址。

5、虚函数的实现原理

1. 虚函数的作用主要是实现了动态多态的机制。动态多态,简单的说就是用父类型的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。

2. 虚函数实现原理 编译器处理虚函数时,给每个对象添加一个隐藏的成员。隐藏的成员是一个指针类型的数据,指向的是函数地址数组,这个数组被称为虚函数表。虚函数表中存储的是类中的虚函数的地址。如果派生类重写了基类中的虚函数,则派生类对象的虚函数表中保存的是派生类的虚函数地址,如果派生类没有重写基类中的虚函数,则派生类对象的虚函数表中保存的是父类的虚函数地址。

6、什么是纯虚函数,有什么作用

1. 纯虚函数是一种特殊的虚函数,它的格式是:虚函数不给出具体的实现,也就是后面没有大括号实现体,而在后面加上 "=0"

2.? 很多情况下,在基类中不能对虚函数给出具体的有意义的实现,就可以把它声明为纯虚函数,它的实现留给该基类的派生类去做。

3.? 如果一个类中有纯虚函数,那么这个类也被称为抽象类。这种类不能实例化对象,也就是不能创建该类的对象。除非在派生类中完全实现基类中所有的纯虚函数,否则派生类也是抽象类,不能实例化对象。

7、虚函数和纯虚函数的区别

1.虚函数可以有具体的实现,纯虚函数没有具体的实现。 对于虚函数来说,父类和子类都有各自的版本,由多态方式调用的时候动态绑定。 有纯虚函数的类称为抽象类,有纯虚函数的类不能实例化,派生类必须实现纯虚函数才可以实例化,否则也是抽象类。

2.虚函数是 C++ 中用于实现动态多态的机制。 很多情况下,在基类中不能对虚函数给出具体的有意义的实现,就可以把它声明为纯虚函数,它的实现留给该基类的派生类去做。

8、虚析构函数有什么作用

虚析构函数的主要作用是为了防止遗漏资源的释放,防止内存泄露。如果基类中的析构函数没有声明为虚函数,基类指针指向派生类对象时,则当基类指针释放时不会调用派生类对象的析构函数,而是调用基类的析构函数,如果派生类析构函数中做了某些释放资源的操作,则这时就会造成内存泄露。

9、重载,复写,隐藏的区别

1. 重载:在同一作用域中,同名函数的形式参数(参数个数、类型或者顺序)不同时,构成函数重载,与返回值类型无关。

2. 重写:指不同作用域中定义的同名函数构成隐藏(不要求函数返回值和函数参数类型相同)。比如派生类成员函数隐藏与其同名的基类成员函数、类成员函数隐藏全局外部函数。

3. 派生类中与基类同返回值类型、同名和同参数的虚函数重定义,构成虚函数覆盖,也叫虚函数重写。

10、什么情况会调用拷贝构造,什么时候会调用赋值操作

1. 拷贝构造函数的调用时机 - 用一个对象初始化另外一个对象 - 对象以值传递的方式传递给函数参数 - 函数局部对象以值传递的方式从函数返回

2. 赋值操作的调用时机 - 将一个对象赋值给另外一个对象

11、虚函数可以是内联函数吗

1. 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。

2. 内联是在编译期建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时不可以内联。

3. inline virtual 唯一可以内联的时候是:编译器知道所调用的对象是哪个类,这只有在编译器具有实际对象而不是对象的指针或引用时才会发生。

12、 C++ 中的四种类型转换

上行转换(把派生类的指针或引用转换成基类表示)? 下行转换(把基类指针或引用转换成派生类表示)

1. static_cast 静态转换 用于类层次结构中基类(父类)和派生类(子类)之间指针或引用的转换, 进行上行转换是安全的 - 进行下行转换时,由于没有动态类型检查,所以是不安全的。

2. dynamic_cast 动态转换 用于类层次间的上行转换和下行转换 ,进行上行转换时,dynamic_cast 和 static_cast 的效果是一样的 在进行下行转换时,dynamic_cast 具有类型检查的功能,比 static_cast 更安全。

3. const_cast 常量转换 用来修改类型的const属性 常量指针被转化成非常量指针,并且仍然指向原来的对象 常量引用被转换成非常量引用,并且仍然指向原来的对象

4. reinterpret_cast 重新解释转换 这是最不安全的一种转换机制,最有可能出问题 主要用于将一种数据类型从一种类型转换为另一种类型,它可以将一个指针转换成一个整数,也可以将一个整数转换成一个指针

13、C 语言和 C++ 语言的区别

1. C 语言是面向过程的语言,而 C++ 支持面向对象,所以 C 语言自然没有面向对象的封装、继承、多态等特性,也不支持面向对象的一些语法;

2. C++ 支持函数重载,C 语言不支持;

3. C 程序中如果函数没有任何参数需要将参数定义为 void 以此来限定函数不可传递任何参数,如果不进行限定让参数表默认为空其意义是可以传递任何参数,在 C++ 中,不带参数的函数表示函数不能传递任何参数;

4. C 语言 struct 中不能有函数,而 C++ 语言 struct 中可以有函数;

5. C 语言函数参数不支持默认值,而 C++ 语言支持参数默认值;

6. C++ 语言支持内联函数,而 C 语言不支持;

7. C++ 语言支持引用,而 C 语言不支持;

8. C 语言采用 malloc 和 free 函数动态申请和释放内存,而 C++ 使用 new 和 delete 运算符;

9. C 语言中只有局部和全局两个作用域,而 C++ 中有局部、全局、类、名称空间作用域。

14、C++ 和 C 中 struct 的区别以及和 class 的区别

C++ 和 C 中 struct 的区别: 1. C 的结构体不允许有函数存在,C++ 的结构体允许有内部成员函数,并且允许该函数是虚函数 2. C 的结构体内部成员不能加权限,默认是 public,而 C++ 的结构体内部成员权限可以是 public、protected、private,默认 public 3. C 的结构体是不可以继承,C++ 的结构体可以从其它的结构体或者类继承 4. C 中的结构体不能直接初始化数据成员,C++ 中可以 5. C 中使用结构体需要加上 struct 关键字,或者对结构体使用 typedef 取别名后直接使用,而 C++ 中使用结构体可以省略

struct 和 class 的区别: 1. struct 一般用于描述一个数据结构集合,而 class 是对一个对象数据的封装 2. struct 中默认访问控制权限是 public,而 class 中默认的访问控制权限是 private。 3. 在继承关系中,struct 默认是公有继承,而 class 是私有继承 4. class 关键字可以用于定义模板参数,而 struct 不能

15、static 关键字的作用

可以用来修饰局部变量、全局变量、成员变量、函数和成员方法。主要作用有:限制数据的作用域、延长数据的生命周期、修饰成员可以被该类所有对象共享。

1. 限制数据的作用域(隐藏) 所有没有加 static 的全局变量和函数都具有全局可见性,其它源文件中也可以访问。被 static 修饰的全局变量和函数只能在当前源文件中访问,其它源文件访问不了,

2. 延长数据的生命周期 普通的局部变量出了作用域就会释放,而静态变量存储在静态区,知道程序运行结束才会释放。?

3. 静态成员被该类所有对象共享, static 关键字可以修饰类中的成员变量和成员方法,被称为静态成员变量和静态成员方法,静态成员拥有一块单独的存储区,不管创建多少个该类的对象,所有对象都共享这一块内存。静态成员本质上属于类,可以通过类名直接访问。

4.静态成员函数中不能访问普通的成员变量,只能访问静态成员变量,并且在静态成员函数中没有 this 指针。

16、const 的用法

1. 用在变量身上,表示该变量只读,不能对它的值进行修改

2. 结合指针一起使用 const int * p 是常量指针,表示指针变量 p 所指向的内容不能修改,指针变量 p 的内容可以修改; int * const p 是指针常量,表示指针变量 p 的内容不能修改,指针变量 p 所指向的内容可以修改; const int * const p 表示指针变量 p 的内容和所指向的内容都不可以修改。

3. const 用于函数参数 const 用于形参时说明形参在函数内部不能被改变

4. 在类中修饰成员方法,防止在方法中修改非 static 成员

5. const 修饰类的成员变量? 如果 const 修饰的是非静态的成员变量,可以在构造函数中对该变量进行初始化;如果 const 修饰的是静态的成员变量,则需要在类外对该变量进行初始化。

17、const 和 define 的区别

1. const 生效于编译阶段,而 define 生效于预处理阶段;

2. define只是简单的字符串替换,没有类型检查,而 const 有对应的数据类型,编译器要进行判断的,可以避免一些低级的错误;

3. 用 define 定义的常量是不可以用指针变量去指向的,用 const 定义的常量是可以用指针去指向该常量的地址的;

4. define 不分配内存,给出的是立即数,有多少次使用就进行多少次替换,在内存中会有多个拷贝,消耗内存大,const? 在静态存储区中分配空间,在程序运行过程中内存中只有一个拷贝;

5. 可以对 const 常量进行调试,但是不能对宏常量进行调试。

18、指针和引用的区别

1.指针是一种数据类型,用于保存地址类型的数据,而引用可以看成是变量的别名。

2. 引用不可以为空,当被创建的时候必须初始化,而指针变量可以是空值,在任何时候初始化;

3. 指针可以有多级,但引用只能是一级;

4. 引用使用时无需解引用(*),指针需要解引用;?

6. 指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了;

7. sizeof 引用得到的是所指向的变量(对象)的大小,而 sizeof 指针得到的是指针变量本身的大小;

8. 指针作为函数参数传递时传递的是指针变量的值,而引用作为函数参数传递时传递的是实参本身,而不是拷贝副本;

9. 指针和引用进行++运算意义不一样。

19、C++ 的内存管理

1. 代码区 加载的是可执行文件代码段,所有的可执行代码都加载到代码区,这块内存是不可以在运行期间修改的。

2. 未初始化数据区 加载的是可执行文件 BSS 段,位置可以分开也可以紧靠数据段,存储于数据段的数据(全局未初始化,静态未初始化数据)的生存周期为整个程序运行过程。

3. 已初始化数据区(全局初始化数据区/静态数据区) 加载的是可执行文件数据段,存储于数据段(全局初始化,静态初始化数据,文字常量(只读))的数据的生存周期为整个程序运行过程。

4. 栈区 栈是一种先进后出的内存结构,由编译器自动分配释放,存放函数的参数值、返回值、局部变量等。在程序运行过程中实时加载和释放,因此,局部变量的生存周期为申请到释放该段栈空间。

5. 堆区 堆是一个大容器,它的容量要远远大于栈,但没有栈那样先进后出的顺序,用于动态内存分配。堆在内存中位于BSS区和栈区之间。一般由程序员分配和释放,若程序员不释放,程序结束时由操作系统回收。


https://www.xamrdz.com/backend/32n1924339.html

相关文章: