面试宝典之C++面试知识分享.docx

上传人:b****8 文档编号:11292858 上传时间:2023-02-26 格式:DOCX 页数:65 大小:42.57KB
下载 相关 举报
面试宝典之C++面试知识分享.docx_第1页
第1页 / 共65页
面试宝典之C++面试知识分享.docx_第2页
第2页 / 共65页
面试宝典之C++面试知识分享.docx_第3页
第3页 / 共65页
面试宝典之C++面试知识分享.docx_第4页
第4页 / 共65页
面试宝典之C++面试知识分享.docx_第5页
第5页 / 共65页
点击查看更多>>
下载资源
资源描述

面试宝典之C++面试知识分享.docx

《面试宝典之C++面试知识分享.docx》由会员分享,可在线阅读,更多相关《面试宝典之C++面试知识分享.docx(65页珍藏版)》请在冰豆网上搜索。

面试宝典之C++面试知识分享.docx

面试宝典之C++面试知识分享

2012.07.20

1.strcpy函数

/*static*/char*my_strcpy(char*pDest,constchar*pSrc)

{

assert((pDest!

=NULL)&&(pSrc!

=NULL));

char*pRet=pDest;//recordthepositionofdestination.

while((*pRet++=*pSrc++)!

='\0')

{

NULL;

}

returnpDest;

}

返回值用char*是为了实现链式表达式而返回具体值,如

intlength=strlen(strcpy(strDest,“helloworld”));

关键点:

①static方法:

使某个函数只在一个源文件中有效,不能被其他源文件所用。

(1)它允其他源文件建立并使用同名的函数,而不相互冲突。

(2)声明为静态的函数不能被其他源文件所调用,因为它的名字不能得到。

②源字符串用const:

保证源字符的内容不被改变(地址可以变)。

constchar*str涉及到的字符串,指针的地址可以变,但是指针指向地址的内容不能变;char*conststr涉及到的指针内容不能改变。

提高性能的方法:

staticchar*my_strcpy2(char*pDest,constchar*pSrc)

{

assert((pDest!

=NULL)&&(pSrc!

=NULL));

char*s=(char*)pSrc;

intdelt=pDest-pSrc;//地址之差

while((s[delt]=*s++)!

='\0');//s[delt]实为*(s+delt)

returnpDest;

}

char*my_memset(char*pDst,intval,size_tsize)

{

char*start=pDst;

while(size>0)

{

*pDst=(char)val;

pDst=pDst+1;

size--;

}

returnstart;

}

void*my_memset(void*pDst,intval,size_tsize)//库函数写法

{

void*start=pDst;

while(size>0)

{

*(char*)pDst=(char)val;

pDst=(char*)pDst+1;

size--;

}

returnstart;

}

memcpy是不考虑内存重叠的。

void*my_memcpy(void*pDst,constvoid*pSrc,size_tsize)

{

void*ret=pDst;

while(size>0)

{

*(char*)pDst=*(char*)pSrc;

pDst=(char*)pDst+1;

pSrc=(char*)pSrc+1;

size--;

}

returnret;

}

void*my_memmove(void*pDst,constvoid*pSrc,size_tsize)

{

void*ret=pDst;

if((char*)pDst<=(char*)pSrc||(char*)pSrc+size>=pDst)

//内存没有重叠时与memcpy一样

{

while(size>0)

{

*(char*)pDst=*(char*)pSrc;

pDst=(char*)pDst+1;

pSrc=(char*)pSrc+1;

size--;

}

}

else//OverLappingbuffers内存重叠

{

pDst=(char*)pDst+size-1;

pSrc=(char*)pSrc+size-1;

while(size>0)

{

*(char*)pDst=*(char*)pSrc;

pDst=(char*)pDst-1;

pSrc=(char*)pSrc-1;

size--;

}

}

returnret;

}

2.赋值语句

inti=1;

intmain()

{

inti=i;//undefinedvalue.里面定义了i之后就屏蔽了全局变量

return0;

}

3.求一个数转换成二进制的数中的1的个数的求法:

intCount1forBinary(intx)

{

intcount=0;

while(x)

{

count++;

x=x&(x-1);//每次消去最后一个1

}

returncount;

}

补充:

判断一个数是不是2的N次方的方法:

(x&(x-1))

4.C语言中printf和C++中cout计算参数时是从右到左压栈的。

printf("%d,%d",*ptr,*(++ptr));//输出的结果应该是一样的

cout<<*ptr<<"\n"<<*(++ptr)<

5.优先级:

++--优先于*&(取值取址)优先于sizeof()优先于*/%优先于<<>>

6.求平均值:

intf(intx,inty)

{

return(x&y)+((x^y)>>1);

}

7.不用判断语句求两个数中间比较大的数

max=(a+b)+abs(a-b)/2;

8.不用中间变量,交换a、b的值

方法一:

a=a+b;b=a-b;a=a-b;//a+b可能会越界

方法二:

a=a^b;b=a^b;a=a^b;

2012.07.21

1.C++的类中的数据成员加上mutable后,修饰为const的成员函数,就可以修改它了。

intfun()const

{

/****/只能调用const的变量,不能改变

}

cout<

2.sizeof的用法

charss3[100]="0123456789";

cout<<"sizeof(ss3):

"<

cout<<"strlen(ss3):

"<

 

intss4[100];

cout<<"sizeof(ss4):

"<

cout<<"strlen(ss4):

"<

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

cout<<"sizeof(str1):

"<

cout<<"sizeof(*str1):

"<

sizeof(struct)时,struct要考虑对齐,一般都是取结构体中最长的来对齐,结构体默认对齐长度为8。

sizeof()计算栈中分配的大小,因此静态变量等全局数据区内的数的大小是不会计算在内的。

sizeof()按字节大小计算。

内存对齐的规则:

1、  对于结构的各个成员,第一个成员位于偏移为0的位置,以后每个数据成员的偏移量必须是min(#pragmapack()指定的数,这个数据成员的自身长度) 的倍数。

2、  在数据成员完成各自对齐之后,结构(或联合)本身也要进行对齐,对齐将按照#pragmapack指定的数值和结构(或联合)最大数据成员长度中,比较小的那个进行。

默认的#pragmapack 指定的值为8,如果最大数据结构成员长度为int,则结果本身按照4字节对齐,结构总大小必须为4的倍数。

内存对齐的主要作用是:

1、  平台原因(移植原因):

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

2、  性能原因:

经过内存对齐后,CPU的内存访问速度大大提升。

CPU把内存当成是一块一块的,块的大小可以是2,4,8,16字节大小,因此CPU在读取内存时是一块一块进行读取的。

块大小称为memoryaccessgranularity(粒度) 本人把它翻译为“内存读取粒度”。

原因在于,为了访问未对齐的内存,处理器需要作两次内存访问;而对齐的内存访问仅需要一次访问。

与strlen()的区别,具体实现

①sizeof是运算符,strlen是函数;②sizeof操作符的结果类型是size_t;③sizeof可以用类型做参数,还可以用函数做参数,而strlen只能用char*的变量做参数,且必须是以’\0’结尾的;④大部分编译器在编译时就计算sizeof的值,是类型或者变量的长度,而strlen在运行时计算,计算字符串的长度,而不是内存的大小;⑤sizeof后如果是变量名可以不加括号。

intmy_strlen(constchar*str)

{

assert(str!

=NULL);

intlen=0;

while((*str++)!

='\0')//先执行*,再执行++

{

len++;

}

returnlen;

}

intmy_strlen2(constchar*str)//库函数

{

constchar*eos=str;

while(*eos++);

return(eos-str-1);

}

3.程序中常规的的变量,都是从大地址向小地址放的(存放在一个堆栈中,堆栈以大地址为底)。

4.数组作为参数传给函数时传的是指针而不是数组,传递的是数组的首地址,如fun(char[8])、fun(char[])都等价于fun(char*),编译器是不知道数组的大小的。

5.类虚继承后,会添加虚表。

虚继承是为了解决多重继承中的重定义而产生的。

classB:

publicvirtualA{}

虚基类:

在虚继承体系中的通过virtual继承而来的基类,如A。

6.inline内联函数,在调用的地方不是跳转,而是把代码直接写到那里。

与宏函数相比,内联函数要做类型检查。

(const与宏的差别也是const有类型设置,会检查类型。

)对于短小的代码,inline减少了普通函数调用时的资源消耗,可以带来效率的提升,而它会增加空间的消耗。

2012.07.23

1.指针和引用的区别

引用不能为空,内容不能改变,使用前不会测试它的合法性;而指针正好相反。

定义引用时必须初始化(const变量一样)。

2.地址值相减,转换成int之后要/sizeof(int)。

P68

int*m=(int*)2000;

int*n=(int*)1000;

printf("%d\n",m-n);//-250

3.const指针:

constint*a;可以执行a++,但是不能执行(*a)++

指向const的指针:

int*const;可以执行(*a)++,但不能执行a++

4.数组名本身就是指针,加个&就变成了双指针,即二位数组。

inta[]={1,2,3,4,5};

int*ptr=(int*)(&a+1);

cout<

5.malloc与free是C++/C的标准库函数,而new/delete是C++的运算符。

对于非内部数据结构的对象而言,只用malloc/free无法满足动态对象的要求。

malloc和free是库函数而不是运算符,不在编译器控制权限之内,不能够执行构造函数和析构函数。

6.智能指针auto_ptr是安全指针,包含在空间std中,用它构造的对象,析构函数总在退栈过程中被调用。

auto_ptrpObj(newObject);

auto_ptrsource(){returnnewObject;}

7.vector*a1=newvector();

a1->push_back(d1);//浅拷贝

deletea1;//释放vector对象,vector包含的元素也同时释放了

8.静态模板类:

template

template用法

STL

9.一个空类默认产生的成员函数:

默认构造函数、析构函数、拷贝构造函数、赋值函数。

拷贝构造函数:

CExample(const CExample&C)就是我们自定义的拷贝构造函数。

可见,拷贝构造函数是一种特殊的构造函数,函数的名称必须和类名称一致,它的唯一的一个参数是本类型的一个引用变量,该参数是const类型,不可变的。

例如:

类X的拷贝构造函数的形式为X(X&x)。

当用一个已初始化过了的自定义类类型对象去初始化另一个新构造的对象的时候,拷贝构造函数就会被自动调用。

也就是说,当类的对象需要拷贝时,拷贝构造函数将会被调用。

以下情况都会调用拷贝构造函数:

一个对象以值传递的方式传入函数体 

一个对象以值传递的方式从函数返回 

一个对象需要通过另外一个对象进行初始化。

深拷贝和浅拷贝可以简单理解为:

如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。

CA(constCA&C)

{

 a=C.a;

 str=newchar[a];//深拷贝

 if(str!

=0)

  strcpy(str,C.str);

}

10.class和struct的区别:

class中变量默认是private,struct中变量默认是public。

C++中存在struct是为了兼容以前的C项目。

11.类中,初始化列表的初始化顺序是根据成员变量的声明顺序来执行的。

常量必须在构造函数的初始化列表里初始化或者将其设置成static。

classA

{

A(){constintSize=0;}

};

或者

classA

{

staticconstintSize=0;

};

12.MFC中Cobject的析构函数是虚拟的。

原因:

多态的情况,CBase*p=newCChild();析构时,如果不是virtual则会调用父类的析构函数,而实际new的子类的空间可能不会完全释放;而如果是virtual的析构函数,则会调用虚拟指针执行的子类的析构函数来释放空间。

13.strcpy,strncpy,strlcpy

strcpy是依据/0作为结束判断的,如果to的空间不够,则会引起bufferoverflow。

strcpy(char*to,constchar*from)

{

      char*save=to;

      for(;(*to=*from)!

='/0';++from,++to);

      return(save);

}

strncpy(path,src,sizeof(path)-1);//要赋值的长度

path[sizeof(path)-1]='/0';

len=strlen(path);

strlcpy(char*dst,constchar*src,size_tsize);

而使用strlcpy,就不需要我们去手动负责/0了,仅需要把sizeof(dst)告之strlcpy即可:

strlcpy(path,src,sizeof(path));

len=strlen(path);

if(len>=sizeof(path))

      printf("srcistruncated.");

并且strlcpy传回的是strlen(str),因此我们也很方便的可以判断数据是否被截断。

2012.07.24

1.String类P112

classString

{

public:

String(constchar*str=NULL);//普通构造函数

String(constString©);//拷贝构造函数

~String();

String&operator=(constString&other);//赋值函数

private:

char*m_data;

};

String:

:

String(constchar*str/*=NULL*/)

{

if(NULL==str)

{

m_data=newchar[1];

m_data='\0';

}

else

{

intlength=strlen(str);

m_data=newchar[length+1];

strcpy(m_data,str);

}

}

String:

:

~String()

{

delete[]m_data;

}

String:

:

String(constString©)

{

intlength=strlen(copy.m_data);

m_data=newchar[length+1];

strcpy(m_data,copy.m_data);

}

String&String:

:

operator=(constString&other)

{

if(this==&other)

{

return*this;

}

delete[]m_data;

m_data=newchar[strlen(other.m_data)+1];

strcpy(m_data,other.m_data);

return*this;

}

调用:

Strings1("hello");

Strings2=s1;或者Strings2(s1);//拷贝构造函数

Strings3;s3=s2;//赋值函数

2.重载在编译期间就确定了,是静态的,重载和多态无关。

真正与多态相关的是覆盖(虚函数)。

当定义了父类的虚函数后,父类指针根据赋给它的不同的子类的指针,动态地调用属于子类的该函数,这样的函数调用在编译期间是无法确定的,调用子类的虚函数的地址无法给出。

封装可以隐藏细节,使得代码模块化,继承可以扩展存在的代码模块,它们都是为了代码重用。

而多态是为了接口重用。

override—覆盖(派生类函数覆盖基类virtual函数),

overload—重载(语义、功能相似的几个函数用同一个名字表示,但参数或返回值不同),

overwrite—重写(派生类的函数屏蔽了与其同名的基类函数)。

重写:

(1)如果派生类的函数与基类的函数同名,但是参数不同。

此时,不论有无virtual关键字,基类的函数将被隐藏(注意别与重载混淆)。

(2)如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数没有virtual关键字。

此时,基类的函数被隐藏(注意别与覆盖混淆)。

classParent

{

public:

voidF()

{

cout<<"ParentF().\n";

}

virtualvoidG()

{

cout<<"ParentG().\n";

}

intAdd(intx,inty)

{

returnx+y;

}

floatAdd(floatx,floaty)//overload

{

returnx+y;

}

};

classChild:

publicParent

{

voidF()//overwrite

{

cout<<"ChildF().\n";

}

virtualvoidG()//override

{

cout<<"ChildG().\n";

}

};

Child*child;

Parent*p=(Parent*)child;(或者Parent*p1=newChild;p1->G();)

p->F();//ParentF().

p->G();//ChildG().

3.voidfunction()const;//常成员函数,它不改变对象的成员变量.也不能调用类中任何非const成员函数。

4.析构函数可以是private的。

classTest1

{

public:

Test1()

{

cout<<"Constructor."<

};

voidDeleteTest1()

{

cout<<"Deletetest1."<

deletethis;

}

private:

~Test1()

{

cout<<"Destructor."<

}

};

Test1*t1=newTest1();

//deletet1;编译的时候会报错,因为析构函数时private的,delete调用不到它

t1->DeleteTest1();

5.构造函数不能是virtual的。

构造函数可以是private的,但是该类不能被实例化,因为不能执行初始化。

private的构造函数不会生成默认的拷贝构造函数。

6.构造函数是从最初始的基类开始构造,各个类的同名变量没有形成覆盖,都是单独的变量。

接口的调用是就近的,优先调用离自己最近的接口(从当前层往上向父辈、祖父辈)。

7.A*pa=newA();

B*pb=(B*)pa;//始终是A类空间

deletepa,pb;//删除所指向的地址,但pa、pb指针并没有删除,也就是悬浮指针

pa=newB();//B类的空间

pb=(B*)pa;

8.子类继承父类时,不管什么继承方式,子类都会继承父类的任何数据成员和方法,不管数据成员和方法是什么类型,只是因为继承方式或类型的原因而

展开阅读全文
相关搜索

当前位置:首页 > 高等教育 > 工学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1