C/C++八股文C/C++篇(1)

本文详细探讨了C++中的智能指针(如shared_ptr,unique_ptr和weak_ptr)的工作原理,引用计数机制,以及它们在内存管理和多态性方面的应用。同时涵盖了面向对象特性如继承、多态、封装和抽象,以及C++内存分布和程序编译流程。
摘要由CSDN通过智能技术生成

最近在准备面试,然后就需要练习八股文。所以最近在整理八股文相关内容,C++八股文系列分为几个篇章:C/C++偏,操作系统篇,计算机网络篇,数据结构篇,编译原理篇。

1.智能指针实现原理?

智能指针的实现是利用了RAII的技术对普通指针进行封装,使得智能指针本质上是一个对象,却表现出一个指针的行为。智能指针的作用是防止忘记调用delete释放内存和程序异常的进入catch块忘记释放内存,解决多次释放同一指针造成崩溃的问题。智能指针还可以把值语义转换成引用语义。

  • shared_ptr多个指针指向相同的对象。shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。每使用他一次,内部的引用计数加1,每析构一次,内部的引用计数减1,减为0时,自动删除所指向的堆内存。shared_ptr内部的引用计数是线程安全的,但是对象的读取需要加锁,因为shared_ptr的指向对象部分并不是线程安全的。

  • unique_ptr独占其所指对象,同一时刻只能有一个unique_ptr指向给定对象(通过禁止拷贝语义、只有移动语义来实现)。与原始指针相比,unique_ptr由于其RAII的特性,使得在出现异常的情况下,动态资源仍然能得到释放。

  • weak_ptr是为了配合shared_ptr而引入的一种智能指针,因为它不具有普通指针的行为。weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。

2.智能指针,里面的计数器何时会改变?

每使用一次shared_ptr他的引用计数就会加1,执行一次析构函数引用计数就会减1,如果引用计数为0则删除其所指向的堆内存。weak_ptr的构造不会引起计数器的变化。

3.智能指针和管理的对象分别在哪个区?

智能指针位于栈区域,它管理的对象是位于堆区域。堆区域是程序运行时内存动态分配的地方,智能指针指向堆上的对象,当不再需要这个对象时会自动释放内存。

4.面向对象的特性:多态原理?

C++的多态分为静态多态和动态多态。

  • 静态多态的实现原理是函数重载模板函数函数重载指的是在同一个作用域内,可以定义多个函数,它们具有相同的名称但参数列表不同的情况。在调用这些函数时,编译器会根据函数调用时提供的参数类型和数量来确定调用哪个函数。模板函数是指在定义函数时使用了类型参数,让函数适用于多种不同的类型。编译器在编译时根据参数类型生成具体的函数实现。模板函数的实现是通过编译时的模板实例化来实现的。

  • 动态多态指的是在运行时根据对象的实际类型确定要调用的函数。动态多态实现的原理是虚函数抽象类。虚函数是指在基类中定义的函数可以被派生类重写的函数。通过将函数声明为虚函数,我们可以在运行时根据对象的实际类型来确定要调用的函数实现。在C++中,只要将函数声明为虚函数即可实现动态多态。抽象类是指包含至少一个纯虚函数的类,这个类不能被实例化,只能用作基类来派生出其他类。在C++中,可以通过将函数声明为纯虚函数来实现抽象类。

5.介绍一下虚函数,虚函数怎么实现的 ?

虚函数通过在基类中声明并使用virtual关键字定义。每个对象中有一个指向虚函数表的指针(vptr),虚函数表是在编译时生成的,存储着类的虚函数地址。这个表中的指针按顺序存储,每个对象的 vptr 指向该类的虚函数表,从而实现动态绑定。虚函数指针的赋值在构造函数之后。因此构造函数不能使用虚函数,可以使用正常的非静态函数(如果有依赖于构造函数的也不能用因为这时候虚函数表未构建完成)。

6.多态和继承在什么情况下使用 ?

继承允许创建一个类,该类可以继承另一个类的属性和行为,从而使代码更易于重用。派生类可以重用基类的成员函数和数据成员,而无需重新编写它们。

多态允许使用基类的指针或引用来引用派生类的对象,从而可以在运行时选择调用哪个函数。这意味着可以使用统一的接口处理不同的对象。

7.除了多态和继承还有什么面向对象方法?

C++的面向对象特性一共是有4种:封装、多态、抽象、继承。

C++除了多态和继承还有2种面向对象方法,分别是封装和抽象。

  • 封装是面向对象编程的核心概念之一,它指的是将数据和操作数据的函数捆绑在一起,形成一个独立的单元。通过封装,可以隐藏对象的内部实现细节,使得对象的使用者只能通过公共接口来访问对象,从而提高了代码的可维护性和安全性。

  • 抽象是将具体的事物或概念抽象成通用的模型或接口的过程。在 C++ 中,抽象通常通过抽象类(含有纯虚函数的类)或接口(只含有纯虚函数的类)来实现。抽象类和接口定义了对象的通用行为,而具体的实现由派生类来完成。

8.C++内存分布?

C++占用的程序内存分为栈区、堆区、静态区、常量区和代码区。

  • 栈区是由编译器自动分配和释放的,存放的是运行时函数分配的局部变量,函数参数,返回数据,返回地址等。

  • 堆区一般由程序员手动分配,分配速度较慢,如果程序员没有释放,程序结束时由操作系统进行回收。

  • 静态区是存放全局变量,静态数据,常量的。程序启动时进行静态区的内存分配,程序结束后由系统释放,全局区分为已初始化全局区(data)和未初始化全局区(bss)。

  • 常量区是存放常量字符串的,程序结束后会由系统进行释放,程序运行时不可修改。

  • 代码区是用于存放函数体(类成员函数和全局区)的二进制代码的,代码区的数据是只读、共享的。代码区放的是具体的执行指令,CPU会逐条执行代码区的指令。

C++有3种内存分配的方式:(前2种是静态分配,第3种是动态分配)

  1. 从静态存储区分配内存:在编译时确定内存大小和位置的分配方式。这种方式下,内存的分配发生在程序运行之前,分配的内存在程序的整个运行期间都会存在。

  2. 在栈上创建内存空间:在执行函数时, 函数内局部变量的存储单元可以在栈上创建,函数执行结束的时候,这些内存单元会自动被释放,栈内存分配运算内置于处理器的指令集,效率高,但是分配的内存容量有限。当函数被调用时,为其局部变量分配内存,当函数返回时,该内存自动被释放。这种分配方式的好处是速度快,但分配的内存大小在编译时就要确定,且生命周期受限于函数调用。

  3. 在堆上分配内存(动态内存分配):程序员在程序运行时自己手动申请任意大小的内存空间,自己进行内存空间的管理,如使用malloc或者是智能智能堆内存进行动态分配。

9.C++从源程序到可执行程序的过程?

过程:编写源代码→预处理→编译→链接→生成可执行文件

程序员使用IDE编写C++代码后,先进行预处理,生成经过预处理的源代码文件,编译器将源代码翻译为目标代码,生成目标文件,然后进行链接,生成可执行文件所需的完整机器代码,最后将机器代码和系统相关的运行时库进行组合,生成最终的exe可执行文件。

10.一个对象=另一个对象会发生什么?

当一个对象被赋值给另一个对象时,涉及到赋值构造函数的调用。C++ 中,如果没有为类定义赋值构造函数,编译器会生成默认的赋值构造函数,它会逐个成员变量地进行复制。需要注意的是这个生成的默认复制构造函数可能是有问题的。编译器生成的默认构造函数可能会执行错误操作,在某些情况下,编译器不能为某些类合成默认构造函数。

如果你自己定义了赋值构造函数,那么在对象赋值时会调用你定义的赋值构造函数。赋值构造函数通常用于在对象已经存在的情况下,将一个对象的值复制给另一个对象。

除此之外还有复制构造函数和赋值运算符重载函数:

  • 复制构造函数是一种特殊的构造函数,它接受同一类型的对象作为参数,用于创建一个新对象,其内容与参数对象相同。使用场景:在需要创建一个对象,其内容与已有对象相同的情况下,通常会调用复制构造函数。

  • 赋值运算符重载函数是类中的成员函数,用于将一个已存在的对象的值赋给另一个已存在的对象。赋值运算符重载函数通常以引用形式返回对象的引用,并接受同一类型的对象作为参数。使用场景:当需要将一个已存在对象的值赋给另一个已存在对象时,通常会使用赋值运算符重载函数。

  • 20
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值