东软C++笔试

更新时间:2024-05-02 05:21:01 阅读量: 综合文库 文档下载

说明:文章内容仅供预览,部分内容可能不全。下载后的文档,内容与下面显示的完全一致。下载之前请确认下面内容是否您想要的,是否完整无缺。

C++笔试题目及部分参考答案-东软集团 . 找错 试题1: Code

Void test1() {

char string[10];

char* str1=\strcpy(string, str1); }

试题2: Code

Void test2() {

char string[10], str1[10]; for(I=0; I<10;I++) {

str1[i] ='a'; }

strcpy(string, str1); }

试题3: Code

Void test3(char* str1) {

char string[10];

if(strlen(str1) <= 10) {

strcpy(string, str1); } }

解答:

test1: 字符串str1需要11个字节才能存放下(包括末尾的'\\0'),而string只有10个字节的空间,strcpy会导致数组越界

test2: 如果面试者指出字符数组str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得从str1内存起复制到string内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数strcpy工作方式的给10分

test3: if(strlen(str1) <= 10)应改为if(strlen(str1) < 10),因为strlen的结果未统计'\\0'所占用的1个字节 剖析:

考查对基本功的掌握:

(1)字符串以'\\0'结尾;

(2)对数组越界把握的敏感度;

(3)库函数strcpy的工作方式,如果编写一个标准strcpy函数的总分值为10,下面给出几个不同得分的答案: 2分

void strcpy( char *strDest, char *strSrc ) {

while( (*strDest++ = * strSrc++) != '\\0' ); } 4分

void strcpy( char *strDest, const char *strSrc )

//将源字符串加const,表明其为输入参数,加2分 {

while( (*strDest++ = * strSrc++) != '\\0' ); } 7分

void strcpy(char *strDest, const char *strSrc) {

//对源地址和目的地址加非0断言,加3分 assert( (strDest != NULL) && (strSrc != NULL) ); while( (*strDest++ = * strSrc++) != '\\0' ); } 10分

//为了实现链式操作,将目的地址返回,加3分! Code

char * strcpy( char *strDest, const char *strSrc ) {

assert( (strDest != NULL) && (strSrc != NULL) ); char *address = strDest;

while( (*strDest++ = * strSrc++) != '\\0' ); return address; }

从2分到10分的几个答案我们可以清楚的看到,小小的strcpy竟然暗藏着这么多玄机,真不是盖的!需要多么扎实的基本功才能写一个完美的strcpy啊! (4)对strlen的掌握,它没有包括字符串末尾的'\\0'。

读者看了不同分值的strcpy版本,应该也可以写出一个10分的strlen函数了,完美的版本为: Code

int strlen( const char *str ) //输入参数const {

assert( strt != NULL ); //断言字符串地址非0 int len;

while( (*str++) != '\\0' )

{

len++; }

return len; }

试题4: Code

void GetMemory( char *p ) {

p = (char *) malloc( 100 ); }

void Test( void ) {

char *str = NULL; GetMemory( str );

strcpy( str, \printf( str ); }

试题5: Code

char *GetMemory( void ) {

char p[] = \ return p; }

void Test( void ) {

char *str = NULL; str = GetMemory(); printf( str ); }

试题6: Code

void GetMemory( char **p, int num ) {

*p = (char *) malloc( num ); }

void Test( void ) {

char *str = NULL;

GetMemory( &str, 100 );

strcpy( str, \printf( str ); }

试题7: Code

void Test( void ) {

char *str = (char *) malloc( 100 ); strcpy( str, \free( str );

//省略的其它语句 }

解答:

试题4传入中GetMemory( char *p )函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完char *str = NULL; GetMemory( str ); 后的str仍然为NULL;

试题5中 char p[] = \的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。 试题6的GetMemory避免了试题4的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句

*p = (char *) malloc( num );后未判断内存是否申请成功,应加上: if ( *p == NULL ) {

...//进行申请内存失败处理 }

试题7存在与试题6同样的问题,在执行char *str = (char *) malloc(100); 后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:str = NULL; 试题6的Test函数中也未对malloc的内存进行释放。 剖析:

试题4~7考查面试者对内存操作的理解程度,基本功扎实的面试者一般都能正确的回答其中50~60的错误。但是要完全解答正确,却也绝非易事。 对内存操作的考查主要集中在: (1) 指针的理解;

(2) 变量的生存期及作用范围;

(3) 良好的动态内存申请和释放习惯。 再看看下面的一段程序有什么错误: Code

swap( int* p1,int* p2 ) {

int *p; *p = *p1; *p1 = *p2;

*p2 = *p; }

在swap函数中,p是一个“野”指针,有可能指向系统区,导致程序运行的崩溃。在VC++中DEBUG运行时提示错误“Access Violation”。 野指针,也就是指向不可用内存区域的指针。通常对这种指针进行操作的话,将会使程序发生不可预知的错误。

“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。野指针的成因主要有两种:

一、指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

二、指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。别看free和delete的名字恶狠狠的(尤其是delete),它们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。通常会用语句if (p != NULL)进行防错处理。很遗憾,此时if语句起不到防错作用,因为即便p不是NULL指针,它也不指向合法的内存块。例:

char *p = (char *) malloc(100);

strcpy(p, “hello”);

free(p); // p 所指的内存被释放,但是p所指的地址仍然不变

if(p != NULL) // 没有起到防错作用

strcpy(p, “world”); // 出错

另外一个要注意的问题:不要返回指向栈内存的指针或引用,因为栈内存在函数结束时会被释放。

该程序应该改为: Code

swap( int* p1,int* p2 ) {

int p; p = *p1; *p1 = *p2; *p2 = p; }

3. 内功题

试题1:分别给出BOOL,int,float,指针变量 与“零值”比较的 if 语句(假设变量名为var) 解答:

BOOL型变量:if(!var) int型变量: if(var==0)

float型变量:const float EPSINON = 0.00001; if ((x >= - EPSINON) && (x <= EPSINON)

指针变量: if(var==NULL) 剖析:

考查对0值判断的“内功”,BOOL型变量的0判断完全可以写成if(var==0),而int型变量也可以写成if(!var),指针变量的判断也可以写成if(!var),上述写法虽然程序都能正确运行,但是未能清晰地表达程序的意思。

一般的,如果想让if判断一个变量的“真”、“假”,应直接使用if(var)、if(!var),表明其为“逻辑”判断;如果用if判断一个数值型变 量(short、int、long等),应该用if(var==0),表明是与0进行“数值”上的比较;而判断指针则适宜用if(var==NULL), 这是一种很好的编程习惯。

浮点型变量并不精确,所以不可将float变量用“==”或“!=”与数字比较,应该设法转化成“>=”或“<=”形式。如果写成if (x == 0.0),则判为错,得0分。

试题2:以下为Windows NT下的32位C++程序,请计算sizeof的值 void Func ( char str[100] ) {

sizeof( str ) = ? }

void *p = malloc( 100 ); sizeof ( p ) = ? 解答:

sizeof( str ) = 4 sizeof ( p ) = 4 剖析:

Func ( char str[100] )函数中数组名作为函数形参时,在函数体内,数组名失去了本身的内涵,仅仅只是一个指针;在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。 数组名的本质如下:

(1) 数组名指代一种数据结构,这种数据结构就是数组; 例如: char str[10];

cout << sizeof(str) << endl;

输出结果为10,str指代数据结构char[10]。

(2) 数组名可以转换为指向其指代实体的指针,而且是一个指针常量,不能作自增、自减等操作,不能被修改; char str[10];

str++; //编译出错,提示str不是左值

(3) 数组名作为函数形参时,沦为普通指针。 Windows NT 32位平台下,指针的长度(占用内存的大小)为4字节,故sizeof( str ) 、sizeof ( p ) 都为4。

试题3:写一个“标准”宏MIN,这个宏输入两个参数并返回较小的一个。另外,当你写下面的代码时会发生什么事? least = MIN(*p++, b); 解答:

#define MIN(A,B) ((A) <= (B) ? (A) : (B)) MIN(*p++, b)会产生宏的副作用 剖析:

这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。程序员对宏定义的使用要非常小心,特别要注意两个问题:

(1) 谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答: #define MIN(A,B) (A) <= (B) ? (A) : (B)

#define MIN(A,B) (A <= B ? A : B ) 都应判0分; (2) 防止宏的副作用。

宏定义#define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:((*p++) <= (b) ? (*p++) : (*p++)) 这个表达式会产生副作用,指针p会作三次++自增操作。除此之外,另一个应该判0分的解答是:#define MIN(A,B) ((A) <= (B) ? (A) : (B)); 这个解答在宏定义的后面加“;”,显示编写者对宏的概念模糊不清,只能被无情地判0分并被面试官淘汰。

宏的一些副作用 1、优先级问题

1) 传入变量优先级

#define MULTI(a,b) a * b

MULTI(1+2,3) => 1 + 2 * 3 其实是想要(1 + 2) * 3

2) 作为值返回时,类似1)

#define ADD(a,b) (a) + (b)

int c = ADD(a,b) * 3; => (a) + (b) * 3 其实是想要(a + b) * 3

所以,一般的规则是:宏里面参数全部用括号括起来;如果作为值返回,整个表达式也用括号括起来。

所以,上面最好这么写:

#define MULTI(a,b) ((a) * (b))

#define ADD(a,b) ((a) + (b))

2、实际使用参数和宏内部变量同名

#define HASH(str,sz,rst) do{unsigned int n = 0; n = xxx; rst = n % sz;}while(0)

这是一个hash的宏实现,其中定义了一个临时变量n,根据str计算n,然后对sz求模并把返回值赋给传进来的rst.

这么调用:

int n;

HASH(\

不会达到改变n的效果,因为实际使用参数n和宏内部的变量n同名。宏扩展中最后一条语句是:n = n % sz;因为宏内部n有更小作 用域,实际赋值的是宏内部的那个临时变量n。外面调用的n不会有任何改变。

这个副作用有些隐蔽,一般的规则是:宏内部变量使用一种不同风格的命名方式。

比如:

#define HASH(str,sz,rst) do{unsigned int __n = 0; __n = ...

3、++,--

#define MAX(a,b) ((a) > (b) ? (a) : (b))

int a = 3,b = 2;

int c = MAX(a++,b);

执行看看,不但a的值不是和想要的一致,返回值c也会让你大吃一惊,哈哈。(a = 5,c = 4)

在宏内部一个变量\执行\多少次,它就自增或自减了多少次。

所以一般使用宏最好不要传入自增自减。如果你一定要在宏里消除这个副作用,可以这样:

#define MAX(a,b) ({int __x = (a), __y = (b);(__x > __y) ? __x : __y;})

也就是:保证传入宏的参数在内部只使用一次。(注意:传入a++或++a都能得到各自正确

的效果)

这里的内部变量__x,__y是不需要用括号包起来的,原因可以自己想想。

另外对宏中括号的使用补充说明两点:

因为宏中定义了临时变量,所以要用{}括起来;

因为要返回值,所以外面还要用()括起来({}不返回值);

另外,这里还有一个问题:实际中a,b不一定是int的,这个宏中的临时变量声明为int,不通用。

改进:

#define MAX(a,b,type) ({type __x = (a), __y = (b);(__x > __y) ? __x : __y;})

使用:

MAX(1,2,int); MAX(1.1,1.2,double);

是不是感觉怪怪的,有点c++的感觉~~ 这样的使用太复杂了,而且也会给代码的阅读带来难度。

我觉得好的态度是多了解些宏的可能的副作用,在实际编码中遵守第1、2条规则,不要往宏中传入自增自减的东西,就够了。不要把过多的复杂度全扔给宏,\通用\也不能盲目,因为毕竟:yy是没有极限的。

试题4:为什么标准头文件都有类似以下的结构? Code

#ifndef __INCvxWorksh #define __INCvxWorksh #ifdef __cplusplus extern \#endif /**/

#ifdef __cplusplus }

#endif

#endif /* __INCvxWorksh */

解答:

头文件中的编译宏

#ifndef __INCvxWorksh #define __INCvxWorksh #endif

的作用是防止被重复引用。

作为一种面向对象的语言,C++支持函数重载,而过程式语言C则不支持。函数被C++编译后在symbol库中的名字与C语言的不同。例如,假设某个函数的原型为: void foo(int x, int y);

该函数被C编译器编译后在symbol库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。_foo_int_int这样的名字包含了函数名和函数参数数量及类型信息,C++就是考这种机制来实现函数重载的。

为了实现C和C++的混合编程,C++提供了C连接交换指定符号extern \来解决名字匹配问题,函数声明前加上extern \后,则编译器就会按照C语言的方式将该函数编译为_foo,这样C语言中就可以调用C++的函数了。

试题5:编写一个函数,作用是把一个char组成的字符串循环右移n个。比如原来是“abcdefghi”如果n=2,移位后应该是“hiabcdefgh” 函数头是这样的:

//pStr是指向以'\\0'结尾的字符串的指针 //steps是要求移动的n

void LoopMove ( char * pStr, int steps ) {

//请填充... }

解答:

正确解答1: Code

void LoopMove ( char *pStr, int steps ) {

int n = strlen( pStr ) - steps; char tmp[MAX_LEN]; strcpy ( tmp, pStr + n ); strcpy ( tmp + steps, pStr); *( tmp + strlen ( pStr ) ) = '\\0'; strcpy( pStr, tmp ); }

正确解答2: Code

void LoopMove ( char *pStr, int steps ) {

int n = strlen( pStr ) - steps; char tmp[MAX_LEN];

memcpy( tmp, pStr + n, steps ); memcpy(pStr + steps, pStr, n );

memcpy(pStr, tmp, steps ); }

剖析:

这个试题主要考查面试者对标准库函数的熟练程度,在需要的时候引用库函数可以很大程度上简化程序编写的工作量。 最频繁被使用的库函数包括: (1) strcpy (2) memcpy (3) memset

memcpy

原型:extern void *memcpy(void *dest, void *src, unsigned int count);

用法:#include

功能:由src所指内存区域复制count个字节到dest所指内存区域。 说明:src和dest所指内存区域不能重叠,函数返回指向dest的指针。 注意:与strcpy相比,memcpy并不是遇到'\\0'就结束,而是一定会拷贝完n个字节。 举例:

// memcpy.c

#include #include

int main(int argc, char* argv[]) {

char *s=\ char d[20]; clrscr();

memcpy(d,s,strlen(s)); d[strlen(s)]='\\0'; printf(\ getchar(); return 0; }

截取view

#include

int main(int argc, char* argv[]) {

char *s=\ char d[20];

memcpy(d,s+14,4);

//memcpy(d,s+14*sizeof(char),4*sizeof(char));也可 d[4]='\\0';

printf(\getchar(); return 0; }

输出结果: View

初始化数组 char msg[10];

memcpy(msg,0,sizeof(msg));

memset

函数原型 void *memset(void *s, int ch, unsigned n); 编辑本段 程序例 #include #include #include

memset函数

int main(void) {

char buffer[] = \

printf(\ memset(buffer, '*', strlen(buffer) );

printf(\ return 0; }

输出结果:

Buffer before memset: Hello world Buffer after memset: *********** 编译平台:

Microsoft Visual C++ 6.0

也不一定就是把内容全部设置为ch指定的ASCII值,而且该处的ch可为int或者其他类型,并不一定要是char类型。例如下面这样:

int array[5] = {1,4,3,5,2}; for(int i = 0; i < 5; i++) cout<

memset(array,0,5*sizeof(int)); for(int k = 0; k < 5; k++) cout<

输出的结果就是: 1 4 3 5 2 0 0 0 0 0

后面的表大小的参数是以字节为单位,所以,对于int或其他的就并不是都乘默认的1(字符型)了。而且不同的机器上int的大小也可能不同,所以最好用sizeof()。

要注意的是,memset是对字节进行操作, 所以上述程序如果改为

int array[5] = {1,4,3,5,2}; for(int i = 0; i < 5; i++) cout<

memset(array,1,5*sizeof(int));// 注意 这里与上面的程序不同 for(int k = 0; k < 5; k++) cout<

输出的结果就是: 1 4 3 5 2

16843009 16843009 16843009 16843009 16843009 为什么呢?

因为memset是以字节为单位就是对array指向的内存的5个字节进行赋值,每个都用ASCII为1的字符去填充,转为二进制后,1就是00000001,占一个字节。一个INT元素是4字节,合一起就是00000001000000010000000100000001,就等于16843009,就完成了对一个INT元素的赋值了。

所以用memset对非字符型数组赋初值是不可取的! 例如有一个结构体Some x,可以这样清零: memset( &x, 0, sizeof(Some) );

如果是一个结构体的数组Some x[10],可以这样: memset( x, 0, sizeof(Some)*10 );

编辑本段

memset函数详细说明

1。void *memset(void *s,int c,size_t n)

总的作用:将已开辟内存空间 s 的首 n 个字节的值设为值 c。

2。例子 main(){

char *s=\ clrscr();

memset(s,'G',6);//貌似这里有点问题// 单步运行到这里会提示内存访问冲突

printf(\ getchar(); return 0; }

【应该是没有问题的,字符串指针一样可以,并不是只读内存,可以正常运行】

3。memset() 函数常用于内存空间初始化。如: char str[100];

memset(str,0,100);

4。memset()的深刻内涵:用来对一段内存空间全部设置为某个字符,一般用在对定义的字符串进行初始化为‘memset(a, '\\0', sizeof(a)); memcpy用来做内存拷贝,你可以拿它拷贝任何数据类型的对象,可以指定拷贝的数据长度;例: char a[100], b[50];

memcpy(b, a, sizeof(b)); //注意如用sizeof(a),会造成b的内存地址溢出。

strcpy就只能拷贝字符串了,它遇到'\\0'就结束拷贝;例: char a[100], b[50]; strcpy(a,b);

如用strcpy(b,a),要注意a中的字符串长度(第一个‘\\0’之前)是否超过50位,如超过,则会造成b的内存地址溢出。 5.补充:某人的一点心得

memset可以方便的清空一个结构类型的变量或数组。 如:

struct sample_struct {

char csName[16]; int iSeq; int iType; };

对于变量

struct sample_strcut stTest; 一般情况下,清空stTest的方法: stTest.csName[0]='\\0'; stTest.iSeq=0; stTest.iType=0;

用memset就非常方便:

memset(&stTest,0,sizeof(struct sample_struct));

如果是数组:

struct sample_struct TEST[10]; 则

memset(TEST,0,sizeof(struct sample_struct)*10);

试题6:已知WAV文件格式如下表,打开一个WAV文件,以适当的数据结构组织WAV文件头并解析WAV格式的各项信息。 WAVE文件格式说明表

偏移地址 字节数 数据类型 内 容 文件头

00H 4 Char \标志 04H 4 int32 文件长度 08H 4 Char \VE\标志 0CH 4 Char \标志 10H 4 过渡字节(不定) 14H 2 int16 格式类别 16H 2 int16 通道数

18H 2 int16 采样率(每秒样本数),表示每个通道的播放速度 1CH 4 int32 波形音频数据传送速率

20H 2 int16 数据块的调整数(按字节算的) 22H 2 每样本的数据位数

24H 4 Char 数据标记符"data" 28H 4 int32 语音数据的长度 解答:

将WAV文件格式定义为结构体WAVEFORMAT: Code

typedef struct tagWaveFormat {

char cRiffFlag[4]; UIN32 nFileLen; char cWaveFlag[4]; char cFmtFlag[4]; char cTransition[4]; UIN16 nFormatTag ; UIN16 nChannels;

UIN16 nSamplesPerSec; UIN32 nAvgBytesperSec; UIN16 nBlockAlign;

UIN16 nBitNumPerSample; char cDataFlag[4]; UIN16 nAudioLength; } WAVEFORMAT;

试题7:编写类String的构造函数、析构函数和赋值函数,已知类String的原型为: Code

#include class String{ public:

String(const char *str = NULL);//默认构造函数 String(const String &str);//复制构造函数 ~String();//析构函数

String operator+(const String & str);//字符串连接 String & operator=(const String &str);//字符串赋值

bool operator==(const String &str);//判断是否字符串相等 int Length();//获取字符串长度

friend ostream & operator<<(ostream &o,const String &str);//重载输出 String SubStr(int start, int end);//求子字符串(start ... end-1) private:

char * charArray; };

String::String(const char *str) {

if(str == NULL){

charArray=new char[1]; charArray[0]='\\0'; }else{

charArray=new char[strlen(str)+1]; strcpy(charArray,str); } }

String::String(const String &str) {

charArray = new char[strlen(str.charArray)+1]; strcpy(charArray,str.charArray); }

String::~String() {

delete [] charArray; }

String String::operator+(const String &str) {

String res;

delete [] res.charArray;//释放原有空间

res.charArray = new char[strlen(charArray)+strlen(str.charArray)+1]; strcpy(res.charArray,charArray);

strcpy(res.charArray+strlen(charArray),str.charArray); return res; }

String & String::operator=(const String &str) {

if(charArray == str.charArray) return *this; delete [] charArray;

charArray = new char[strlen(str.charArray)+1]; strcpy(charArray,str.charArray); return *this; }

bool String::operator==(const String &str) {

return strcmp(charArray,str.charArray) == 0;

}

int String::Length() {

return strlen(charArray); }

ostream & operator<<(ostream &o, const String &str) {

o<

String String::SubStr(int start, int end) {

String res;

delete [] res.charArray;//释放原有内存

res.charArray = new char[end-start+1];//重新申请内存 for(int i=0; i+start

res.charArray[i]=charArray[start+i]; }

res.charArray[end-start] = '\\0'; return res; }

void main() {

String s = \ String t = \

cout<

String s2 = \ String s3 = \ cout<

cout<<(s2+s3).SubStr(0,s2.Length()+s3.Length())<

能够准确无误地编写出String类的构造函数、拷贝构造函数、赋值函数和析构函数的面试者至少已经具备了C++基本功的60%以上!

在这个类中包括了指针类成员变量m_data,当类中包括指针类成员变量时,一定要重载其拷贝构造函数、赋值函数和析构函数,这既是对C++程序员的基本要求,也是《Effective C++》中特别强调的条款。仔细学习这个类,特别注意加注释的得分点和加分点的意义,这样就具备了60%以上的C++基本功!

试题8:请说出static和const关键字尽可能多的作用 解答:

static关键字至少有下列n个作用:

(1) 函数体内static变量的作用范围为该函数体,不同于auto变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;

(2) 在模块内的static全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问; (3) 在模块内的static函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;

(4) 在类中的static成员变量属于整个类所拥有,对类的所有对象只有一份拷贝;

(5) 在类中的static成员函数属于整个类所拥有,这个函数不接收this指针,因而只能访问类的static成员变量。

const关键字至少有下列n个作用:

(1) 欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了

(2) 对指针来说,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const;

(3) 在一个函数声明中,const可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;

(4) 对于类的成员函数,若指定其为const类型,则表明其是一个常函数,不能修改类的成员变量;

(5) 对于类的成员函数,有时候必须指定其返回值为const类型,以使得其返回值不为“左值”。例如:

const classA operator*(const classA& a1,const classA& a2);

operator*的返回结果必须是一个const对象。如果不是,这样的变态代码也不会编译出错: classA a, b, c;

(a * b) = c; // 对a*b的结果赋值

操作(a * b) = c显然不符合编程者的初衷,也没有任何意义。

东软经典笔试及参考答案

1.static有什么用途?(请至少说明两种)

1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。 2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用 2.引用与指针有什么区别?

1) 引用必须被初始化,指针不必。

2) 引用初始化以后不能被改变,指针可以改变所指的对象。 3) 不存在指向空值的引用,但是存在指向空值的指针。 3.描述实时系统的基本特性

在特定时间内完成特定的任务,实时性与可靠性。

4.全局变量和局部变量在内存中是否有区别?如果有,是什么区别? 全局变量储存在静态数据库,局部变量在堆栈。 5.什么是平衡二叉树?

左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1。 6.堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源。

7.什么函数不能声明为虚函数? constructor函数不能声明为虚函数。 8.冒泡排序算法的时间复杂度是什么? 时间复杂度是O(n^2)。

9.写出float x 与“零值”比较的if语句。 if(x>0.000001&&x<-0.000001)

10.Internet采用哪种网络协议?该协议的主要层次结构? Tcp/Ip协议

主要层次结构为: 应用层/传输层/网络层/数据链路层/物理层。

11.Internet物理地址和IP地址转换采用什么协议? ARP (Address Resolution Protocol)(地址解析協議) 12.IP地址的编码分为哪俩部分?

IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与上之后才能区分哪些是网络位哪些是主机位。

13.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。

循环链表,用取余操作做

14.不能做switch()的参数类型是: switch的参数不能为实型。

1.写出判断ABCD四个表达式的是否正确, 若正确, 写出经过表达式中 a的值(3分) int a = 4;

(A)a += (a++); (B) a += (++a) ;(C) (a++) += a;(D) (++a) += (a++); a = ?

答:C错误,左侧不是一个有效变量,不能赋值,可改为(++a) += a; 改后答案依次为9,10,10,11

2.某32位系统下, C++程序,请计算sizeof 的值(5分). char str[] = “http://www.ibegroup.com/” char *p = str ; int n = 10; 请计算

sizeof (str ) = ?(1) sizeof ( p ) = ?(2) sizeof ( n ) = ?(3) void Foo ( char str[100]){ 请计算

sizeof( str ) = ?(4) }

void *p = malloc( 100 ); 请计算

sizeof ( p ) = ?(5) 答:(1)17 (2)4 (3) 4 (4)4 (5)4 3. 回答下面的问题. (4分)

(1).头文件中的 ifndef/define/endif 干什么用?预处理 答:防止头文件被重复引用

(2). #i nclude 和 #i nclude “filename.h” 有什么区别?

答:前者用来包含开发环境提供的库头文件,后者用来包含自己编写的头文件。 (3).在C++ 程序中调用被 C 编译器编译后的函数,为什么要加 extern “C”声明? 答:函数和变量被C++编译后在符号库中的名字与C语言的不同,被extern \修饰的变 量和函数是按照C语言方式编译和连接的。由于编译后的名字不同,C++程序不能直接调 用C 函数。C++提供了一个C 连接交换指定符号extern“C”来解决这个问题。 (4). switch()中不允许的数据类型是? 答:实型

4. 回答下面的问题(6分)

(1).Void GetMemory(char **p, int num){ *p = (char *)malloc(num); }

void Test(void){ char *str = NULL;

GetMemory(&str, 100); strcpy(str, \printf(str); }

请问运行Test 函数会有什么样的结果? 答:输出“hello” (2). void Test(void){

char *str = (char *) malloc(100); strcpy(str, “hello”); free(str);

if(str != NULL){ strcpy(str, “world”); printf(str); } }

请问运行Test 函数会有什么样的结果? 答:输出“world”

(3). char *GetMemory(void){ char p[] = \return p; }

void Test(void){ char *str = NULL; str = GetMemory(); printf(str); }

请问运行Test 函数会有什么样的结果? 答:无效的指针,输出不确定 5. 编写strcat函数(6分)

已知strcat函数的原型是char *strcat (char *strDest, const char *strSrc); 其中strDest 是目的字符串,strSrc 是源字符串。

(1)不调用C++/C 的字符串库函数,请编写函数 strcat 答:

VC源码:

char * __cdecl strcat (char * dst, const char * src) {

char * cp = dst; while( *cp )

cp++; /* find end of dst */

while( *cp++ = *src++ ) ; /* Copy src to end of dst */ return( dst ); /* return dst */ }

(2)strcat能把strSrc 的内容连接到strDest,为什么还要char * 类型的返回值? 答:方便赋值给其他变量

6.MFC中CString是类型安全类么?

答:不是,其它数据类型转换到CString可以使用CString的成员函数Format来转换 7.C++中为什么用模板类。

答:(1)可用来创建动态增长和减小的数据结构

(2)它是类型无关的,因此具有很高的可复用性。

(3)它在编译时而不是运行时检查数据类型,保证了类型安全 (4)它是平台无关的,可移植性 (5)可用于基本数据类型 8.CSingleLock是干什么的。

答:同步多个线程对一个数据类的同时访问 9.NEWTEXTMETRIC 是什么。

答:物理字体结构,用来设置字体的高宽大小

10.程序什么时候应该使用线程,什么时候单线程效率高。 答:1.耗时的操作使用线程,提高应用程序响应

2.并行操作时使用线程,如C/S架构的服务器端并发线程响应用户的请求。 3.多CPU系统中,使用线程提高CPU利用率

4.改善程序结构。一个既长又复杂的进程可以考虑分为多个线程,成为几个独立或半独 立的运行部分,这样的程序会利于理解和修改。 其他情况都使用单线程。

11.Windows是内核级线程么。 答:见下一题

12.Linux有内核级线程么。

答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两 种类型:“用户级线程”和“内核级线程”。 用户线程指不需要内核支持而在用户程序 中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度 和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现 ,但线程的调度需要用户程序完成,这有些类似 Windows 3.x 的协作式多任务。另外一 种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部 需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支 ,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线 程因 I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不 到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占 用了更多的系统开支。

Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程

13.C++中什么数据分配在栈或堆中,New分配数据是在近堆还是远堆中?

答:栈: 存放局部变量,函数调用参数,函数返回值,函数返回地址。由系统管理 堆: 程序运行时动态申请,new 和 malloc申请的内存就在堆上 14.使用线程是如何防止出现大的波峰。

答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提 高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队 等候。

15函数模板与类模板有什么区别?

答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化 必须由程序员在程序中显式地指定。

16一般数据库若出现日志满了,会出现什么情况,是否还能使用?

答:只能执行查询等读操作,不能执行更改,备份等写操作,原因是任何写操作都要记 录日志。也就是说基本上处于不能使用的状态。 17 SQL Server是否支持行级锁,有什么好处?

答:支持,设立封锁机制主要是为了对并发操作进行控制,对干扰进行封锁,保证数据 的一致性和准确性,行级封锁确保在用户取得被更新的行到该行进行更新这段时间内不 被其它用户所修改。因而行级锁即可保证数据的一致性又能提高数据操作的迸发性。 18如果数据库满了会出现什么情况,是否还能使用? 答:见16

19 关于内存对齐的问题以及sizof()的输出

答:编译器自动对齐的原因:为了提高程序的性能,数据结构(尤其是栈)应该尽可能 地在自然边界上对齐。原因在于,为了访问未对齐的内存,处理器需要作两次内存访问 ;然而,对齐的内存访问仅需要一次访问。 20 int i=10, j=10, k=3; k*=i+j; k最后的值是?

答:60,此题考察优先级,实际写成: k*=(i+j);,赋值运算符优先级最低 21.对数据库的一张表进行操作,同时要对另一张表进行操作,如何实现? 答:将操作多个表的操作放入到事务中进行处理 22.TCP/IP 建立连接的过程?(3-way shake)

答:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。 第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状 态,等待服务器确认;

第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个 SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态; 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1) ,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。 23.ICMP是什么协议,处于哪一层?

答:Internet控制报文协议,处于网络层(IP层) 24.触发器怎么工作的?

答:触发器主要是通过事件进行触发而被执行的,当对某一表进行诸如UPDATE、 INSERT 、 DELETE 这些操作时,数据库就会自动执行触发器所定义的SQL 语句,从而确保对数 据的处理必须符合由这些SQL 语句所定义的规则。 25.winsock建立连接的主要实现步骤?

答:服务器端:socker()建立套接字,绑定(bind)并监听(listen),用accept() 等待客户端连接。

客户端:socker()建立套接字,连接(connect)服务器,连接上后使用send()和recv( ),在套接字上写读数据,直至数据交换完毕,closesocket()关闭套接字。

服务器端:accept()发现有客户端连接,建立一个新的套接字,自身重新开始等待连 接。该新产生的套接字使用send()和recv()写读数据,直至数据交换完毕,closesock

et()关闭套接字。

26.动态连接库的两种方式?

答:调用一个DLL中的函数有两种方法:

1.载入时动态链接(load-time dynamic linking),模块非常明确调用某个导出函数

,使得他们就像本地函数一样。这需要链接时链接那些函数所在DLL的导入库,导入库向 系统提供了载入DLL时所需的信息及DLL函数定位。 2.运行时动态链接(run-time dynamic linking),运行时可以通过LoadLibrary或Loa

dLibraryEx函数载入DLL。DLL载入后,模块可以通过调用GetProcAddress获取DLL函数的

出口地址,然后就可以通过返回的函数指针调用DLL函数了。如此即可避免导入库文件了 。

27.IP组播有那些好处?

答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧 消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的数据包 到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无 论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播 技术的核心就是针对如何节约网络资源的前提下保证服务质量。

19. 如何引用一个已经定义过的全局变量? 答:extern

可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。

20. 全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么? 答:可以,在不同的C文件中以static形式来声明同名全局变量。

可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错。

21. 语句for( ;1 ;)有什么问题?它是什么意思? 答:无限循环,和while(1)相同。

22. do??while和while??do有什么区别?

答:前一个循环一遍再判断,后一个判断以后再循环。

23. 请写出下列代码的输出内容 #include main() {

int a,b,c,d; a=10; b=a++; c=++a;

d=10*a++;

printf(\,c,d:%d,%d,%d\,b,c,d); return 0; }

答:10,12,120

1. 以下三条输出语句分别输出什么? char str1[] = \char str2[] = \

const char str3[] = \ const char str4[] = \ const char* str5 = \const char* str6 = \

cout << boolalpha << ( str1==str2 ) << endl; // 输出什么? cout << boolalpha << ( str3==str4 ) << endl; // 输出什么? cout << boolalpha << ( str5==str6 ) << endl; // 输出什么?

答:分别输出false,false,true。str1和str2都是字符数组,每个都有其自己的存储区,它们的值则是各存储区首地址,不等;str3和str4同上,只是按const语义,它们所指向的数据区不能修改。str5和str6并非数组而是字符指针,并不分配存储区,其后的“abc”以常量形式存于静态数据区,而它们自己仅是指向该区首地址的指针,相等。

2. 以下代码中的两个sizeof用法有问题吗?

void UpperCase( char str[] ) // 将 str 中的小写字母转换成大写字母 {

for( size_t i=0; iif( 'a'<=str[i] && str[i]<='z' ) str[i] -= ('a'-'A' ); }

char str[] = \

cout << \字符长度为: \UpperCase( str ); cout << str << endl;

答:函数内的sizeof有问题。根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的str是一个静态定义的数组,因此其大小为6,函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。

3. 非C++内建型别 A 和 B,在哪几种情况下B能隐式转化为A? 答:

a. class B : public A { ??} // B公有继承自A,可以是间接继承的 b. class B { operator A( ); } // B实现了隐式转化为A的转化 c. class A { A( const B& ); } // A实现了non-explicit的参数为B(可以有其他带默认值的参数)构造函数

d. A& operator= ( const A& ); // 赋值操作,虽不是正宗的隐式类型转换,但也可以勉强算一个

4. 以下代码有什么问题? struct Test {

Test( int ) {} Test() {} void fun() {} };

void main( void ) {

Test a(1); a.fun(); Test b(); b.fun(); }

答:变量b定义出错。按默认构造函数定义对象,不需要加括号。 5. 以下代码有什么问题? cout << (true?1:\

答:三元表达式“?:”问号后面的两个操作数必须为同一类型。

6. 以下代码能够编译通过吗,为什么? unsigned int const size1 = 2; char str1[ size1 ];

unsigned int temp = 0; cin >> temp;

unsigned int const size2 = temp; char str2[ size2 ];

答:str2定义出错,size2非编译器期间常量,而数组定义要求长度必须为编译期常量。

7. 以下反向遍历array数组的方法有什么错误? vector array;

array.push_back( 1 ); array.push_back( 2 ); array.push_back( 3 );

for( vector::size_type i=array.size()-1; i>=0; --i ) // 反向遍历array数组 {

cout << array[i] << endl; }

答:首先数组定义有误,应加上类型参数:vector array。其次vector::size_type被定义为unsigned int,即无符号数,这样做为循环变量的i为0时再减1就会变成最大的整数,导致循环失去控制。

8. 以下代码中的输出语句输出0吗,为什么? struct CLS {

int m_i;

CLS( int i ) : m_i(i) {} CLS() {

CLS(0); } };

CLS obj;

cout << obj.m_i << endl;

答:不能。在默认构造函数内部再调用带参的构造函数属用户行为而非编译器行为,亦即仅执行函数调用,而不会执行其后的初始化表达式。只有在生成对象时,初始化表达式才会随相应的构造函数一起调用。

9. C++中的空类,默认产生哪些类成员函数? 答:

class Empty {

public:

Empty(); // 缺省构造函数

Empty( const Empty& ); // 拷贝构造函数 ~Empty(); // 析构函数

Empty& operator=( const Empty& ); // 赋值运算符 Empty* operator&(); // 取址运算符

const Empty* operator&() const; // 取址运算符 const };

10. 以下两条输出语句分别输出什么? float a = 1.0f;

cout << (int)a << endl; cout << (int&)a << endl;

cout << boolalpha << ( (int)a == (int&)a ) << endl; // 输出什么? float b = 0.0f;

cout << (int)b << endl; cout << (int&)b << endl;

cout << boolalpha << ( (int)b == (int&)b ) << endl; // 输出什么?

答:分别输出false和true。注意转换的应用。(int)a实际上是以浮点数a为参数构造了一个整型数,该整数的值是1,(int&)a则是告诉编译器将a当作整数看(并没有做任何实质上的转换)。因为1以整数形式存放和以浮点形式存放其内存数据是不一样的,因此两者不等。对b的两种转换意义同上,但是0的整数形式和浮点形式其内存数据是一样的,因此在这种特殊情形下,两者相等(仅仅在数值意义上)。

注意,程序的输出会显示(int&)a=1065353216,这个值是怎么来的呢?前面已经说了,1以

浮点数形式存放在内存中,按ieee754规定,其内容为0x0000803F(已考虑字节反序)。这也就是a这个变量所占据的内存单元的值。当(int&)a出现时,它相当于告诉它的上下文:“把这块地址当做整数看待!不要管它原来是什么。”这样,内容0x0000803F按整数解释,其值正好就是1065353216(十进制数)。 通过查看汇编代码可以证实“(int)a相当于重新构造了一个值等于a的整型数”之说,而(int&)的作用则仅仅是表达了一个类型信息,意义在于为cout<<及==选择正确的重载版本。

11. 以下代码有什么问题? typedef vector IntArray; IntArray array;

array.push_back( 1 ); array.push_back( 2 ); array.push_back( 2 ); array.push_back( 3 );

// 删除array数组中所有的2

for( IntArray::iterator itor=array.begin(); itor!=array.end(); ++itor ) {

if( 2 == *itor ) array.erase( itor ); }

答:同样有缺少类型参数的问题。另外,每次调用“array.erase( itor );”,被删除元素之后的内容会自动往前移,导致迭代漏项,应在删除一项后使itor--,使之从已经前移的下一个元素起继续遍历。

12. 写一个函数,完成内存之间的拷贝。[考虑问题是否全面] 答:

void* mymemcpy( void *dest, const void *src, size_t count ) {

char* pdest = static_cast( dest ); const char* psrc = static_cast( src ); if( pdest>psrc && pdest{

for( size_t i=count-1; i!=-1; --i ) pdest[i] = psrc[i]; } else {

for( size_t i=0; ipdest[i] = psrc[i]; }

return dest; }

1.实现双向链表删除一个节点P,在节点P后插入一个节点,写出这两个函数; 答:

//假设线性表的双向链表存储结构 typedef struct DulNode{

struct DulNode *prior; //前驱指针 ElemType data; //数据

struct DulNode *next; //后继指针 }DulNode,*DuLinkList; //删除操作

Status ListDelete_DuL(DuLinkList &L,int i,ElemType &e) {

if(!(p=GetElemP_DuL(L,i))) //此处得到i位置的节点指针,如果有需要也得写出具体函数实现

return ERROR; e=p->data;

p->prior->next=p->next; p->next->prior=p->pror; free(p); return OK; }

//插入操作

Status ListInsert_DuL(DuLinkList &L,int i,ElemType &e) {

if(!(p=GetElemP_DuL(L,i))) return ERROR;

if(!(s=(DuLinkList)malloc(sizeof(DuLNode)))) return ERROR;

s->data=e;

s->prior=p->prior; p->prior->next=s; s->next=p; p->prior=s; return OK; }

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

该函数命名为convert,参数的意义为:

*strDest目的字符串,*strSrc源字符串,length源字符串的长度 函数实现为:

char* convert(char *strDest, const char *strSrc,int length) {

char * cp = strDest; int i=0;

while(*strSrc && i{

if (*strSrc=='\\t') //将\\t转换成4个空格 {

for(int j=0;j<4;j++) *cp++=' '; }

else //否则直接拷贝 *cp++=*strSrc; strSrc++; i++; }

return strDest; }

3.Windows程序的入口是哪里?写出Windows消息机制的流程。 答:

Windows程序的入口是WinMain函数 消息机制:系统将会维护一个或多个消息队列,所有产生的消息都会被放入或是插入队列中。系统会在队列中取出每一条消息,根据消息的接收句柄而将该消息发送给拥有该窗口的程序的消息循环。每一个运行的程序都有自己的消息循环,在循环中得到属于自己的消息并根据接收窗口的句柄调用相应的窗口过程。而在没有消息时消息循环就将控制权交给系统。

4.如何定义和实现一个类的成员函数为回调函数? 答:

所谓的回调函数,就是预先在系统的对函数进行注册,让系统知道这个函数的存在,以后,当某个事件发生时,再调用这个函数对事件进行响应。

定义一个类的成员函数时在该函数前加CALLBACK即将其定义为回调函数,函数的实现和普通成员函数没有区别

5.C++里面是不是所有的动作都是main()引起的?如果不是,请举例。

答:不是,比如中断引起的中断处理不是直接由main()引起的,而是由外部事件引起的。

6.C++里面如何声明const void f(void)函数为C程序中的库函数? 答:在该函数前添加extern “C”声明

7.下列哪两个是等同的 int b;

A const int* a = &b; B const* int a = &b;

C const int* const a = &b; D int const* const a = &b; 答:

各式表示的意思分别为:

A const int* a = &b; //*a是const,但指针a可变 B const* int a = &b; //a是const,但*a可变

C const int* const a = &b; //a和*a都是const,常量和指针的值都不能改变 D int const* const a = &b; //a和*a都是const,常量和指针的值都不能改变 因此C,D两者是相同的。

总结个技巧:如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。

8. 内联函数在编译时是否做参数类型检查?

答:做类型检查,因为内联函数就是在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来代替

1. C++中如何阻止一个类被实例化?

纯虚函数;构造函数私有化(友元)

2. 一般在什么时候构造函数被声明成private呢?

singleton模式; 阻止某些操作(如阻止拷贝构造)

3. 什么时候编译器会生成默认的copy constructor呢?

用户没有自定义copy constructor;在代码中使用到了copy constructor; 4. 如果你已经写了一个构造函数,编译器还会生成copy constructor吗?

5. struct和class有什么区别?

答:默认的访问级别不同,struct是public,class是private

6. 没有别的不同了吗?

7. 为什么说如果一个类作为基类,则它的析构函数要声明成virtual的?

因为,如果delete一个基类的指针时, 如果它指向的是一个子类的对象,那么析构函数不为虚就会导致无法调用子类析构函数,从而导致资源泄露。 当然,另一种做法是将基类析构函数设为protected.

8. inline的函数和#define有什么区别?

1) 宏是在预编译阶段简单文本替代, inline在编译阶段实现展开 2)宏肯定会被替代,而复杂的inline函数不会被展开 3)宏容易出错(运算顺序),且难以被调试,inline不会

4)宏不是类型安全,而inline是类型安全的,会提供参数与返回值的类型检查 当出现以下情况时inline失败

9. inline是什么意思?

10. 那你说说什么时候会真的被inline,什么时候不会呢?

当出现以下情况时inline失败:函数size太大;inline虚函数函数中存在循环或递归函数;调用其他inline函数

11. 如果把一个类的成员函数写在类的声明中是什么意思? inline此函数 (inline与template类似, 必须在.h中实现)

12. public继承和private继承有什么架构上的区别?

public是is-a的关系,继承接口与实现;private是has-a的关系 ,只继承实现

13. 在多继承的时候,如果一个类继承同时继承自class A和class B,而class A和 B中都有一个函数叫foo(),如何明确的在子类中指出override哪个父类的foo()?

14. 虚拟继承的语法是什么?

A / \\ B C \\ / D class A{};

class B: virtual public A{}; class C: virtual public A{}; class D: public B, public C{};

13. 在多继承的时候,如果一个类继承同时继承自class A和class B,而class A和 B中都有一个函数叫foo(),如何明确的在子类中指出override哪个父类的foo()?

14. 虚拟继承的语法是什么?

A / \\ B C \\ / D class A{};

class B: virtual public A{}; class C: virtual public A{}; class D: public B, public C{};

本文来源:https://www.bwwdw.com/article/5d4g.html

Top