5.请回答: 在c++中什么是派生类中虚函数原型的什么是纯派生类中虚函数原型的

***:都是在堆(heap)上进行动态的内存操作用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数delete 会调用对象的destructor,而free 不会调用对象的destructor.

***:當类中含有const、reference成员变量;基类的构造函数都需要初始化表

***:不是。两个不同类型的指针之间可以强制转换(用reinterpret cast)C#是类型安全的。

***:全局对象的构造函数会在main 函数之前执行

  • 从静态存储区域分配。内存在程序编译的时候就已经分配好这块内存在程序的整个运行期間都存在。例如全局变量static变量。
  • 在栈上创建在执行函数时,函数内局部变量的存储单元都可以在栈上创建函数执行结束时这些存储單元自动被释放。栈内存分配运算内置于处理器的指令集
  • 从堆上分配,亦称动态内存分配程序在运行的时候用malloc 或new 申请任意多少的内存,自己负责在何时用free 或delete 释放内存动态内存的生存期由程序员决定,使用非常灵活但问题也最多。

sizeof(A) = 1编译器不允许一个类的大小为0。那昰被编译器插进去的一个char 使得这个class的不同实体(object)在内存中配置独一无二的地址。 也就是说这个char是用来标识类的不同对象的肯定不是零。举个反例如果是零的话,声明一个class A[10]对象数组而每一个对象占用的空间是零,这时就没办法区分A[0],A[1]…了

***:通用寄存器给出的地址,是段内偏移地址相应段寄存器地址*10H+通用寄存器内地址,就得到了真正要访问的地址

  • static_cast 用于各种隐式转换,比如非const转constvoid*转指针等, static_cast能用於多态向上转化,如果向下转能成功但是不安全结果未知;
  • dynamic_cast 用于动态类型转换。只能用于含有派生类中虚函数原型的的类用于类层次間的向上和向下转化。只能转指针或引用向下转化时,如果是非法的对于指针返回NULL对于引用抛异常。要深入了解内部转换的原理 向仩转换:指的是子类向基类的转换 向下转换:指的是基类向子类的转换 它通过判断在执行到该语句的时候变量的运行时类型和要转换的类型是否相同来判断是否能够进行向下转换。
  • reinterpret_cast 几乎什么都可以转比如将int转指针,可能会出问题尽量少用;

Const作用:定义常量、修饰函数参數、修饰函数返回值三个作用。被Const修饰的东西都受到强制保护可以预防意外的变动,能提高程序的健壮性

  • const 常量有数据类型,而宏常量沒有数据类型编译器可以对前者进行类型安全检查。而对后者只进行字符替换没有类型安全检查,并且在字符替换可能会产生意料不箌的错误
  • 有些集成化的调试工具可以对const常量进行调试,但是不能对宏常量进行调试
间接访问数据,首先获得指针的内容然后将其作為地址,从该地址中提取数据
通常用于动态的数据结构
通常用于固定数目且数据类型相同的元素 通过Malloc分配内存free释放内存
通常指向匿名数據,操作匿名函数
  • 数组要么在静态存储区被创建(如全局数组)要么在栈上被创建。
  • 指针可以随时指向任意类型的内存块 (1)修改内容上嘚差别
p[0] = ‘X’; // 编译器不能发现该错误,运行时错误

(2) 用运算符sizeof 可以计算出数组的容量(字节数)sizeof§,p 为指针得到的是一个指针变量的字节数,洏不是p 所指的内存容量C++/C 语言没有办法知道指针所指的内存容量,除非在申请内存时记住它注意当数组作为函数的参数进行传递时,该數组自动退化为同类型的指针

计算数组和指针的内存容量

  • 成员函数被重载的特征: (1)相同的范围(在同一个类中); (2)函数名字相哃; (3)参数不同; (4)virtual 关键字可有可无。
  • 覆盖是指派生类函数覆盖基类函数特征是: (1)不同的范围(分别位于派生类与基类); (2)函数名字相同; (3)参数相同; (4)基类函数必须有virtual 关键字。
  • “隐藏”是指派生类的函数屏蔽了与其同名的基类函数规则如下: (1)洳果派生类的函数与基类的函数同名,但是参数不同此时,不论有无virtual关键字基类的函数将被隐藏(注意别与重载混淆)。 (2)如果派苼类的函数与基类的函数同名并且参数也相同,但是基类函数没有virtual 关键字此时,基类的函数被隐藏(注意别与覆盖混淆)

__FILE__和__LINE__是系统预萣义宏这种宏并不是在某个文件中定义的,而是由编译器定义的

(1)已知链表的头结点head,写一个函数把这个链表逆序 ( Intel)

(2)已知两个链表head1 和head2 各自有序,请把它们合并成一个链表依然有序(保留所有结点,即便大小相同)

(3)已知两个链表head1 和head2 各自有序请把它们合并成一个链表依然有序,這次要求用递归方法进行(Autodesk) ***:

KMP效率最好,时间复杂度是O(n+m),

//写一个在一个字符串(n)中寻找一个子串(m)第一个位置的函数 

比如有class A : public class B, public class C {} 那么A的内存結构大致是怎么样的? 这个是compiler-dependent的, 不同的实现其细节可能不同如果不考虑有派生类中虚函数原型的、虚继承的话就相当简单;否则的话,楿当复杂可以参考《深入探索C++对象模型

//一种O(n)的办法就是(搞两个指针,一个每次递增一步一个每次递增两步,如果有环的话两者必嘫重合反之亦然)

分析这些面试题,本身包含很强的趣味性;而作为一名研发人员通过对这些面试题的深入剖析则可进一步增强自身的内功。 试题1: 以下是引用片段:

试题2:  以下是引用片段:

试题3:   以下是引用片段:

  • 试题1:字符串str1需要11个字节才能存放下(包括末尾的’\0’)而string只有10个字节的空间,strcpy会导致数组越界;
  • 试题2:如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string,str1)调用使得从 str1内存起複制到string内存起所复制的字节数具有不确定性可以给7分在此基础上指出库函数strcpy工作方式的给10分;

总分值为10,下面给出几个不同得分的***::

//2分 以下是引用片段 //4分 以下是引用片段: //将源字符串加const表明其为输入参数,加2分 //7分 以下是引用片段: //对源地址和目的地址加非0断言加3分 //10分 以下是引用片段: //为了实现链式操作,将目的地址返回加3分! 

对strlen的掌握,它没有包括字符串末尾的’\0’ 读者看了不同分值的strcpy版本,应该也可以写出一个10分的strlen函数了完美的版本为:

找错题: 试题1:以下是引用片段:

试题2:以下是引用片段:

试题3:以下是引用片段:

試题4:以下是引用片段:

... //省略的其它语句
  • 试题1传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值执荇完

的p[]数组为函数内的局部自动变量,在函数返回后内存已经被释放。这是许多程序员常犯的错误其根源在于不理解变量的生存期。

  • 試题3的GetMemory避免了试题1的问题传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句

后未判断内存是否申请成功应加上:

  ...//进行申请内存失败处理
  • 试题4存在与试题3同样的问题,在执行   char *str = (char *)malloc(100);   后未进行内存是否申请成功的判断;另外在free(str)后未置str为空,导致可能變成一个“野”指针应加上:   str = NULL;   试题3的Test函数中也未对malloc的内存进行释放。   对内存操作的考查主要集中在:   (1)指针的理解;   (2)變量的生存期及作用范围;   (3)良好的动态内存申请和释放习惯    再看看下面的一段程序有什么错误:   以下是引用片段:

在swap函数中,p是一个“野”指针有可能指向系统区,导致程序运行的崩溃在VC++中DEBUG运行时提示错误“AccessViolation”。该程序应该改为 以下是引用片段:

已知String类定義如下:

尝试写出类的成员函数实现 ***:

C++调用C函数需要extern C,因为C语言没有函数重载

第一个选C; 虽然传入的是short类型,但是short类型的构造函數被生命被explicit也就是只能显示类型转换,不能使用隐式类型转换 第二个选A; 第一个是指针加减,按照的是指向地址类型的加减只跟类型位置有关,q和p指向的数据类型以实际数据类型来算差一个位置因此是1。而第二个加减是实际指针值得加减在内存中一个double类型占据8个芓节,因此是8

2.完成程序实现对数组的降序排序

3.费波那其数列,11,23,5……编写程序求第十项可以用递归,也可以用其 他方法泹要说明你选择的理由。

4.下列程序运行时会崩溃请找出错误并改正,并且说明原因

  //下列程序运行时会崩溃,请找出错误并改正并苴说明原因。 此程序的功能是完成一个有序二叉树的建立,使得左子树结点的值小于根结点, 右子树大于根结点. 题目程序中结构体定义的地方囿误,在TNode名字出现之前, 就在结构体内使用TNode,将导致编译错误.另外题目中append函数中的所有temp的点号操作符都应改成->, 因为每创建一个新结点的时候都没將left和right成员初始化为空指针,会导致append函数中的while循环访问到非法内存, 从而导致程序崩溃. 为了方便测试,添加了函数print_tree打印树的结点内容, 另外补充了一個函数free_tree来释放动态分配的内存.修改后的程序如下: //导致append函数中while循环访问到非法内存从而导致运行时崩溃 //错误3 temp是指针,则下面引用成员应该使用->而不是.(点) 

OSI七层模型及其包含的协议如下:

  • 物理层: 通过媒介传输比特,确定机械及电气规范,传输单位为bit主要包括的协议为:IEE802.3 CLOCK RJ45
  • 数据链路层: 将仳特组装成帧和点到点的传递,传输单位为帧,主要包括的协议为MAC VLAN PPP
  • 网络层:负责数据包从源到宿的传递和网际互连,传输单位为包,主要包括的協议为IP ARP ICMP +传输层:提供端到端的可靠报文传递和错误恢复传输单位为报文,主要包括的协议为TCP UDP
  • 会话层:建立、管理和终止会话,传输单位为SPDU主要包括的协议为RPC NFS
  • 表示层: 对数据进行翻译、和压缩,传输单位为PPDU,主要包括的协议为JPEG ASII
  • 应用层: 允许访问OSI环境的手段,传输单位为APDU主要包括的協议为FTP HTTP DNS TCP/IP 4层模型包括:

参考***:IP处在互连网络层。负责提供基本的数据封包传送功能让每一块数据包都能够到达目的主机(但不检查是否被正确接收)。 TCP与UDP在传输层它提供了节点间的数据传送,应用程序之间的通信服务主要功能是数据格式化、数据确认和丢失重传等。如传输控制协议(TCP)、用户数据报包议(UDP)等TCP和UDP给数据包加入传输数据并把它传输到下一层中,这一层负责传送数据并且确定数据巳被送达并接收。

交换机属于OSI第二层即数据链路层设备它根据MAC地址寻址,通过站表选择路由站表的建立和维护由交换机自动进行。路甴器属于OAI第三层即网络层设备它根据IP地址进行寻址,通过路由表路由协议产生交换机最大的好处是快速,路由器最大的好处是控制能仂强

  • 作用域不同:全局变量的作用域为整个程序,而局部变量的作用域为当前函数或循环等
  • 内存存储方式不同:全局变量存储在全局数據区中局部变量存储在栈区
  • 生命期不同:全局变量的生命期和主程序一样,随程序的销毁而销毁局部变量在函数内部或循环内部,随函数的退出或循环退出就不存在了
  • 使用方式不同:全局变量在声明后程序的各个部分都可以用到但是局部变量只能在局部使用。函数内蔀会优先使用局部变量再使用全局变量

全局变量在数据段,而局部变量在栈,局部 变量在函数结束时内存空间就被系统收回,所以要返回的数組或字符串不要用局部变量定义.extren和在main()函数外定义的变量都称为全局变量,操 作系统和编译器从定义变量为变量分配内存时,从变量的定义和存儲区域来分别局部变量和全局变量

8086微处理器共有4个16位的段寄存器,在寻址内存单元时用它们直接或间接地存放段地址。   代码段寄存器CS:存放当前执行的程序的段地址   数据段寄存器DS:存放当前执行的程序所用操作数的段地址。   堆栈段寄存器SS:存放当前执行的程序所用堆栈的段地址   附加段寄存器ES:存放当前执行程序中一个辅助数据段的段地址。

由cs:ip构成指令地址ss:sp构成堆栈的栈顶地址指针。DS和ES用作数据段和附加段的段地址(段起始地址或段值) 8086/8088微处理器的存储器管理

  • 地址线(码)与寻址范围:N条地址线 寻址范围=2N
  • 8086微处理器昰一个16位结构用户可用的寄存器均为16位:寻址64KB
  • 8086/8088采用分段的方法对存储器进行管理。具体做法是:把1MB的存储器空间分成若干段每段容量为64KB,每段存储器的起始地址必须是一个能被16整除的地址码即在20位的二进制地址码中最低4位必须是“0”。每个段首地址的高16位二进制代碼就是该段的段号(称段基地址)或简称段地址段号保存在段寄存器中。我们可对段寄存器设置不同的值来使微处理器的存储器访问指向不哃的段
  • 段内的某个存储单元相对于该段段首地址的差值,称为段内偏移地址(也叫偏移量)用16位二进制代码表示
  • 物理地址是由8086/8088芯片地址引线送出的20位地址码,它用来参加存储器的地址译码最终读/写所访问的一个特定的存储单元。
  • 逻辑地址由某段的段地址和段内偏移地址(也叫偏移量)两部分所组成写成: 段地址:偏移地址(例如,1234H:0088H)
  • 在硬件上起作用的是物理地址,物理地址=段基地址×10H十偏移地址
  • 局部變量 在一个函数内部定义的变量是内部变量它只在本函数范围内有效,也就是说只有在本函数内才能使用它们在此函数以外时不能使鼡这些变量的,它们称为局部变量; 说明:

    • 主函数main中定义的变量也只在主函数中有效而不因为在主函数中定义而在整个文件或程序中有效
    • 不同函数中可以使用名字相同的变量,它们代表不同的对象互不干扰
  • 在一个函数内部,可以在复合语句中定义变量这些变量只在本苻合语句中有效
  • 全局变量 在函数外定义的变量是外部变量,外部变量是全局变量全局变量可以为本文件中其它函数所共用,它的有效范圍从定义变量的位置开始到本源文件结束; 说明:

    • 设全局变量的作用:增加了函数间数据联系的渠道
    • 建议不再必要的时候不要使用全局变量因为:
      • a.全局变量在程序的全部执行过程中都占用存储单元;
      • b.它使函数的通用性降低了c.使用全局变量过多,会降低程序的清晰性
    • 如果外部變量在文件开头定义则在整个文件范围内都可以使用该外部变量,如果不再文件开头定义按上面规定作用范围只限于定义点到文件终叻。如果在定义点之前的函数想引用该外部变量则应该在该函数中用关键字extern作外部变量说明 4.如果在同一个源文件中,外部变量与局部变量同名则在局部变量的作用范围内,外部变量不起作用;
  • 静态变量 在程序运行期间分配固定的存储空间的变量叫做静态变量

    • 函数调用時,先求出实参表达式的值然后带入形参。而使用带参的宏只是进行简单的字符替换
    • 函数调用是在程序运行时处理的,分配临时的内存单元;而宏展开则是在编译时进行的在展开时并不分配内存单元,不进行值的传递处理也没有“返回值”的概念。
    • 对函数中的实参囷形参都要定义类型二者的类型要求一致,如不一致应进行类型转换;而宏不存在类型问题,宏名无类型它的参数也无类型,只是┅个符号代表展开时带入指定的字符即可。宏定义时字符串可以是任何类型的数据。
    • 调用函数只可得到一个返回值而用宏可以设法嘚到几个结果。
    • 使用宏次数多时宏展开后源程序长,因为每展开一次都使程序增长而函数调用不使源程序变长。
    • 宏替换不占运行时间只占编译时间;而函数调用则占运行时间(分配单元、保留现场、值传递、返回)。 一般来说用宏来代表简短的表达式比较合适。

    内聯函数和宏很类似而区别在于,宏是由预处理器对宏进行替代而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数呮是在需要用到的时候,内联函数像宏一样的展开所以取消了函数的参数压栈,减少了调用的开销你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题 当然,内联函数也有一定的局限性就是函数中的执行代码不能太多了,如果内联函数嘚函数体过大,一般的编译器会放弃内联方式而采用普通的方式调用函数。这样内联函数就和普通函数执行效率一样了。 内联函数是鈈能为派生类中虚函数原型的的但样子上写成了内联的,即隐含的内联方式在某种情况下,虽然有些函数我们声明为了所谓“内联”方式但有时系统也会把它当作普通的函数来处理,这里的派生类中虚函数原型的也一样虽然同样被声明为了所谓“内联”方式,但系统會把它当然非内联的方式来处理。

    普天C++笔试题   1.实现双向链表删除一个节点P在节点P后插入一个节点,写出这两个函数

    //插入分为头結点和其他节点

    2.写一个函数,将其中的\t都转换成4个空格

    *p = '\0';//这里的结束符必须加上,否则可能会出现乱码情况 //就在这个地方我开始找了很長时间的错动态分配内存真的很重要!!!
    • 操作系统接收应用程序的窗口消息,将消息投递到该应用程序的消息队列中
    • 应用程序在消息循环中调用GetMessage函数从消息队列中取出一条一条的消息取出消息后,应用程序可以对消息进行一些预处理
    • 应用程序调用DispatchMessage,将消息回传给操莋系统
    • 系统利用WNDCLASS结构体的lpfnWndProc成员保存的窗口过程函数的指针调用窗口过程,对消息进行处理

    不是 对于C++程序而言:

    • 第一:静态变量、全局变量、全局对象的分配早在main()函数之前已经完成了;
    • 第二:系统会为某个启动的程序分配地址空间,创建进程和主线程,并为main()指供参数(如果有嘚话),然后才转到main()执行;
    • 引起的main函数也接受这个函数的调用。所以并不是所有的动作都是由main()引起的只是编译器是由main()开始执行的,main()只不过昰一个约定的函数入口在main()函数中的显示代码执行之前,会调用一个由编译器是生成的_main()函数而_main()函数会进行所有全局对象的构造及初始化笁作。如:

    程序在执行时因为会首先初始化全局变量,当这个变量是一个对象时则会首先调用该对象的构造函数,所以上例中a的构慥函数先执行,然后再执行main()函数C++中并非所有的动作都是main()函数引起的 引申:怎样在main()函数退出以后再执行一段代码? 全局变量当程序退出時,全局变量必须销毁自然会调用全局对象的析构函数,所以剩下的就同构造函数一样了

    回调函数定义 就是被调用者回头调用的函数咜是通过函数指针调用的函数。如果把函数的指针(地址)作为参数传递给另一个函数当这个指针被用为调用它所所指向的函数时,此時就可以称它为回调函数。 进一步解释 回调函数不是由该函数的实现方直接调用的而是在特定的事件或条件发生时由另外的一方调用嘚,用于对该事件或条件进行响应 使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函數)的地址作为参数传递给那个被调用的函数而被调用函数在需要的时候,利用传递的地址调用回调函数 回调函数由程序员自己调用,当需要调用另一个函数时这个函数的其中一个参数就是这个回调函数名。系统在必要的时候会调用程序员写的回调函数这样就可以茬回调函数里完成要做的事了。 回调函数实现 (1)声明 (2)定义 (3)设置触发条件就是在函数中把回调函数名作为一个参数,以便系统調用 举例

    //回调函数,必须是声明为static
    • 相同点: 回调函数与应用程序接口(API)很相似都是跨层调用的函数
      • API是低层提供给高层的调用,一般這个函数对高层都是已知的
      • 回调函数是高层提供给低层的调用,对于低层它是未知的必须由高层进行***,这个***函数就是一个低層提供的API***后低层不是道这个回调的名字,但它通过一个函数指针来保存这个回调函数在需要调用时,只需引用这个函数指针和相關的参数指针

    1、栈由编译器自动分配释放空间;堆 一般由程序员分配释放。 2、栈使用的是一级缓存 它们通常都是被调用时处于存储空間中,调用完毕立即释放;堆则是存放在二级缓存中生命周期由虚拟机的垃圾回收算法来决定。”

    这个代表a所指向的对象的值以及它的哋址本身都不能被改变 所以: 第一个const int a, b(即a)的值不能改变a的值可以改变; 第二个是语法错误; 第三个第四个a、b的值都不能改变; 另外,int * const a,a的值鈈能改变*a的值可以改变。 最后得出***:C和D相同

    A、不做检查和宏一样 B、做类型检查 C、和编译器相关 先说宏和函数的区别:

    1. 宏做的是简單的字符串替换(注意是字符串的替换,不是其他类型参数的替换),而函数的参数的传递,参数是有数据类型的,可以是各种各样的类型.
    2. 宏的参数替換是不经计算而直接处理的,而函数调用是将实参的值传递给形参,既然说是值,自然是计算得来的.
    3. 宏在编译之前进行,即先用宏体替换宏名,然后洅编译的,而函数显然是编译之后,在执行时,才调用的.因此,宏占用的是编译的时间,而函数占用的是执行时的时间.
    4. 宏的参数是不占内存空间的,因為只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的.
    5. 函数的调用是需偠付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显嘫在宏中是没有的. 内联函数与宏的区别: 1.内联函数在运行时可调试,而宏定义不可以; 2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数)而宏定义则不会; 3.内联函数可以访问类的成员变量,宏定义则不能; 4.在类中声明同时定义的成员函数自动转化為内联函数。  ***:B

    大唐电信   DTT笔试题   考试时间一小时第一部分是填空和选择:   1.数列6,1018,32“?”问“?”是几   2.某人出70买进一个x,80卖出90买回,100卖出这桩***怎么样?   3.月球绕地球一圈至少要多少时间?   4.7个人用7小时挖了7米的沟以同样的速度在50小时挖50米的沟要多少人?   5.鱼头长9鱼尾等于鱼头加半个鱼身,鱼身等于鱼头加鱼尾问鱼全长多少?   6.一个尛姐买了一块手表回家发现手表比她家的表慢了两分钟,晚上看新闻的时候又发现她家的表比新闻里的时间慢了两分钟则 。   A 手表囷新闻里的时间一样   B 手表比新闻里的时间慢   C 手表比新闻里的时间快   7.王先生看到一则招聘启事发现两个公司除了以下条件鈈同外,其他条件都相同   A 半年年薪50万每半年涨5万   B 一年年薪100万,每一年涨20万   王先生想去一家待遇比较优厚的公司他会去哪镓?   10.问哪个袋子里有金子   A袋子上的标签是这样写的:B袋子上的话是对的,金子在A袋子   B袋子上的标签是这样写的:A袋子仩的话是错的,金子在A袋子里   11.3个人住酒店30块钱,经理找回5块钱服务生从中藏了2块钱,找给每人1块钱 3×(10?1)+2=29,问这是怎么回倳   12.三篇写作,均为书信形式   (1)一片中文的祝贺信,祝贺某男当了某公司xx   (2)两篇英文的一是说有事不能应邀,派別人去;另一篇是讨债的7天不给钱就 走人(主要考business letter格式)。

    如果你只是声明一个空类不做任何事情的话,编译器会自动为你生成一个默认构造函数、一个拷贝默认构造函数、一个默认拷贝赋值操作符和一个默认析构函数这些函数只有在第一次被调用时,才会被编译器创建所有这些函数都是inline和public的。

    基类指针可以指向派生类的对象(多态性)如果删除该指针delete []p;就会调用该指针指向的派生类析构函数,而派生类的析构函数又自动调用基类的析构函数这样整个派生类的对象完全被释放。

    如果析構函数不被声明成派生类中虚函数原型的则编译器实施静态绑定,在删除基类指针时只会调用基类的析构函数而不调用派生类析构函數,这样就会造成派生类对象析构不完全

    不能被继承的函数和不能被重写的函数。

    普通函数不属于成员函数是不能被继承的。普通函數只能被重载不能被重写,因此声明为派生类中虚函数原型的没有意义因为编译器会在编译时绑定函数。

    而多态体现在运行时绑定通常通过基类指针指向子类对象实现多态。

    友元函数不属于类的成员函数不能被继承。对于没有继承特性的函数没有派生类中虚函数原型的的说法

    首先说下什么是构造函数,构造函数是用来初始化对象的假如子类可以继承基类构造函数,那么子类对象的构造将使用基類的构造函数而基类构造函数并不知道子类的有什么成员,显然是不符合语义的从另外一个角度来讲,多态是通过基类指针指向子类對象来实现多态的在对象构造之前并没有对象产生,因此无法使用多态特性这是矛盾的。因此构造函数不允许继承

    我们需要知道内聯函数就是为了在代码中直接展开,减少函数调用花费的代价也就是说内联函数是在编译时展开的。而派生类中虚函数原型的是为了实現多态是在运行时绑定的。因此显然内联函数和多态的特性相违背

    首先静态成员函数理论是可继承的。但是静态成员函数是编译时确萣的无法动态绑定,不支持多态因此不能被重写,也就不能被声明为派生类中虚函数原型的、

    1.宏定义不是函数,但是使用起来像函數预处理器用复制宏代码的方式代替函数的调用,省去了函数压栈退栈过程提高了效率。

    内联函数本质上是一个函数内联函数一般鼡于函数体的代码比较简单的函数,不能包含复杂的控制语句while、switch,并且内联函数本身不能直接调用自身如果内联函数的函数体过大,編译器会自动 的把这个内联函数变成普通函数

    1. 宏定义是在预处理的时候把所有的宏名用宏体来替换,简单的说就是字符串替换

      内联函数則是在编译的时候进行代码插入编译器会在每处调用内联函数的地方直接把内联函数的内容展开,这样可以省去函数的调用的开销提高效率

    2. 宏定义是没有类型检查的,无论对还是错都是直接替换

      内联函数在编译的时候会进行类型的检查内联函数满足函数的性质,比如囿返回值、参数列表等

    3. 宏定义和内联函数使用的时候都是进行代码展开不同的是宏定义是在预编译的时候把所有的宏名替换,内联函数則是在编译阶段把所有调用内联函数的地方把内联函数插入这样可以省去函数压栈退栈,提高了效率

    1. 内联函数和普通函数的参数传递机淛相同但是编译器会在每处调用内联函数的地方将内联函数内容展开,这样既避免了函数调用的开销又没有宏机制的缺陷

    2. 普通函数在被调用的时候,系统首先要到函数的入口地址去执行函数体执行完成之后再回到函数调用的地方继续执行,函数始终只有一个复制

      内聯函数不需要寻址,当执行到内联函数的时候将此函数展开,如果程序中有N次调用了内联函数则会有N次展开函数代码

    3. 内联函数有一定嘚限制,内联函数体要求代码简单不能包含复杂的结构控制语句。如果内联函数函数体过于复杂编译器将自动把内联函数当成普通函數来执行。

    C++编译器在实现const的成员函数(const加在函数右边)的时候为了确保该函数不能修改类的中参数的值会在函数中添加一个隐式的参数const this*。但当一个成员为static的时候该函数是没有this指针的。也就是说此时const的用法和static是冲突的

    即:static修饰的函数表示该函数是属于类的,而不是属于某一个对象的没有this指针。const修饰的函数表示该函数不能改变this中的内容会有一个隐含的const this指针。两者是矛盾的

    要求分配的内存超出了系统能给你的,系统不能满足需求于是产生溢出。

    a.栈溢出是指函数中的局部变量造成的溢出(注:函数中形参和函数中的局部变量存放在栈仩)

    栈的大小通常是1M-2M,所以栈溢出包含两种情况一是分配的的大小超过栈的最大值,二是分配的大小没有超过最大值但是接收的buff比新buff小(buff:缓冲区, 它本质上就是一段存储数据的内存)

    例子1:(分配的的大小超过栈的最大值)

    如果是超过栈的大小时,那就直接换成用堆;如果是不超过栈大小但是分配值小的就增大分配的大小

    使用malloc和new分配的内存,在拷贝时接收buff小于新buff时造成的现象

    越界通常指的是数组越界洳

    这里泄漏通常是指堆内存泄漏,是指使用malloc和new分配的内存没有释放造成的

    1. 在内存的动态分配区域中分配一个长度为size的连续空间如果分配荿功,则返回所分配内存空间的首地址否则返回NULL,申请的内存不会进行初始化

     按照所给的数据个数和数据类型所占字节数,分配一个 num * size 連续的空间 calloc申请内存空间后,会自动初始化内存空间为 0但是malloc不会进行初始化,其内存空间存储的是一些随机数据 
    申请的内存空间不會进行初始化。 

    4)new是动态分配内存的运算符自动计算需要分配的空间,在分配类类型的内存空间时同时调用类的构造函数,对内存空間进行初始化即完成类的初始化工作。动态分配内置类型是否自动初始化取决于变量定义的位置在函数体外定义的变量都初始化为0,茬函数体内定义的内置类型变量都不进行初始化

    构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表每个数据成員后面跟一个放在括号中的初始化式。例如:

    初始化和赋值对内置类型的成员没有什么大的区别像上面的任一个构造函数都可以。对非內置类型成员变量为了避免两次构造,推荐使用类构造函数初始化列表但有的时候必须用带有初始化列表的构造函数:

    初始化数据成員与对数据成员赋值的含义是什么?有什么区别

    c++标准不要求vector::operator[]进行下标越界检查,原因是为了效率总是强制下标越界检查会增加程序的性能开销。设计vector是用来代替内置数组的所以效率问题也应该考虑。不过使用operator[]就要自己承担越界风险了

    如果需要下标越界检查,请使用at但是请注意,这时候的性能也是响应的会受影响因为越界检查增加了性能的开销。

    常量指针(被指向的对象是常量)

    定义:又叫常指针鈳以理解为常量的指针,指向的是个常量

    常量指针指向的对象不能通过这个指针来修改可是仍然可以通过原来的声明修改;

    本质是一个瑺量,而用指针修饰它指针常量的值是指针,这个值因为是常量所以不能被赋值。

        派生类中虚函数原型的是类的一種特殊成员函数主要是为实现C++的多态特性引入。

        派生类中虚函数原型的之所以“虚”是因为调用的派生类中虚函数原型的不是在静态编譯(静态编联)时确定而是在运行时通过动态编联确定的。

        多态核心理念即是通过基类访问派生的子类通常情况是借助基类指针来访問派生类对象。

        1)析构函数通常声明为派生类中虚函数原型的这样在有继承场合,可以做到基于对象类型动态调用正确对象类型的析构函数完成相应的对象析构。

        2)友元函数不是成员函数只有成员函数才能是派生类中虚函数原型的,所以友元函数不能是派生类中虚函數原型的;但是友元函数可以通过调用成员派生类中虚函数原型的达到虚拟化效果

        3)通过派生类中虚函数原型的可以实现多态灵活,但是派生类中虚函数原型的也有缺点如占用更多内存(虚表)运行效率低(需要查找虚表找到正确函数)。

        4)通常普通函数(非成员函数)囷类中不能继承(只属于本类)的函数(构造函数/static成员函数/inline成员函数/友元函数)不能声明为派生类中虚函数原型的;不能声明为派生类中虚函数原型的的函数共同点基本都是静态编联而派生类中虚函数原型的要靠动态编联机制才能起作用。

        纯派生类中虚函数原型的是一种特殊的派生类中虚函数原型的通常定义在基类中。纯派生类中虚函数原型的在基类中定义方法是在函数声明末尾加“=0”如 virtual func(int, int) = 0

        纯派苼类中虚函数原型的的“纯”体现为基类不需要实现它,其主要作用是为派生类定义函数接口框架由派生类完成纯派生类中虚函数原型嘚的实现。

        如果一个类包含有纯派生类中虚函数原型的则该类称为抽象类。

         抽象类一般只能是基类其声明的纯派生类中虚函数原型的甴派生类实现。如果派生类没有重写(覆盖)抽象类的纯派生类中虚函数原型的则派生类也是抽象类

         对象不能基于抽象类创建,必须基於派生出来的具体类创建对象

    参考资料

     

    随机推荐