菜鸟笔记
提升您的技术认知

C++内功修炼----基础知识的零碎整理

c和c++的区别?

  1. 第一点就应该想到C是面向过程的语言,而C++是面向对象的语言,一般简历上第一条都是熟悉C/C++基本语法,了解C++面向对象思想,那么,请问什么是面向对象?
  2. C和C++动态管理内存的方法不一样,C是使用malloc/free函数,而C++除此之外还有new/delete关键字;(关于malooc/free与new/delete的不同又可以说一大堆,最后的扩展_1部分列出十大区别);
  3. 接下来就不得不谈到C中的struct和C++的类,C++的类是C所没有的,但是C中的struct是可以在C++中正常使用的,并且C++对struct进行了进一步的扩展,使struct在C++中可以和class一样当做类使用,而唯一和class不同的地方在于struct的成员默认访问修饰符是public,而class默认的是private;
  4. C++支持函数重载,而C不支持函数重载,而C++支持重载的依仗就在于C++的名字修饰与C不同,例如在C++中函数int fun(int ,int)经过名字修饰之后变为 _fun_int_int ,而C是 
    _fun,一般是这样的,所以C++才会支持不同的参数调用不同的函数;
  5. C++中有引用,而C没有;这样就不得不提一下引用和指针的区别(文后扩展_2);
  6. 当然还有C++全部变量的默认链接属性是外链接,而C是内连接;
  7. C 中用const修饰的变量不可以用在定义数组时的大小,但是C++用const修饰的变量可以(如果不进行&,解引用的操作的话,是存放在符号表的,不开辟内存);
  8. 当然还有局部变量的声明规则不同,多态,C++特有输入输出流之类的,很多,下面就不再列出来了

 

位运算

若一个数m满足 m = 2^n;那么k%m=k&(m-1)

​​​​​​​函数调用过程栈的变化,返回值和参数变量哪个先入栈?

1、调用者函数把被调函数所需要的参数按照与被调函数的形参顺序相反的顺序压入栈中,即:从右向左依次把被调函数所需要的参数压入栈;
2、调用者函数使用call指令调用被调函数,并把call指令的下一条指令的地址当成返回地址压入栈中(这个压栈操作隐含在call指令中);
3、在被调函数中,被调函数会先保存调用者函数的栈底地址(push ebp),然后再保存调用者函数的栈顶地址,即:当前被调函数的栈底地址(mov ebp,esp);
4、在被调函数中,从ebp的位置处开始存放被调函数中的局部变量和临时变量,并且这些变量的地址按照定义时的顺序依次减小,即:这些变量的地址是按照栈的延伸方向排列的,先定义的变量先入栈,后定义的变量后入栈;

​​​​​​​宏定义一个取两个数中较大值的功能

  1. #define MAX(x,y)((x>y?)x:y)
    printf实现原理?

在C/C++中,对函数参数的扫描是从后向前的。C/C++的函数参数通过压入堆栈的方式来给函数传参数的(堆栈是一种先进后出的数据结构),最先压入的参数最后出来,在计算机的内存中,数据有2块,一块是堆,一块是栈(函数参数及局部变量在这里),而栈是从内存的高地址向低地址生长的,控制生长的就是堆栈指针了,最先压入的参数是在最上面,就是说在所有参数的最后面,最后压入的参数在最下面,结构上看起来是第一个,所以最后压入的参数总是能够被函数找到,因为它就在堆栈指针的上方。printf的第一个被找到的参数就是那个字符指针,就是被双引号括起来的那一部分,函数通过判断字符串里控制参数的个数来判断参数个数及数据类型,通过这些就可算出数据需要的堆栈指针的偏移量了,下面给出printf("%d,%d",a,b);(其中a、b都是int型的)的汇编代码.

​​​​​​​全局变量和static变量的区别

1、全局变量(外部变量)的说明之前再冠以static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量当然也是静态存储方式。

这两者在存储方式上并无不同。这两者的区别在于非静态全局变量的作用域是整个源程序,当一个源程序由多个原文件组成时,非静态的全局变量在各个源文件中都是有效的。而静态全局变量则限制了其作用域,即只在定义该变量的源文件内有效,在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域限于一个源文件内,只能为该源文件内的函数公用,因此可以避免在其他源文件中引起错误。static全局变量与普通的全局变量的区别是static全局变量只初始化一次,防止在其他文件单元被引用。

2.static函数与普通函数有什么区别?
static函数与普通的函数作用域不同。尽在本文件中。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件。
    static函数与普通函数最主要区别是static函数在内存中只有一份,普通静态函数在每个被调用中维持一份拷贝程序的局部变量存在于(堆栈)中,全局变量存在于(静态区)中,动态申请数据存在于(堆)

23.malloc和free是线程安全的吗,在多线程开发用这两个函数应该注意什么

线程安全的,但是不可重入的。

如果并发量高,分配频繁,可以考虑使用tcmalloc。tcmalloc是用于优化C++写的多线程应用。可以使得程序在高并发下内存占用更加稳定。

在多线程高并发时候最好使用线程池,或者是直接一次性分配好内存,后面复用。

malloc/free会导致系统用户态/核心态切换,消耗大。

Malloc/free线程安全意味着它们要加锁,可以看到任务管理器的锯齿形状

不断的malloc/free运行久了会有内存碎片。

用到的概念,线程安全,可重入函数

   线程安全:在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常并且正确的执行,不会出现数据污染等情况。如果每次运行的结果和单线程运行的结果是一样的,而且其他变量的值和预期也是一样的,就是线程安全的。

   可重入函数:可重入函数可以有多于一个任务并发使用,而不必担心数据错误。不可充数函数不能超过一个数据共享,除非能确保函数的互斥,或者使用信号量,或者在代码的关键部分禁用中断。可重入函数可以在任何时候被中断,稍后继续运行,不会丢失数据。可重入函数要么使用本地,要么使用全局变量保护数据。

 

 

​​​​​​​C++如何处理多个异常的?

  1. C++中的异常情况: 
    语法错误(编译错误):比如变量未定义、括号不匹配、关键字拼写错误等等编译器在编译时能发现的错误,这类错误可以及时被编译器发现,而且可以及时知道出错的位置及原因,方便改正。 
    运行时错误:比如数组下标越界、系统内存不足等等。这类错误不易被程序员发现,它能通过编译且能进入运行,但运行时会出错,导致程序崩溃。为了有效处理程序运行时错误,C++中引入异常处理机制来解决此问题。
  2. C++异常处理机制: 
    异常处理基本思想:执行一个函数的过程中发现异常,可以不用在本函数内立即进行处理, 而是抛出该异常,让函数的调用者直接或间接处理这个问题。 
    C++异常处理机制由3个模块组成:try(检查)throw(抛出)catch(捕获) 
    抛出异常的语句格式为:throw 表达式;如果try块中程序段发现了异常则抛出异常。 
    try 

    可能抛出异常的语句;(检查) 

    catch(类型名[形参名])//捕获特定类型的异常 

    //处理1; 

    catch(类型名[形参名])//捕获特定类型的异常 

    //处理2; 

    catch(…)//捕获所有类型的异常 

 

​​​​​​​执行int main(int argc, char *argv[])时的内存结构

参数的含义是程序在命令行下运行的时候,需要输入argc 个参数,每个参数是以char 类型输入的,依次存在数组里面,数组是 argv[],所有的参数在指针

char * 指向的内存中,数组的中元素的个数为 argc 个第一个参数为程序的名称。​​​​​​​

讲讲大端小端,如何检测(三种方法)

大端模式:是指数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址端。
小端模式,是指数据的高字节保存在内存的高地址中,低位字节保存在在内存的低地址端。

  1. 直接读取存放在内存中的十六进制数值,取低位进行值判断

int a = 0x12345678;

int *c = &a;

c[0] == 0x12   大端模式

c[0] == 0x78   小段模式

  1. 用共同体来进行判断

union共同体所有数据成员是共享一段内存的,后写入的成员数据将覆盖之前的成员数据,成员数据都有相同的首地址。Union的大小为最大数据成员的大小。

union的成员数据共用内存,并且首地址都是低地址首字节。Int i= 1时:大端存储1放在最高位,小端存储1放在最低位。当读取char ch时,是最低地址首字节,大小端会显示不同的值。

union w w p;

{     p.i = 1;

int i; if(ch == 1)

char ch;

};

​​​​​​​查看内存的方法

  1. 首先打开vs编译器,创建好项目,并且将代码写进去,这里就不贴代码了,你可以随便的写个做个测试;
  2. 调试的时候做好相应的断点,然后点击开始调试;
  3. 程序调试之后会在你设置断点的地方暂停,然后选择调试->窗口->内存,就打开了内存数据查看的窗口了。

​​​​​​​编码实现某一变量某位清0或置1

#define BIT3 (0x1 << 3 ) Satic int a;

//设置a的bit 3:

void set_bit3( void )

{

      a |= BIT3; //将a第3位置1

}

//清a的bit 3

void set_bit3( void )

{

     a &= ~BIT3; //将a第3位清零

} 

​​​​​​​局部变量全局变量的问题?

  1. 局部会屏蔽全局。要用全局变量,需要使用"::"局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局部变量的作用域就在那个循环体内。
  2. 如何引用一个已经定义过的全局变量可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。
  3. 全局变量可不可以定义在可被多个.C文件包含的头文件中在不同的C文件中以static形式来声明同名全局变量。可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错
    为什么要用static_cast转换而不用c语言中的转换?
  4. 更加安全
  5. 更直接明显,能够一眼看出是什么类型转换为什么类型,容易找出程序中的错误;可清楚地辨别代码中每个显式的强制转;可读性更好,能体现程序员的意图

变量声明和定义区别?

声明仅仅是把变量的声明的位置及类型提供给编译器,并不分配内存空间;定义要在定义的地方为其分配存储空间。

相同变量可以再多处声明(外部变量extern),但只能在一处定义。

"零值比较"?

bool类型:if(flag)

int类型:if(flag == 0)

指针类型:if(flag == null)

float类型:if((flag >= -0.000001) && (flag <= 0. 000001))

static作用是什么?在C和C++中有何区别?

static可以修饰局部变量(静态局部变量)、全局变量(静态全局变量)和函数,被修饰的变量存储位置在静态区。对于静态局部变量,相对于一般局部变量其生命周期长,直到程序运行结束而非函数调用结束,且只在第一次被调用时定义;对于静态全局变量,相对于全局变量其可见范围被缩小,只能在本文件中可见;修饰函数时作用和修饰全局变量相同,都是为了限定访问域。C++的static除了上述两种用途,还可以修饰类成员(静态成员变量和静态成员函数),静态成员变量和静态成员函数不属于任何一个对象,是所有类实例所共有。static的数据记忆性可以满足函数在不同调用期的通信,也可以满足同一个类的多个实例间的通信。未初始化时,static变量默认值为0。

在main执行之前执行的代码可能是什么?

全局对象的构造函数

哪几种情况必须用到初始化成员列表?
初始化一个const成员。
初始化一个reference成员。
调用一个基类的构造函数,而该函数有一组参数。
调用一个数据成员对象的构造函数,而该函数有一组参数

 

​​​​​​​assert与NDEBUGE

  1. assert宏的原型定义在中,其作用是如果它的条件返回错误,则终止程序执行,原型定义:

#include

void assert( int expression );

assert的作用是现计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行如果表达式为真,assert什么也不做。

  1. NDEBUG宏是Standard C中定义的宏,专门用来控制assert()的行为。如果定义了这个宏,则assert不会起作用定义NDEBUG能避免检查各种条件所需的运行时开销,当然此时根本就不会执行运行时检查。

C Standard中规定了assert以宏来实现被设计来可以被多次包含,其中一上来就undef assert,然后由NDEBUG宏来决定其行为。

 

动态内存

​​​​​​​深拷贝与浅拷贝?

  1. 浅复制 —-只是拷贝了基本类型的数据,而引用类型数据,复制后也是会发生引用,我们把这种拷贝叫做“(浅复制)浅拷贝”,换句话说,浅复制仅仅是指向被复制的内存地址,如果原地址中对象被改变了,那么浅复制出来的对象也会相应改变

深复制 —-在计算机中开辟了一块新的内存地址用于存放复制的对象。

  1. 在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。

多态

 编译

​​​​​​​C语言的编译链接过程?

源代码-->预处理-->编译-->优化-->汇编-->链接-->可执行文件

  1. 预处理

读取c源程序,对其中的伪指令(以#开头的指令)和特殊符号进行处理。包括宏定义替换、条件编译指令、头文件包含指令、特殊符号 预编译程序所完成的基本上是对源程序的“替代”工作。经过此种替代,生成一个没有宏定义、没有条件编译指令、没有特殊符号的输出文件。.i预处理后的c文件,.ii预处理后的C++文件。

  1. 编译阶段

编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码.s文件

  1. 汇编过程

汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。.o目标文件

  1. 链接阶段
  1. 链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够诶操作系统装入执行的统一整体。

​​​​​​​ hello world 程序开始到打印到屏幕上的全过程?

1.用户告诉操作系统执行HelloWorld程序(通过键盘输入等)

2操作系统:找到helloworld程序的相关信息,检查其类型是否是可执行文件;并通过程序首部信息,确定代码和数据在可执行文件中的位置并计算出对应的磁盘块地址。

3操作系统:创建一个新进程,将HelloWorld可执行文件映射到该进程结构,表示由该进程执行helloworld程序。

4操作系统:为helloworld程序设置cpu上下文环境,并跳到程序开始处。

5执行helloworld程序的第一条指令,发生缺页异常

6操作系统:分配一页物理内存,并将代码从磁盘读入内存,然后继续执行helloworld程序

7helloword程序执行puts函数(系统调用),在显示器上写一字符串

8操作系统:找到要将字符串送往的显示设备,通常设备是由一个进程控制的,所以,操作系统将要写的字符串送给该进程

9操作系统:控制设备的进程告诉设备的窗口系统,它要显示该字符串,窗口系统确定这是一个合法的操作,然后将字符串转换成像素,将像素写入设备的存储映像区

10视频硬件将像素转换成显示器可接收和一组控制数据信号

11显示器解释信号,激发液晶屏

12OK,我们在屏幕上看到了HelloWorld

动态联编与静态联编

  1. 在C++中,联编是指一个计算机程序不同部分彼此关联的过程。按照联编所进行的阶段不同,可以分为静态联编和动态联编;
  2. 静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。
  3. 动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强,但效率低。动态联编规定,只能通过指向基类的指针或基类对象的引用来调用虚函数,其格式为:指向基类的指针变量名->虚函数名(实参表)或基类对象的引用名.虚函数名(实参表)
  4. 实现动态联编三个条件:

必须把动态联编的行为定义为类的虚函数;

类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来;

必须先使用基类指针指向子类型的对象,然后直接或间接使用基类指针调用虚函数;

​​​​​​​动态编译与静态编译

  1. 静态编译,编译器在编译可执行文件时,把需要用到的对应动态链接库中的部分提取出来,连接到可执行文件中去,使可执行文件在运行时不需要依赖于动态链接库
  2. 动态编译的可执行文件需要附带一个动态链接库,在执行时,需要调用其对应动态链接库的命令。所以其优点一方面是缩小了执行文件本身的体积,另一方面是加快了编译速度,节省了系统资源。缺点是哪怕是很简单的程序,只用到了链接库的一两条命令,也需要附带一个相对庞大的链接库;二是如果其他计算机上没有安装对应的运行库,则用动态编译的可执行文件就不能运行。
    动态链接和静态链接区别
  3. 静态连接库就是把(lib)文件中用到的函数代码直接链接进目标程序,程序运行的时候不再需要其它的库文件;动态链接就是把调用的函数所在文件模块(DLL)和调用函数在文件中的位置等信息链接进目标程序,程序运行的时候再从DLL中寻找相应函数代码,因此需要相应DLL文件的支持。
  4. 静态链接库与动态链接库都是共享代码的方式,如果采用静态链接库,则无论你愿不愿意,lib 中的指令全部被直接包含在最终生成的 EXE 文件中了。但是若使用 DLL,该 DLL 不必被包含在最终 EXE 文件中,EXE 文件执行时可以“动态”地引用和卸载这个与 EXE 独立的 DLL 文件。静态链接库和动态链接库的另外一个区别在于静态链接库不能再包含其他的动态链接库或者静态库,而在动态链接库中还可以再包含其他的动态或静态链接库。
      1. 动态库就是在需要调用其中的函数时,根据函数映射表找到该函数然后调入堆栈执行。如果在当前工程中有多处对dll文件中同一个函数的调用,那么执行时,这个函数只会留下一份拷贝。但是如果有多处对lib文件中同一个函数的调用,那么执行时,该函数将在当前程序的执行空间里留下多份拷贝,而且是一处调用就产生一份拷贝。
        怎么快速定位错误出现的地方
  5. 如果是简单的错误,可以直接双击错误列表里的错误项或者生成输出的错误信息中带行号的地方就可以让编辑窗口定位到错误的位置上。
      1. 对于复杂的模板错误,最好使用生成输出窗口。多数情况下出发错误的位置是最靠后的引用位置。如果这样确定不了错误,就需要先把自己写的代码里的引用位置找出来,然后逐个分析了。
        Denug和release的区别
  1. 调试版本,包含调试信息,所以容量比Release大很多,并且不进行任何优化(优化会使调试复杂化,因为源代码和生成的指令间关系会更复杂),便于程序员调试Debug模式下生成两个文件,除了.exe.dll文件外,还有一个.pdb文件,该文件记录了代码中断点等调试信息 
  2. 发布版本,不对源代码进行调试,编译时对应用程序的速度进行优化,使得程序在代码大小和运行速度上都是最优的。(调试信息可在单独的PDB文件中生成)。Release模式下生成一个文件.exe或.dll文件
  3. 实际上,Debug 和 Release 并没有本质的界限,他们只是一组编译选项的集合,编译器只是按照预定的选项行动。事实上,我们甚至可以修改这些选项从而得到优化过的调试版本或是带跟踪语句的发布版本。
    法调用的原理(栈,汇编)
  4. 机器用栈来传递过程参数、存储返回信息、保存寄存器用于以后恢复,以及本地存储。而为单个过程分配的那部分栈称为帧栈;帧栈可以认为是程序栈的一段,它有两个端点,一个标识起始地址,一个标识着结束地址,两个指针结束地址指针esp,开始地址指针ebp;
  5. 由一系列栈帧构成,这些栈帧对应一个过程,而且每一个栈指针+4的位置存储函数返回地址;每一个栈帧都建立在调用者的下方,当被调用者执行完毕时,这一段栈帧会被释放。由于栈帧是向地址递减的方向延伸,因此如果我们将栈指针减去一定的值,就相当于给栈帧分配了一定空间的内存。如果将栈指针加上一定的值,也就是向上移动,那么就相当于压缩了栈帧的长度,也就是说内存被释放了。
  6. 过程实现
  • 备份原来的帧指针,调整当前的栈帧指针到栈指针位置;
  • 建立起来的栈帧就是为被调用者准备的,当被调用者使用栈帧时,需要给临时变量分配预留内存;
  • 使用建立好的栈帧,比如读取和写入,一般使用mov,push以及pop指令等等。
  • 恢复被调用者寄存器当中的值,这一过程其实是从栈帧中将备份的值再恢复到寄存器,不过此时这些值可能已经不在栈顶了
  • 恢复被调用者寄存器当中的值,这一过程其实是从栈帧中将备份的值再恢复到寄存器,不过此时这些值可能已经不在栈顶了。
  • 释放被调用者的栈帧,释放就意味着将栈指针加大,而具体的做法一般是直接将栈指针指向帧指针,因此会采用类似下面的汇编代码处理。
  • 恢复调用者的栈帧,恢复其实就是调整栈帧两端,使得当前栈帧的区域又回到了原始的位置。
  • 弹出返回地址,跳出当前过程,继续执行调用者的代码。
  1. 过程调用和返回指令
  • call指令
  • leave指令
  • ret指令

预处理

​​​​​​​#include 的顺序以及尖叫括号和双引号的区别

表示编译器只在系统默认目录或尖括号内的工作目录下搜索头文件,并不去用户的工作目录下寻找,所以一般尖括号用于包含标准库文件

表示编译器先在用户的工作目录下搜索头文件,如果搜索不到则到系统默认目录下去寻找,所以双引号一般用于包含用户自己编写的头文件。

封装

​​​​​​​main函数有没有返回值

  1. 程序运行过程入口点main函数,main()函数返回值类型必须是int,这样返回值才能传递给程序激活者(如操作系统)表示程序正常退出。main(int args, char **argv) 参数的传递。参数的处理,一般会调用getopt()函数处理,但实践中,这仅仅是一部分,不会经常用到的技能点。
    c++怎么实现一个函数先于main函数运行
  2. 如果在main函数之前声明一个类的全局的对象。那么其执行顺序,根据全局对象的生存期和作用域,肯定先于main函数。

class simpleClass  

{  

public:  

       simpleClass( )  

       {  

              cout << "simpleClass constructor.." << endl;

       }  

};     

simpleClass g_objectSimple;         //step1全局对象  

int _tmain(int argc, _TCHAR* argv[])  //step3  

{  

       return 0;  

}  

  1. 定义在main( )函数之前的全局对象静态对象的构造函数在main( )函数之前执行。
  2. Main函数执行之前,主要就是初始化系统相关资源
  • 设置栈指针
  • 初始化static静态和global全局变量,即data段的内容
  • 将未初始化部分的全局变量赋初值:数值型short,int,long等为0,bool为FALSE,指针为NULL,等等,即.bss段的内容
  • 全局对象初始化,在main之前调用构造函数
  • 将main函数的参数,argc,argv等传递给main函数,然后才真正运行main函数
  1. Main函数执行之后
  • 全局对象的析构函数会在main函数之后执行;
  • 可以用_onexit 注册一个函数,它会在main 之后执行;
    员函数里memset(this,0,sizeof(*this))会发生什么
  1. 有时候类里面定义了很多int,char,struct等c语言里的那些类型的变量,我习惯在构造函数中将它们初始化为0,但是一句句的写太麻烦,所以直接就memset(this, 0, sizeof *this);将整个对象的内存全部置为0。对于这种情形可以很好的工作,但是下面几种情形是不可以这么使用的;
  2. 类含有虚函数表:这么做会破坏虚函数表,后续对虚函数的调用都将出现异常;
  3. 类中含有C++类型的对象:例如,类中定义了一个list的对象,由于在构造函数体的代码执行之前就对list对象完成了初始化,假设list在它的构造函数里分配了内存,那么我们这么一做就破坏了list对象的内存。

对象存储空间?

非静态成员的数据类型大小之和。

编译器加入的额外成员变量(如指向虚函数表的指针)。

<

p style="margin-left:0pt;">为了边缘对齐优化加入的padding。