C++基础

C语言const的作用有哪些,谈一谈你对const的理解/h3>

        1.定义const常量,具有不可变性

        2.进行类型检查

        3.保护被修饰的变量,防止意外修改

        4.节省空间,避免不必要的内存分配

        5.编译器通常不为普通的const常量分配存储空间,而是将他们作为编译期间的常量

描述char*、const char*、char* const、const char* const的区别/h3>

        1.普通的指向char类型的指针

        2.const char* 定义一个指向字符常量的指针,*p不可更改,p可更改

        3.定义一个指向字符的指针常量,*p可更改, p不可更改

static的作用是什么,什么情况下用到static/h3>

        1.修饰全局变量时,表明这个全局变量只在这个文件中可见

        2.修饰局部变量时,表明这个变量的值不会因为函数的结束而丢失,而是会保存下来

        3.修饰函数时,表明这个函数只在同一文件中调用

        4.修饰成员变量时,表示该成员变量归该类所有实例共用

        5.修饰成员函数时,这个函数只能调用本身的参数、类的静态成员变量、类的全局变量

全局变量与局部变量的区别/h3>

        1.全局变量所有文件可见,局部变量只在其作用域下可见

        2.全局变量存在时间与程序运行时间保持一致,局部变量在退出其作用域后被回收

        3.全局变量存储在全局数据区,局部变量存储在栈区

        4.局部变量可以与全局变量重名,但是局部变量会屏蔽全局变量。

宏定义的作用是什么/h3>

        1.条件编译,告诉编译器可以只编译需要的代码部分

        2.可以定义函数,一些简单的函数可以定义为宏函数,这样的定义会在编译期间,以函数定义的代码替换函数名,把代码嵌入到程序中,不会有函数调用的情况,提高效率

        3.提高程序的通用性和易读性,提高一致性,减少输入错误

内存对齐的概念什么会有内存对齐/h3>

        1.每个元素存放到存储空间时,都会认为内存是按照自己宽度的大小来存储的。因此元素放置的位置一定会在自己宽度的整数倍上开始。

        2.平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

        3.性能原因:数据结构(尤其是栈)应该尽可能地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

inline 内联函数的特点有哪些它的优缺点是什么/h3>

如何用C 实现 C++ 的面向对象特性(封装、继承、多态)/h3>

memcpy怎么实现让它效率更高/h3>

typedef和define有什么区别/h3>

        1.一个时自定义类型

        2.一个是宏定义,把定义的函数在编译阶段嵌入到代码

extern有什么作用,extern C有什么作用/h3>

        

如何避免野指针/h3>

        1.指针一定要初始化

        2.当指针不在需要时,需要进行重置

        3.指针赋值时,应该检查是否有内存分配,如果没有分配再赋值。

如何计算结构体长度/h3>

        1.相对地址必须是本身类型的整数倍

        2.整个结构体的长度,必须是类型最大成员的长度整数倍

        3.无论结构体中是否有内嵌结构体都是按所有结构体中基本类型最大的对齐

sizeof和strlen有什么区别/h3>

        1.sizeof 查看变量所占内存空间大小

        2.strlen 查看字符串长度,不包括‘ ’

知道条件变量吗条件变量为什么要和锁配合使用/h3>

        

struct和class有什么区别/h3>

        在面向c语言时,struct是个数据类型,不能有成员函数。

        在面向c++时,struct被扩展了,可以有成员函数,可以被继承,可以实现多态

        struct的默认继承访问权是public,class默认继承访问权是private。

        struct更适合看作一个数据结构,存储数据。class更适合看作一个对象,有对象的数据与属性

extern “C”的作用/h3>

        为了能够正确实现C++代码调用其他C语言代码,为了使指定代码按照c语言风格进行编译。

主要原因是C++支持函数重载,而c语言不支持函数重载。

函数重载和覆盖有什么区别/h3>

        函数重载是同一个类中可以有相同名称的函数名但传入参数类型不同,达到多态的目的。

        函数覆盖是针对于子类实例对于父类虚函数的一个重新实现,即函数名与参数列表都保持一致,但是实现不同,达到多态的目的

多态的理解,运行时多态的实现原理是什么/h3>

        简单来说就是一个接口形式,但是有不同的实现方式。

        运行时多态的实现原理是根据子类在继承基类时通过虚函数实现的动态选择调用。vptable中记录了基类的虚函数地址,在派生类中继承了基类的这张vptable,而且在派生类的构造函数中对继承来的vptable相关地址进行替换,因此在调用时通过vptable的实际地址能够知道调用哪个函数。

单继承、多继承、虚继承条件下虚函数表的结构

如果虚函数是有效的,那为什么不把所有函数设为虚函数/h3>

        1.虚函数会有虚函数表的产生,会影响软件性能

构造函数可以是虚函数吗/h3>

        不可以。

①从存储空间角度

        虚函数对应一个vtable,这大家都知道,可是这个vtable其实是存储在对象的内存空间的。问题出来了,如果构造函数是虚的,就需要通过 vtable来调用,可是对象还没有实例化,也就是内存空间还没有,无法找到vtable,所以构造函数不能是虚函数。

②从使用角度

        虚函数的作用在于通过父类的指针或者引用来调用它的时候能够变成调用子类的那个成员函数。而构造函数是在创建对象时自动调用的,不可能通过父类的指针或者引用去调用,因此也就规定构造函数不能是虚函数。

析构函数可以是虚函数吗/h3>

        可以,而且我个人感觉析构函数很适合写为虚函数。

        编译器总是根据类型来调用类成员函数。一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,C++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于派生类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。

        所以建议的方式是将析构函数声明为虚函数。一个函数一旦声明为虚函数,那么不管你是否加上virtual 修饰符,它在所有派生类中都成为虚函数。但是由于理解明确起见,建议的方式还是加上virtual 修饰符。

        C++不把虚析构函数直接作为默认值的原因是虚函数表的开销以及和C语言的类型的兼容性。有虚函数的对象总是在开始的位置包含一个隐含的虚函数表指针成员。

什么场景需要用到纯虚函数/h3>

        virtual 函数类型 函数名 (参数表列) = 0;

        在含有纯虚函数的类中,称为抽象类,抽象类不能被实例化,只能被作为基类来使用

        如果派生类中没有重新定义纯虚函数,而只是继承基类的纯虚函数,则这个派生类仍然还是一个抽象类。如果派生类中给出了基类纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以建立对象的具体的类。

        需要子类必须实现这个接口的时候。

了解RAII吗绍一下/h3>

        局部对象自动销毁的特性来控制资源的生命周期

        智能指针的实现就是一个很好的例子。

        保存在栈的变量,在超出其作用域后由系统自动回收,如果是对象的话,会调用其析构函数,如果在析构函数中释放需要管理指针对象等堆内存空间,就可以实现资源的自动回收。

类的大小怎么计算/h3>

        c++类的大小计算_rotation?? ??的博客-CSDN博客_c++ 类大小

volatile关键字的作用/h3>

如何实现一个线程池/h3>

        适用于需要频繁创建线程,但是线程任务处理时间短的。可以有效降低 创建线程的性能开销。

       管理一个任务队列,一个线程队列,然后每次取一个任务分配给一个线程去做,循环往复

        

了解各种强制类型转换的原理及使用/h3>

        基础类型的强制转换,会导致丢精度的问题,比如int类型的变量占用4个字节,而char类型的会占用1个字节,在int -> char 强制转换时会丢失3个字节,导致精度丢失

        动态类型转换:只能用在父类与子类之间(static_cast、const_cast、dynamic_cast、reinterpret_cast )

        static_cast:基本的类型转换,比如 int y;char x = static_casty;

        const_cast: 看名称可以看出是对于const类型的变量进行转换,主要用于将const类型的变量转换为非const类型变量。

        dynamic_cast:动态类型转换-主要用于使用基类的指针或引用调用派生类的操作且该操作不是虚函数(多态性继承)。该操作符用于运行时检查该转换是否类型安全,但只在多态类型时合法,即该类至少具有一个虚拟方法。dynamic_cast用于运行时检查该转换是否类型安全,只在多态类型时合法,该类中至少要有一个虚函数,dynamic_cast主要用于类之间的上行转换和下行转换,也可用于类之间的交叉转换,上行转换时与static_cast效果一致,下行转换时具有检查功能,更安全一些。

        reinterpret_cast:interpret是解释的意思,reinterpret即为重新解释,此标识符的意思即为数据的二进制形式重新解释,但是不改变其值。如:int i; char *ptr=”hello freind!”; i=reinterpret_cast(ptr);这个转换方式很少使用。

       (贴自网上各种解释)

指针和引用有什么区别/h3>

        引用在实现上相当于是一个指针常量 。

        1.定义与性质不同:指针是一个变量,是可以修改里面存储的内容的,存储着一个地址,指向内存。引用是原引用的一个别名,在初始化赋值后不能再指向其他地址

        2.指针可以有多级指针,引用不可以

  

        3.初始化:指针可以只定义不初始化。引用必须在定义时进行初始化。

        4.指针可以指向NULL,引用不可以,必须与合法的存储单元关联

        5.sizeof运算符得出结果不同,指针是获取到指针的大小,引用是获取到指向内容的大小

        6.自增运算符意义不同,指针++ 是指向后面的内存,引用++是指向的值++

        7.指针和引用作为函数参数时,指针需要检查是否为空,引用不需要

什么情况下用指针,什么情况下用引用/h3>

        1.如果使用一个变量指向一个对象时,但是该变量在一定条件下可能会修改指向的内容时,应该把变量声明为指针

        2.如果变量肯定指向一个对象,例如设计中不允许变量为空,这时可以声明为引用

        3.重载操作符应该返回引用(主要是为了减少不必要的开销,引用的效率高)

new和malloc有什么区别/h3>

        1.申请内存的所在位置:new操作符从自由存储区上为对象动态分配内存空间,而malloc函数从堆上动态分配内存。自由存储区是c++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即为自由存储区。堆是对于操作系统而言的,是操作系统上的一块特殊的内存,用于程序的内存动态分配。

        2.返回类型的安全性:new操作符分配成功时,返回的是对象类型的指针,不需要类型转换。

malloc内存分配成功时返回的是void* 指针,需要强制类型转换将void*指针转换。

        3.分配失败时的返回值:new分配异常时是抛出异常bac_alloc,不会返回NULL。malloc分配内存失败时返回NULL。

        4.new操作符不需要指定内存的大小,编译器会根据类型信息自动计算。malloc需要显式的指出所需内存的大小

        5.new操作符:1-分配内存空间;2-调用构造函数来构造对象,并且传入初值;3-返回一个指向该对象的指针。

        delete操作符:1-调用对象的析构函数;2释放内存空间

特征 new/delete malloc/free
分配内存的位置 自由存储区
内存分配失败返回值 完整类型指针 void*
内存分配失败返回值 默认抛出异常 返回NULL
分配内存的大小 由编译器根据类型计算得出 必须显式指定字节数
处理数组 有处理数组的new版本new[] 需要用户计算数组的大小后进行内存分配
已分配内存的扩充 无法直观地处理 使用realloc简单完成
是否相互调用 可以,看具体的operator new/delete实现 不可调用new
分配内存时内存不足 客户能够指定处理函数或重新制定分配器 无法通过用户代码进行处理
函数重载 允许 不允许
构造函数与析构函数 调用 不调用

malloc的内存可以用delete释放吗/h3>

        不可以。

        调用delete函数时是针对对象而言的,首先会调用其指向对象的析构函数,然后再释放其内存空间。而malloc出来的内存里面并不能调用其析构函数

malloc出来20字节内存,为什么free不需要传入20呢,不会产生内存泄漏吗/h3>

        在分配空间时,应该有一部分空间是存储的当前空间的基本信息。

new[]和delete[]一定要配对使用吗nbsp;  

        当类型为int, float等内置类型时,new、delete、new[]、delete[]不需要配对使用;

        当是自定义类型时,new、delete和new[]、delete[]才需要配对使用。

        new[] 在申请空间时会在其头部存放当前对象数组的长度,delete[]会先去读取此长度,然后释放对应的空间。

        如果是内置类型,delete不需要调用对象的析构函数,所以此时可以不配对使用

        如果是自定义的类型,delete[]就需要调用很多次析构函数,必须去读到new[]头部存储的数组长度。

拷贝构造函数与赋值运算符的区别

拷贝构造函数是一个构造函数,使用传入的参数实例来新创建一个实例对象。

赋值运算符是执行某种运算将一个已存在的实例对象赋值给另一个已存在的对象。

来源:月下偷瓜

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2022年8月14日
下一篇 2022年8月14日

相关推荐