C C++最常考面试题总结_第1页
C C++最常考面试题总结_第2页
C C++最常考面试题总结_第3页
C C++最常考面试题总结_第4页
C C++最常考面试题总结_第5页
已阅读5页,还剩19页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

目录

1.char和int之间的转换?..............................................................2

2.sizeof和strlen区别?.................................................................2

3.不使用sizeof。,如何求int占用的字节数?..............................................2

4.C中struct和union的区别?............................................................3

5.static关键字?.........................................................................3

6.volatile?.....................................................................................................................................................5

7.const关键字?........................................................................6

8.malloc,new、delete、free.....................................................................................................................11

9.extern"C"的作用?...................................................................14

10.C语言内存分配方式?................................................................14

11.重新实现memcpyO函数?...........................................................14

12.指针与引用的区别?..................................................................15

13.C++中四个与类型转换相关的关键字?.................................................16

14.C语言圆圆?.........................................................................18

15.explicit关键字?.....................................................................19

16.虚继承和继承?......................................................................21

17.C++如何实现多态?.................................................................22

15.C++子类调用父类的同名成员函数和成员变量?........................................23

16.析构函数可以抛出异常吗?为什么不能抛出异常?除了资源泄露,还有其他需考虑的因素吗?23

17.关于拷贝构造函数、深拷贝、浅拷贝?................................................24

18.关于_declspec(dllexport)^n_declspec(dllimport)Windows平台编写和使用DLL?................24

19.名字修饰约定extern"C"与extern"C++"?....................................................................................24

1.char和int之间的转换?

都分为signed和unsigned类型,默认都是signed类型。

从长字节数据类型转换为短字节数据类型会产生截断,如int转char会取int最低字节给char,且是

有符号的即最高位是符号位,如果转为unsignedchar,直接取int最低字节给char。

从短字节数据类型转为长字节数据类型,如char转int,会在前三个字节填充符号位,根据char的符

号位决定填充的符号位是0还是1。如果是unsignedchar转int前面三个字节填充0。

2.sizeof和strlen区别?

strlen是一个库函数,计算传进来的字符串的长度,该长度不包括字符串结尾的‘\0'。

sizeof是一个运算符,它计算的是传进来的指针在内存中所占空间大小,在编译时就已经获得了结果。

strlen函数与sizeof的区另U,C语言strlen与sizeof的区别详解()

strlen计算的是字符串到''0'位置的大小

sizeof计算的字符串占的内存大小

#include<stdio.h>

#include<string.h>

intmain()

|

charstr[100]={0};

strcpy(str,"abed");

intstr_len=strlen(str);

intstr__size=sizeof(str);

printf(nstrlen(str)=%d\nn,(str_len));

printf(nsizeof(str)=%d\nn,(str_size));

return0;

)

strlen(str)=4

sizeof(str)=100

3.不使用sizeof。,如何求int占用的字节数?

#defineMySizeof(Value)(char*)(&Value+l)-(char*)&Value

(char*)&Value返回Value地址的第一个字节

(char*)(&Value+l)返回Value地址的下一个地址的第一个字节,所以他们之差为他所占的字节数。

4.C中struct和union的区别?

struct(结构体)与union(联合体)是C语言中两种不同的数据结构,两者都是常见的复合结构,其区

别主要表现在以下两个方面.

1.结构体与联合体虽然都是由多个不同的数据类型成员组成的,但不同之处在于联合体中所有成员共

用一块地址空间,即联合体只存放了一个被选中的成员,而结构体中所有成员占用空间是累加的,

其所有成员都存在,不同成员会存放在不同的地址.在计算一个结构型变量的总长度时,其内存空

间大小等于所有成员长度之和(需要考虑字节对齐),而在联合体中,所有成员不能同时占用内存

空间,它们不能同时存在,所以一个联合型变量的长度等于其最长的成员的长度。

2.对于联合体的不同成员赋值,将会对它的其他成员重写,原来成员的值就不存在了,而对结构体的

不同成员赋值是互不影响的.

举个例子.下列代码执行结果是多少?

1typedefunion{doublei;intk[5];charc;}DATE;

2typedefstructdata(intcat;DATEcow;doubledog;)too;

3DATEmax;

4printfC'%d",sizeof(too)+sizeof(max));

假设为32位机器,in理占4个字节,double型占8个字节,cha国占1个字节,而DATE是f联合型变

量,联合型变量共用空间,uion里面最大的变量类型是int[5],所以占用20个字节,它的大小是20.而

由于union中double占了8个字节,因此union是要8个字节对齐,所占内存空间为8的倍数。为了实现

8个字节对齐,所占空间为24.而data是一个结构体变量,每个变量分开占用空间,依次为sizeof(int)

+sizeof(DATE)+sizeof(double)=4+24+8=36按照8字节对齐,占用空间为40,所以结果为

40+24=64,

5.static关键字?

可以分为面向过程程序设计中的static和面向对象程序设计中的static,前者主要是修饰普通变量和函

数,不涉及类,后者主要是涉及它对类内成员的修饰作用。

面向过程程序设计中:

静态全局变量:在全局数据区分配内存(全局数据区的数据不会因为函数的退出而释放内存),未初始

化的话被自动初始化为0o静态全局变量在声明它的整个文件都是可见的,在文件之外是不可见的。所以

在一个文件中staticintn;在另一个文件中externintn;是会报错的。

静态局部变量:通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配

栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。但有时候我们需要在两次

调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于

函数本身了,不再仅受函数的控制,给程序的维护带来不便。静态局部变量正好可以解决这个问题。静态

局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。

静态局部变量有以下特点:66

•该变量在全局数据区分配内存;

•静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;

•静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;

•它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句

块结束时,其作用域随之结束;

静态函数:函数返回类型前加上static关键字。静态函数只能在声明它的文件中可见,其他文件不能

调用,所以其他文件可以定义名字相同的函数,不会发生冲突。

面向对象程序设计中:

静态数据成员:

#include<iostream.h>

classMyclass

{

public:

Myclass(inta,intb,intc);

voidGetSumO;

private:

inta,b,c;

staticintSum;〃声明静态数据成员

};

intMyclass::Sum=0;〃定义并初始化静态数据成员

•对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象

被定义了多少个,静态数据成员在程序中也只有一份拷贝,由该类型的所有对象共享访问。也就是说,静态

数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象

共用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;

•静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。在Example

5中,语句intMyclass::Sum=0;是定义静态数据成员;

•静态数据成员和普通数据成员一样遵从public,protected,private访问规则;

•因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没

有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;

•静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为:

〈数据类型〉(类名>::<静态数据成员名>=<值>

•类的静态数据成员有两种访问形式:

<类对象名>.<静态数据成员名>或<类类型名>::<静态数据成员名>

如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员

I

•静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相

同的。所以,应该把利息设为存款类的静态数据成员。这有两个好处,第一,不管定义多少个存款类对象,

利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变

一次,则所有存款类对象的利息全改变过来了;

•同全局变量相比,使用静态数据成员有两个优势:

1.静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性;

2.可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;

静态成员函数:与静态数据成员相似,它为类的全部服务而不是为具体的某一类对象服务,普通的成

员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个

类的具体对象的。通常情况下,this是缺省的。如函数fn()实际上是this->fn()。但是与普通函数相比,静

态成员函数由于不是与任何的对象相联系,因此它不具有this指针。从这个意义上讲,它无法访问属于类

对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。

#include<iostream.h>

classMyclass

(

public:

Myclass(inta,intb,intc);

staticvoidGetSum。;/声明静态成员函数

private:

inta,b,c;

staticintSum;〃声明静态数据成员

);

intMyclass::Sum=O;〃定义并初始化静态数据成员

Myclass::Myclass(inta,intb,intc)

(

this->a=a;

this->b=b;

this->c=c;

Sum+=a+b+c;〃非静态成员函数可以访问静态数据成员

}

voidMyclass::GetSum()〃静态成员函数的实现

(

//cout<<a<<endl;〃错误代码,a是非静态数据成员

cout<<"Sum="<<Sum<<endl;

}

关于静态成员函数,可以总结为以下几点:

•出现在类体外的函数定义不能指定关键字static;

・静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;

•非静态成员函数可以任意地访问静态成员函数和静态数据成员;

•静态成员函数不能访问非静态成员函数和非静态数据成员:

•由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;

•调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函

数,也可以直接使用如下格式:

(类名>::<静态成员函数名〉(〈参数表〉)

调用类的静态成员函数。

Static类成员函数为何不能设置为虚函数:

static类成员函数没有this指针,虚函数需要通过this指针找到,所以静态函数不可能是虚函数。

6.volatile?

Volatile关键字是一种类型修饰符,当要求使用volatile声明的变量的值的时候,系统总是重新从它

所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据,而且读取的数据立刻被保存(即让编译

器不做优化)。

一般说来,volatile用在如下的几个地方:

1)中断服务程序中修改的供其它程序检测的变量需要加volatile;

2)多任务环境下各任务间共享的标志应该加volatile;

3)存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;

多线程下的volatile

有些变量是用volatile关键字声明的。当两个线程都要用到某一个变量且该变量的值会被改变时,应

该用volatile声明,该关键字的作用是防止优化编译器把变量从内存装入CPU寄存器中。如果变量被装入

寄存器,那么两个线程有可能一个使用内存中的变量,一个使用寄存器中的变量,这会造成程序的错误执

行。volatile的意思是让编译器每次操作该变量时一定要从内存中真正取出,而不是使用已经存在寄存器中

的值,如下:

volatileBOOLbStop=FALSE;

(1)在一个线程中:

while(lbStop)

{•.)

bStop=FALSE;

return;

(2)在另外一个线程中,要终止上面的线程循环:

bStop=TRUE;

while(bStop);〃等待上面的线程终止,如果bStop不使用volatile申明,那么这个循环将是一个死循

环,因为bStop已经读取到了寄存器中,寄存器中bStop的值永远不会变成FALSE,加上volatile,

程序在执行时,每次均从内存中读出bStop的值,就不会死循环了。

这个关键字是用来设定某个对象的存储位置在内存中,而不是寄存器中。因为一般的对象编译器

可能会将其的拷贝放在寄存器中用以加快指令的执行速度,例如下段代码中:

IntnMyCounter=O;

for(;nMyCounter<100;nMyCounter++)

在此段代码中,nMyCounter的拷贝可能存放到某个寄存器中(循环中,对nMyCounter的测试

及操作总是对此寄存器中的值进行),但是另外又有段代码执行了这样的操作:nMyCounter-=l;这个

操作中,对nMyCounter的改变是对内存中的nMyCounter进行操作,于是出现了这样一个现象:

nMyCounter的改变不同步。

7.const关键字?

关于它的定义与声明:

conststd::stringhi="hell。";

constintj=0;〃错误,i未初始化|

常量在定义后就不能被修改,所以普通的常量定义时必须初始化。而对于类定义中的const成员变量,

必须通过初始化列表进行初始化。如下所示:

cLassA

(

public:

A(inti);

voidprint();

int&r;

private:

inta;

staticconstintb;

};

intA::b=10;

A::A(inti):a(i),r(a)

(

)

正常一个普通的变量定义成全局的之后,在其他的文件中用extern声明一下就可以访问了。但是在全局作

用域声明的const变量只存在于该文件中,不能在别的文件中访问,若想访问,需要在声明的时候就指定

为extern。如下:

//file_l.ee

externconstintbufSize=fcn();

2.cc

externconstintbufSize;//usesbufSiz|efromf.h

for(intindex=0;index!=bufSize;++index)

即:非const变量默认为extern,要使const变量能够在其他文件中访问,必须在文件中显式地指定它为

externo

关于const与指针的结合:

指向常量的指针:

constdouble*cptr;

这里的eptr本身并不是const,只是该指针所指向的值是const修饰的,所以指针可以修改,重新指向不同

的constdouble,但是不能通过指针修改它所指向的量,即:

♦eptr=42;//error!!i|

不能使用void*指针保存const对象的地址,必须使用constvoid*类型的指针保存const对象的地址。

二。「intuniverse=42;

orvoid*cpv=&universe;

void*pv=&universe;//Erroruniverse>const

如果一个变量被const修饰了,那么要想用指针指向这个变量,这个指针也必须用const修饰。

doubLepi=3.14;

doubLe*ptr=π//Error

doubLe*cptr=π

如果一个变量是普通变量,既可以用常规指针指向它,也可以用指向const对象的指针指向它。即:允许

把非const对象的地址赋值给一个指向const对象的指针。如下:

constdouble*cptr;

doubtedval=3.14;

cptr=&dval;|

在这种情况下,我们不能通过cptr指针修改dval的值,即使cptr指针指向的是一个非const变量!!!但

是可以通过其他方式修改dval的值,如下:

dval=3.13159;I

♦cptr=3.14159;7Error!!!Qconst

doubLe*ptr=&dval;:一个非cons,Jf^dval

♦ptr=2.72;

cout<<♦cptr;

综上可以把指向const对象的指针理解为自以为指向const对象的指针。

Const指针(常指针)

interrNumb=0;

int*curErr=&errNumb;

以上为指向int型对象的const指针,该指针本身不能修改,即不能让该指针指向其他对象,const指针必

须在定义的时候初始化。

指向常量的常指针:

doubLepi=3.14159;

doubte*:pi_ptr=π

变量和指针本身都不能修改。

关于const与static的区别:

const定义的常量在超出其作用域之后其空间会被释放,而static定义的静态常量在函数执行后不会释

放其存储空间。

static表示的是静态的。类的静态成员函数、静态成员变量是和类相关的,而不是和类的具体对象相关

的。即使没有具体对象,也能调用类的静态成员函数和成员变量。一般类的静态函数几乎就是一个全局函

数,只不过它的作用域限于包含它的文件中。

在C++中,static静态成员变量不能在类的内部初始化。在类的内部只是声明,定义必须在类定义体

的外部,通常在类的实现文件中初始化,如:doubleAccount::Rate=2.25;static关键字只能用于类定义体

内部的声明中,定义时不能标示为static

在C++中,const成员变量也不能在类定义处初始化,只能通过构造函数初始化列表进行,并且必须

有构造函数。

const数据成员,只在某个对象生存期内是常量,而对于整个类而言却是可变的。因为类可以创建多个

对象,不同的对象其const数据成员的值可以不同。所以不能在类的声明中初始化const数据成员,因为

类的对象没被创建时,编译器不知道const数据成员的值是什么。

const数据成员的初始化只能在类的构造函数的初始化列表中进行。要想建立在整个类中都恒定的常

量,应该用类中的枚举常量来实现,或者staticconst。例如使用枚举常量:

classA

{-

enum{SIZE1=100,SIZE2=200};〃枚举常量

intarrayl[SIZEl];

intarray2[SIZE2];

);

枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是:它的隐含数据类

型是整数,其最大值有限,且不能表示浮点数(^PI=3.14159)Osizeof(A)=1200;o

const成员函数主要目的是防止成员函数修改对象的内容。即const成员函数不能修改成员变量的值,

但可以访问成员变量。当访问成员函数时,该函数只能是const成员函数。

static成员函数主要目的是作为类作用域的全局函数。不能访问类的非静态数据成员。类的静态成员

函数没有this指针,这导致:1、不能直接存取类的非静态成员变量,调用非静态成员函数2、不能被声明

为virtual

cLassTest

f

public:

Test():a(0){}

enum{sizel=100,size2=200};

private:

constinta;〃只1

staticintb;

conststaticintc;〃与

li

intTest::b=0;

cosntintTest::c=0;

关于static、constxstaticconstsconststatic成员的初始化问题:

在类中建立一个const时,不能给他初始值,只能在构造函数初始化列表中初始化:

cLassfoo

1

foo():i(100){}

nstinti=100;

15

foo::foo():i(100)

类中的static变量是属于类的,不属于某个对象,它在整个程序的运行过程中只有一个副本,因此不能在

定义对象时对变量进行初始化,即不能用构造函数进行初始化,正确方式如下:

cLassfoo

foo();

inti)

};

intfoo::i=20;

类里的staticcosnt和conststatic成员初始化(这两种写法是一致的!!)

cLassTest

intmaskl;

intmask2;

);

Test::maskl=0xffff;

Test::mask2=0xffff;

关于const与#define宏定义的区别:

(1)编译器处理方式不同

define宏是在预处理阶段展开。

const常量是编译运行阶段使用。

(2)类型和安全检查不同

define宏没有类型,不做任何类型检查,仅仅是展开。

const常量有具体的类型,在编译阶段会执行类型检查。

⑶存储方式不同

define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。(宏定义不分配内存,变量定

义分配内存。)

const常量会在内存中分配(可以是堆中也可以是栈中)。

(4)const可以节省空间,避免不必要的内存分配。例如:

#definePI3.14159〃常量宏

constdoulbePi=3.14159;//此时并未将Pi放入ROM中......

doublei=Pi;〃此时为Pi分配内存,以后不再分配!

double仁PI;〃编译期间进行宏替换,分配内存

doublej=Pi;〃没有内存分配

doubleJ=PI;〃再进行宏替换,又一次分配内存!

const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即

数,所以,const定义的常量在程序运行过程中只有一份拷贝(因为是全局的只读变量,存在静态区),

而#define定义的常量在内存中有若干个拷贝。

⑸提高了效率。编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表中,这使得它

成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。

(6)宏替换只作替换,不做计算,不做表达式求解;

宏预编译时就替换了,程序运行时,并不分配内存。

顶层const与底层const

C++顶层const与底层const总结-简书()

8.malloc>new、delete、free

malloc能够申请的空间大小与物理内存的大小没有直接关系,仅与程序的虚拟地址空间相关。程序运

行时,堆空间只是程序向操作系统申请划出来的一大块虚拟地址空间。应用程序通过malloc申请空间,得

到的是在虚拟地址空间中的地址,之后程序运行所提供的物理内存是由操作系统完成的。

关于new与malloc的区别:

a.申请的内存所在位置

new操作符从自由存储区(freestore)上为对象动态分配内存空间,而malloc函数从堆上动态分配内

存。自由存储区是C++基于new操作符的一个抽象概念,凡是通过new操作符进行内存申请,该内存即

为自由存储区。而堆是操作系统中的术语,是操作系统所维护的一块特殊内存,用于程序的内存动态分配,

C语言使用malloc从堆上分配内存,使用free释放已分配的对应内存。

那么自由存储区是否能够是堆(问题等价于new是否能在堆上动态分配内存),这取决于operatornew

的实现细节。自由存储区不仅可以是堆,还可以是静态存储区,这都看operatornew在哪里为对象分配内

存。

b.返回类型安全性

new操作符内存分配成功时,返回的是对象类型的指针,类型严格与对象匹配,无须进行类型转换,

故new是符合类型安全性的操作符。而malloc内存分配成功则是返回void*,需要通过强制类型转换将

void*指针转换成我们需要的类型。

c.内存分配失败时的返回值

new内存分配失败时,会抛出bad_alloc异常,它不会返回NULL;malloc分配内存失败时返回NULL。

在使用C语言时,我们习惯在malloc分配内存后判断分配是否成功:

int*a=(int*)malloc(sizeof(int));

if(NULL==a)

)

else

)

从C语言走入C++阵营的新手可能会把这个习惯带入C++:

int*a=newint();

if(NULL==a)

(

)

else

}

实际上这样做一点意义也没有,因为new根本不会返回NULL,而且程序能够执行到if语句已经说明内存

分配成功了,如果失败早就抛异常了。正确的做法应该是使用异常机制:

try

(

int*a=newint();

)

catch(bad_alloc)

)

d.是否需要指定内存大小

使用new操作符申请内存分配时无须指定内存块的大小,编译器会根据类型信息自行计算,而malloc

则需要显式地指出所需内存的尺寸。

classA{...}

A*ptr=newA;

A*ptr=(A*)malloc(sizeof(A));〃需要显式指定所需内存大小sizeof(A);

当然了,我这里使用malloc来为我们自定义类型分配内存是不怎么合适的,请看下一条。

e.是否调用构造函数/析构函数

使用new操作符来分配对象内存时会经历三个步骤:

第一步调用operatornew函数(对于数组是operatornew。)分配一块足够大的,原始的,未命名的内

存空间以便存储特定类型的对象。

第二步:编译器运行相应的构造函数以构造对象,并为其传入初值。

第三部:对象构造完成后,返回一个指向该对象的指针。

使用delete操作符来释放对象内存时会经历两个步骤:

第一步:调用对象的析构函数。

第二步:编译器调用operatordelete(或operatordelete。)函数释放内存空间。

总之来说,new/delete会调用对象的构造函数/析构函数以完成对象的构造/析构。而malloc则不会。

f.对数组的处理

C++提供了new。与delete口来专门处理数组类型:

A*ptr=newA[10];〃分配10个A对象

使用new口分配的内存必须使用delete□进行释放:

delete口ptr;

new对数组的支持体现在它会分别调用构造函数函数初始化每一个数组元素,释放对象时为每个对象调用

析构函数。注意delete□要与new口配套使用,不然会找出数组对象部分释放的现象,造成内存泄漏。

至于malloc,它并知道你在这块内存上要放的数组还是啥别的东西,反正它就给你一块原始的内存,

在给你个内存的地址就完事。所以如果要动态分配一个数组的内存,还需要我们手动自定数组的大小:

int*ptr=(int*)malloc(sizeof(int)*10);〃分配一个10个int元素的数组

g.new与malloc是否可以相互调用

operatornew/operatordelete的实现可以基于malloc,而malloc的实现不可以去调用new。下面是

编写operatornew/operatordelete的一种简单方式,其他版本也与之类似:

>Users>58216>Desktop>Untitled-1.c

void.operatornew(sieze_tsize)

2{一

if(void*mem=malloc(size)

returnmem;

else

throwbad_alloc();

7}~

voidoperatordelete(void*mem)noexcept

9{

free(mem);

11}

h.是否可以被重载

opeartornew/operatordelete可以被重载。标准库是定义了operatornew函数和operatordelete函

数的8个重载版本:

〃这些版本可能抛出异常

void*operatornew(size_t);

void*operatornew[](size_t);

void*operatordelete(void*)noexcept;

void*operatordelete[](void*0)noexcept;

〃这些版本承诺不抛出异常

void*operatornew(size_t,nothrow_t&)noexcept;

void*operatornew[](size_t,nothrow_t&);

void*operatordelete(void*,nothrow_t&)noexcept;

void*operatordelete[](void*0,nothrow_t&)noexcept;

我们可以自定义上面函数版本中的任意一个,前提是自定义版本必须位于全局作用域或者类作用域中。

总之,我们知道我们有足够的自由去重载operatornew/operatordelete,以决定我们的new与delete如何

为对象分配内存,如何回收对象°而malloc/f「ee并不允许重载。

i.能够直观地重新分配内存

使用malloc分配的内存后,如果在使用过程中发现内存不足,可以使用realloc函数进行内存重新分

配实现内存的扩充。realloc先判断当前的指针所指内存是否有足够的连续空间,如果有,原地扩大可分配

的内存地址,并且返回原来的地址指针;如果空间不够,先按照新指定的大小分配空间,将原有数据从头

到尾拷贝到新分配的内存区域,而后释放原来的内存区域。

new没有这样直观的配套设施来扩充内存。

9.extern”C”的作用?

用法:

1)C++调用一个C语言编写的.so库时,包含描述.so库中函数的头文件时,应该将对应的头文件放

置在extern“C”{■■■}格式的{一}中。

2)C中引用C++中的全局函数时,C++的头文件需要加extern"C",而C文件中不能用extern"C”,

只能使用extern关键字。

extern”C”的主要作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern“C”后,会指

示编译器这部分代码按C语言的进行编译,而不是C++的。由于C++支持函数重载,因此编译器编译函

数的过程中会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名;而C语言并不支持函数重载,

因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

这个功能十分有用处,因为在C++出现以前,很多代码都是C语言写的,而且很底层的库也是C语

言写的,为了更好的支持原来的C代码和已经写好的C语言库,需要在C++中尽可能的支持C,而extern

“C”就是其中的一个策略。

10.C语言内存分配方式?

•从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存

在。例如全局变量,static变量。

•在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些

存储单元自动被释放。

•从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序

员自己负责在何时用free或delete释放内存。动态内存的生存期由程序员决定,使用非常灵活,

但如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,频繁地分配和释放

不同大小的堆空间将会产生堆内碎块。

11.重新实现memcpy()函数?

标准的memcpyO函数:

void*memcpy(void*dst,constvoid*src,size_tn);

//Ifcopyingtakesplacebetweenobjectsthatoverlap,thebehaviorisundefined.

可见,如果发生源地址和目标地址有重叠的情况,函数行为将是未定义的。

考虑到该情况,标准库提供了有内存重叠时的内存拷贝函数memmoveO,但是该函数是把源字符

串拷贝到临时buf中,再从临时buf写到目的地址,多了一次不必要的开销,所以效率不高。所以,重写该

函数的时候最好考虑到内存重叠的情况:

r)Memq)y(void*,constvoid*,size_t)

void*Memcpy(void*dst,constvoid*src,size_tLength)

char*pdst;

char*psrc;

if(dst==null||src==null)

{

returnnull;

)

10如果有地址重叠,采用自后向前的拷贝方式

if((src<dst)&&(char*)src+Length>(char*)dst)

{

psrc=(char*)src+Length-1;

pdst=(char*)dst+Length-1;

while(Length--)

{

*pdst--=*psrc--;

}

}

else

{

psrc=(char*)src;

pdst=(char*)dst;

while(Length--)

(

*pdst++=*psrc++;

}

)

returndst;

}

12.指针与引用的区别?

指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。

区别:

•指针是一个实体,而引用仅是个别名

•指针和弓I用的自增(++)运算意义不一样,指针是对内存地址的自增,引用是对值的自

增;

•引用使用时无需解引用(*),指针需要解引用;

•引用只能在定义时被初始化一次,之后不可变;指针可变;

•引用不能为空,指针可以为空;

•"sizeof引用”得到的是所指向的变量(对象)的大小,而“sizeof指针”得到的是指针本身(地址)大

小。

•从内存分配上看:程序为指针变量分配内存区域,而引用不需要分配内存区域。

13.C++中四个与类型转换相关的关键字?

static_cast:

特点:静态转换,在编译处理期间。

应用场合:主要用于C++中内置的基本数据类型之间的转换,但是没有运行时类型的检测

来保证转换的安全性。

用于基类和子类之间的指针或引用之间的转换,这种转换把子类的指针或引用转换为基类表示是安全

的;进行下行转换,把基类的指针或引用转换为子类表示时,由于没有进行动态类型检测,所以是不安

全的。上行安全下行不安全

把void类型的指针转换成目标类型的指针(不安全)。

不能用于两个不相关的类型转换。

不能把const对象转换成非const对象。

const_cast:

特点:去常转换,编译时执行。不是运行时执行

应用场合:const_cast操作不能在不同的种类间转换。相反,它仅仅把它作用的表达式转换成常量。它可

以使一个本来不是const类型的数据转换成const类型的,或者把const属性去掉。

去掉const属性:const_case<int*>(&num),常用,因为不能把一个const变量直接赋给一个非const变

量,必须要转换。

intmair)()

{

constintconstant=26;

constint*const_p=&constant;

int*modifier=const_cast<int*>(const_p);

*modifier=3;

cout<<"constant:"<<constant<<endl;//26

cout<<"modifier:"<<*modifier<<endl;//3

return0;

)

I一

加上const属性:constint*k=const_case<constint*>(j),一般很少用,因为可以把一个非const变量直

接赋给一个const变量,比如:constint*k=j;

const_case只能转换指针或引用不能转换变量

constinti=3;

intj=const_cast<int>(i);是不行的

reinterpret_cast:

特点:重解释类型转换

应用场合:它有着和c风格强制类型转换同样的功能;它可以转化任何的内置数据类型为

其他的类型,同时它也可以把任何类型的指针转化为其他的类型,最好不要用。

dynamic_cast:

dynamic_cast<type-id>(expression)

•该运算符将expression转换成type_id类型的对象。type_id必须是类的指针,类的引用或者空类

型的指针。

•如果type_id是一个指针类型,那么expression也必须是一个指针类型,如果type_id是一个引用

类型,那么expression也必须是一个引用类型。

•如果type_id是一个空类型的指针,在运行的时候,就会检测expression的实际类型,结果是一个

由expression决定的指针类型。

•其它三种都是编译时完成的,dynamijcast是运行时处理的,运行要进程类型检查。

•将一个指向基类的指针转换成指向派生类的指针,如果转换失败,返回NULL。不能用于内置的基本

数据类型的强制换。

Users>58216>Desktop>Untitled-l.c

classCBasic

public:

CBasic(){};

~CBasic(){};

virtualvoidspeak()

{//要有virtual才能实现多态,才能使用dynamic

cast,如果父类没有虚函数,是编译不过的

printfC'dsdfsd");

//哺乳动物类

classCDerived:publicCBasic

{

public:

cDerived(){};

~cDerived(){};

};

intmain()

CBasiccBasic;

CDerivedCDerived;

CBasic*pBl=newCBasic;

CBasic*pB2=newCDerived;

//dynamiccastfailed)sopDlisnuLL.

//pBl指向对象和括号里的Dertved.不一样,转换失败

CDerived*pDl=dynamic_cast<CDerived*>(pBl);

//dynamiccastsucceeded,sopD2pointstoCDerivedobject

//dynamiccast用于将指向子类的父类指针或引用,转换为子类指针或引用,

//pB2指向对象和括号里的Derived"一样,转换成功

CDerived*pD2=dynamic_cast<CDerived*>(pB2);

//dynamcicastfailed,sothrowanexception.

CDerived&rDl=dynamic_cast<CDerived&>(*pBl);

//dynamiccastsucceeded,sorD2referencestoCDerivedobject.

CDerived&rD2=dynamic_cast<CDerived&>(*pB2);

return0:

14.C语言画圆?

方法1:一般而言,半角文字的长宽比为1:2。现在画一个范围内的圆。如果该点落于

圆中,则用“=”符号填充,否则用空格代替。理论上来说,只要有相对应的图像方程,这段代码也可以进

行相应的修改从而输出对应图像。

#include<stdio.h>

intmain()

I

for(doublei=1;i>-1;i-=0.08,printf(,,\n11))

for(doublej=1;j>-1;j-=0.04)

printf((i*i+j*j<=1)?"H);

getchar();

return0;

)

15.explicit关键字?

用于防止类构造函数的隐式自动转换。跟它相对应的另一个关键字是implicit,意思是隐藏的,类构造

函数默认情况下即声明为implicit(隐式).

1.classCxString//没有使用❷xplicit关键字的类声明,即默认为做式声明

2.

3.public:

4.char*_pstr;

5.int_size;

6.CxString(intsize)

7.(

8._size=size;//string的预设大小

9._pstr=malloc(size+1);//分配string的内存

10.memset(_pstr,0,size+1);

11.)

12.CxString(constchar*p)

13.(

14.intsize=strlen(p);

15._pstr=malloc(size+1);//分配string的内存

16.strcpy(_pstr,p);//复制字符串

17._size=strlen(_pstr);

18.}

19.//析构函数这里不讨论,省略・・・

20.};

21.

22.//F面是调用:

23.

24.CxStringstringl(24);//这样是OK的,MZxString预分配24字仙的大小的内存

25.CxStringstring2=10;//这样是OK的,为CxString预分配10字「的大小的内存

26.CxStringstrings;//这样是不行的,因为没有默认构造函数,错误

为:“CxString":没有合适的默认构造函数可用

27.CxStringstring4("aaaan);//这样是OK的

28.CxStringstrings="bbb";//这样也是OK的,调用的是CxString(constchar*p)

29.CxStringstring6='c';//这样也是OK的,其实调用的是CxString(intsize),I[size

等「'L的ascii码

30.stringl=2;//这样也是OK的,为CxString预分配2字节的大小的内存

31.string2=3;//这样也是0K的,为CxString预分配3字节的大小的内存

32.strings=stringl;//这样也是0K的,至少编译是没问题的,但是如果析构函数里用

free样放_pstr内存指针的时候可能会报错,完整的代码必须di载运管符并在凡中处理内存释

上面的代码中,”CxStringstring2

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论