名企C++面试题及答案(2)

2019-03-15 19:51

例如 int length = strlen( strcpy( strDest, “hello world”) ); 六、编写类String的构造函数、析构函数和赋值函数(25分)

已知类String的原型为: class String {

public:

String(const char *str = NULL); // 普通构造函数 String(const String &other); // 拷贝构造函数 ~ String(void); // 析构函数

String & operate =(const String &other); // 赋值函数

private:

char *m_data; // 用于保存字符串 };

请编写String的上述4个函数。 标准答案:

// String的析构函数

String::~String(void) // 3分 {

delete [] m_data;

// 由于m_data是内部数据类型,也可以写成 delete m_data; }

// String的普通构造函数

String::String(const char *str) // 6分 {

if(str==NULL) {

m_data = new char[1]; // 若能加 NULL 判断则更好

*m_data = ‘\\0’; } else {

int length = strlen(str);

m_data = new char[length+1]; // 若能加 NULL 判断则更好

strcpy(m_data, str); } }

// 拷贝构造函数

String::String(const String &other) // 3分 {

int length = strlen(other.m_data);

m_data = new char[length+1]; // 若能加 NULL 判断则更好

strcpy(m_data, other.m_data); }

// 赋值函数

String & String::operate =(const String &other) // 13分 {

// (1) 检查自赋值 // 4分 if(this == &other)

return *this;//文章来源 草根IT网

(www.caogenit.com)

// (2) 释放原有的内存资源 // 3分 delete [] m_data;

// (3)分配新的内存资源,并复制内容 // 3分 int length = strlen(other.m_data);

m_data = new char[length+1]; // 若能加 NULL 判断则更好

strcpy(m_data, other.m_data);

// (4)返回本对象的引用 // 3分 return *this;

一. 单选题(每题4分,15题,共60分)

1.考虑函数原型void hello(int a,int b=7,char* pszC=”*”),下面的函数调用钟,属于不合法调用的是: A hello(5) B.hello(5,8) C.hello(6,”#”) D.hello(0,0,”#”)

2.下面有关重载函数的说法中正确的是:

A.重载函数必须具有不同的返回值类型 B.重载函数形参个数必须不同

C.重载函数必须有不同的形参列表 D.重载函数名可以不同

3.分析一下程序的运行结果: #include class CBase {

public:

CBase(){cout<<”constructing CBase class”<

class CSub : public CBase {

public:

CSub(){cout<<”constructing CSub class”<

void main() {

CSub obj; }

A. constructing CSub class B.

constructing CBase class

constructing CBase class constructing CSub class

destructing CSub class destructing CBase class

destructing CBase class destructing CSub class

C. constructing CBase class constructing CSub class destructing CSub class destructing CBase class D. constructing CSub class constructing CBase class destructing CBase class destructing CSub class

4.在一个cpp文件里面,定义了一个static类型的全局变量,下面一个正确的描述是:

A.只能在该cpp所在的编译模块中使用该变量 B.该变量的值是不可改变的

C.该变量不能在类的成员函数中引用

D.这种变量只能是基本类型(如int,char)不能是C++类型

5.观察下面一段代码: class ClassA {

public:

virtual ~ ClassA(){};

virtual void FunctionA(){}; };

class ClassB {

public:

virtual void FunctionB(){}; };

class ClassC : public ClassA,public ClassB {

public: };

ClassC aObject;

ClassA* pA=&aObject; ClassB* pB=&aObject; ClassC* pC=&aObject;

关于pA,pB,pC的取值,下面的描述中正确的是: A.pA,pB,pC的取值相同. B.pC=pA+pB

C.pA和pB不相同

D.pC不等于pA也不等于pB

6.参照1.5的代码,假设定义了ClassA* pA2,下面正确的代码是: A.pA2=static_cast(pB);

B.void* pVoid=static_cast(pB); pA2=static_cast(pVoid); C.pA2=pB;

D.pA2=static_cast(static_cast(pB));

7.参照1.5的代码,下面那一个语句是不安全的: A.delete pA B.delete pB C.delete pC 8.下列程序的运行结果为: #include void main() {

int a=2; int b=++a;

cout<

A.0.5 B.0 C0.7 D.0.6666666- 9.有如下一段代码: #define ADD(x,y) x+y int m=3;

m+=m*ADD(m,m); 则m的值为:

A.15 B.12 C.18 D.58

10.如下是一个带权的图,图中结点A到结点D的关键路径的长度是:

A.13 B.15 C.28 D.58 11.下面的模板声明中,正确的是: A.template B.template

C.template

D.template 12.在Windows编程中下面的说法正确的是:

A.两个窗口,他们的窗口句柄可以是相同的 B.两个窗口,他们的处理函数可以是相同的

C.两个窗口,他们的窗口句柄和窗口处理函数都不可以相同.

13.下面哪种情况下,B不能隐式转换为A? A.class B:public A{} B.class A:public B{} C.class B{operator A();} D.class A{A(const B&);}

14.某公司使用包过滤防火墙控制进出公司局域网的数据,在不考虑使用代理服务器的情况下,下面描述错误的是”该防火墙能够( )”.

A.使公司员工只能访问Internet上与其业务联系的公司的IP地址.

B.仅允许HTTP协议通过,不允许其他协议通过,例如TCP/UDP.

C.使员工不能直接访问FTP服务器端口号为21的FTP地

址.

D.仅允许公司中具有某些特定IP地址的计算机可以访问外部网络

15.数字字符0的ASCII值为48,若有以下程序: main() {

char a=’1’,b=’2’; printf(“%c,”,b++); printf(“%d\\n”,b-a); }

程序运行之后的输出结果是: A.3,2 B.50,2 C.2,2 D.2,50 二. 填空题(共40分)

本程序从正文文件text.in读入一篇英文短文,统计该短文中不同单词和它的出现次数,并按词典编辑顺序将单词及它的出现次数输出到正文文件word.out中.

程序用一棵有序二叉树存储这些单词及其出现的次数,一边读入一边建立.然后中序遍历该二叉树,将遍历经过的二叉树上的节点的内容输出. 程序中的外部函数

int getword(FILE* pFile,char* pszWordBuffer,int nBufferLen);

从与pFile所对应的文件中读取单词置入pszWordBuffer,并返回1;若单词遇文件尾,已无单词可读时,则返回0. #include #include #include #include

#define SOURCE_FILE “text.in” #define OUTPUT_FILE “word.out” #define MAX_WORD_LEN 128 typedef struct treenode {

char szWord[MAX_WORD_LEN]; int nCount;

struct treenode* pLeft; struct treenode* pRight; }BNODE;

int getword(FILE* pFile,char* pasWordBuffer,int nBufferLen);

void binary_tree(BNODE** ppNode,char* pszWord) {

if(ppNode != NULL && pszWord != NULL) {

BNODE* pCurrentNode = NULL; BNODE* pMemoNode = NULL; int nStrCmpRes=0;

____(1)_____;pCurrentNode=*ppNode while(pCurrentNode) {

/*寻找插入位置*/

nStrCmpRes = strcmp(pszWord, ___(2)___ );pCurrentNode->nCount if(!nStrCmpRes) {

___(3)___; pCurrentNode->nCount++ return; } else {

___(4)___; pMemoNode=pCurrentNode

pCurrentNode = nStrCmpRes>0? pCurrentNode->pRight : pCurrentNode->pLeft; } } }

pCurrent=new BNODE;

if(pCurrentNode != NULL) {

memset(pCurrentNode,0,sizeof(BNODE));

strncpy(pCurrentNode->szWord,pszWord,MAX_WORD_LEN-1);

pCurrentNode->nCount=1; }

if(pMemoNode==NULL) {

___(5)___; *ppNode= pCurrentNode }

else if(nStrCmpRes>0) {

pMemoNode->pRight=pCurrentNode; } else {

pMemoNode->pLeft=pCurrentNode; } }

void midorder(FILE* pFile,BNODE* pNode) {

if(___(6)___) return;!pNode||!pFile midorder(pFile,pNode->pLeft);

fprintf(pFile,”%s %d\\n”,pNode->szWord,pNode->nCount);

midorder(pFile,pNode->pRight); }

void main() {

FILE* pFile=NULL;

BNODE* pRootNode=NULL;

char szWord[MAX_WORD_LEN]={0}; pFile=fopen(SOURCE_FILE,”r”); if(pFile==NULL) {

printf(”Can’t open file %s\\n”,SOURCE_FILE); return; }

while(getword(pFile,szWord,MAX_WORD_LEN)==1) {

binary_tree(___(7)___);// pRootNode,szWord }

fclose(pFile);

pFile=fopen(OUTPUT_FILE,”w”); midorder(pFile,pRootNode); fclose(pFile); }

三. 附加题(每题30分,2题,共60分)

1. 从程序健壮性进行分析,下面的FillUserInfo函数和Main函数分别存在什么问题? #include #include

#define MAX_NAME_LEN 20 struct USERINFO {

int nAge;

char szName[MAX_NAME_LEN]; };

void FillUserInfo(USERINFO* parUserInfo) {

stu::cout<<”请输入用户的个数:”; int nCount=0; std::cin>>nCount;

for(int i=0;i

std::cout<<”请输入年龄:”; std::cin>>parUserInfo[i]->nAge; std::string strName;

std::cout<<”请输入姓名:”; std::cin>>strName;

strcpy(parUserInfo[i].szName,strName.c_str()); } }

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

USERINFO arUserInfos[100]={0}; FillUserInfo(arUserInfos);

printf(”The first name is:”); printf(arUserInfos[0].szName); printf(”\\n”); return 0; }

2. 假设你在编写一个使用多线程技术的程序,当程序中止运行时,需要怎样一个机制来安全有效的中止所有的线程?请描述其具体流程. int func(x) {

int countx = 0; while(x) {

countx ++; x = x&(x-1); }

return countx; }

假定x = 9999. 答案:8思路:将x转化为2进制,看含有的1的个数。

2. 什么是“引用”?申明和使用“引用”要注意哪些问题?

答:引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。

3. 将“引用”作为函数参数有哪些特点?

(1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。

(2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。

(3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用\指针变量名\的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。

4. 在什么时候需要使用“常引用”?

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名; 例1 int a ;

const int &ra=a; ra=1; //错误 a=1; //正确 例2

string foo( );

void bar(string & s);

那么下面的表达式将是非法的: bar(foo( ));

bar(\

原因在于foo( )和\world\串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。

引用型参数应该在能被定义为const的情况下,尽量定义为const .

5. 将“引用”作为函数返回值类型的格式、好处和需要遵守的规则?

格式:类型标识符 &函数名(形参列表及类型说明){ //函数体 }好处:在内存中不产生被返回值的副本;(注意:正是因为这点原因,所以返回一个局部变量的引用是不可取的。因为随着该局部变量生存期的结束,相应的引用也会失效,产生runtime error! 注意事项:

(1)不能返回局部变量的引用。这条可以参照Effective C++[1]的Item 31.主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了\无所指\的引用,程序会进入未知状态。

(2)不能返回函数内部new分配的内存的引用。这条可以参照Effective C++[1]的Item 31.虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak.(3)可以返回类成员的引用,但最好是const.这条原则可以参照Effective C++[1]的Item 30.主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

(4)流操作符重载返回值申明为“引用”的作用:流操作符<<和>>,这两个操作符常常希望被连续使用,例如:cout << \; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是C++语言中引入引用这个概念的原因吧。赋值操作符=.这个操作符象流操作符一样,是可以连续使用的,例如:x = j = 10;或者(x=10)=100;赋值操作符的返回值必须是一个左值,以便可以被继续赋值。因此引用成了这个操作符的惟一返回值选择。 例3

#i nclude int &put(int n); int vals[10]; int error=-1; void main() {

put(0)=10; //以put(0)函数值作为左值,等价于vals[0]=10;

put(9)=20; //以put(9)函数值作为左值,等价于vals[9]=20; cout<

int &put(int n) {

if (n>=0 && n<=9 ) return vals[n];

else { cout<<\}

(5)在另外的一些操作符中,却千万不能返回引用:+-*/ 四则运算符。它们不能返回引用,Effective C++[1]的Item23详细的讨论了这个问题。主要原因是这四个操作符没有side effect,因此,它们必须构造一个对象作为返回值,可选的方案包括:返回一个对象、返回一个局部变量的引用,返回一个new分配的对象的引用、返回一个静态对象引用。根据前面提到的引用作为返回值的三个规则,第2、3两个方案都被否决了。静态对象的引用又因为((a+b) == (c+d))会永远为true而导致错误。所以可选的只剩下返回一个对象了。 6. “引用”与多态的关系?

引用是除指针外另一个可以产生多态效果的手段。这意味着,一个基类的引用可以指向它的派生类实例。 例4

Class A; Class B : Class A{...}; B b; A& ref = b; 7. “引用”与指针的区别是什么?


名企C++面试题及答案(2).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:低温罐ATG系统技术方案

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: