更新时间:2018-11-22 16:13作者:李天扬老师
题目六: 1.改错 #include #include class CBuffer { char * m_pBuffer; int m_size; public: CBuffer() { m_pBuffer=NULL; } ~CBuffer() { Free(); } void Allocte(int size) (3) { m_size=size; m_pBuffer= new char[size]; } private: void Free() { if(m_pBuffer!=NULL) { delete m_pBuffer; m_pBuffer=NULL; } } public: void SaveString(const char* pText) const { strcpy(m_pBuffer, pText); } char* GetBuffer() const { return m_pBuffer; }}; void main (int argc, char* argv[]) { CBuffer buffer1; buffer1.SaveString("Microsoft"); printf(buffer1.GetBuffer()); } 答:改正后 主要改正 SaveString 函数 将 void SaveString(const char* pText) const { strcpy(m_pBuffer, pText); } 改为 void SaveString(const char* pText) (1) { Allocte(strlen(pText)+1); (2) strcpy(m_pBuffer, pText); } 原因: (1) const 成员函数表示不会修改数据成员,而 SaveString 做不到,去掉 const 声明 (2) m_pBuffer 指向 NULL,必须用 Allocte 分配空间才能赋值。 (3) 另外需要将 Allocte 成员函数声明为私有成员函数更符合实际 2.下来程序想打印Welcome MSR Asia,改正错误 #include #include char * GetName (void) { //To return MSR Asia String char name[]="MSR Asia"; return name; } void main(int argc, char* argv[]) { char name[32]; //Fill in zeros into name for(int i=0;i<=32;i++) { name[i]=' ';} //copy Welcome to name name="Welcome"; //Append a blank char name[8]=" "; //Append string to name strcat(name,GetName()); //print out printf(name); } 答:改正后为 #include #include char * GetName (void) { //To return MSR Asia String //char name[]="MSR Asia"; (1) char *name=(char *)malloc(strlen("MSR Asia")+1); strcpy(name,"MSR Asia"); return name; } void main(int argc, char* argv[]) { char name[32]; //Fill in zeros into name for(int i=0;i<=32;i++) { name[i]=' '; } //copy Welcome to name //name="Welcome"; (2) strcat(name,"Welcome "); //Append a blank char // name[8]=' '; (3) //Append string to name char *p=GetName(); (4) strcat(name,p); free (p); //print out printf(name); } 原因:(1)在函数内部定义的变量在函数结束时就清空了,必须动态分配内存(2)字符串赋值语句错误,应该用 strcat (3)该语句无效,可去掉 (4)定义一个指针指向动态分配的内存,用完后需用 free 语句释放 3.写出下面程序的输出结果 #include class A { public: void FuncA() { printf("FuncA called "); } virtual void FuncB() { printf("FuncB called "); } }; class B: public A { public: void FuncA() { A::FuncA(); printf("FuncAB called "); } virtual void FuncB() { printf("FuncBB called "); } }; void main(void) { B b; A *pa; pa=&b; A *pa2=new A; b.FuncA(); (1) b.FuncB(); (2) pa->FuncA(); (3)pa->FuncB(); (4) pa2->FuncA(); (5) pa2->FuncB(); delete pa2; } 答: 1.b.FuncA(); 输出 FuncA called FuncAB called 2.b.FuncB();输出 FuncBB called 上两者好理解,直接调用类 B 的相应成员函数 3.pa->FuncA();输出 FuncA called 调用类 A 的 FuncA() 4.pa->FuncB();输出 FuncBB called 调用类 B 的 FuncB(),原因是 C++的动态决议机制,当基类函数声明为 virtual 时,指向派生 类对象的基类指针来调用该函数会选择派生类的实现,除非派生类没有才调用基类的虚函数。还有一点注 意的是:指向基类类型的指针可以指向基类对象也可以指向派生类对象,如 pa=&b; 5. pa2->FuncA(); pa2->FuncB();输出 FuncA called FuncB called 这也好理解,直接调用类 A 的相应成员函数 4.In the main() function, after ModifyString(text) is called, what’s the value of ‘text’? #include #include int FindSubString(char* pch) { int count=0; char* p1=pch; while(*p1!=' ') { if(*p1==p1[1]-1) { p1++; count++; } else { break; } }int count2=count; while(*p1!=' ') { if(*p1==p1[1]+1) { p1++; count2--; } else { break; } } if(count2==0) return count; return 0; } void ModifyString(char* pText) { char* p1=pText; char* p2=p1; while(*p1!=' ') { int count=FindSubString(p1); if(count>0){ *p2++=*p1; sprintf(p2, "%I", count); while(*p2!= ' ') { p2++; } p1+=count+count+1; } else{ *p2++=*p1++; }} void main(void) { char text[32]="XYBCDCBABABA"; ModifyString(text); printf(text); } 答:我不知道这个结构混乱的程序到底想考察什么,只能将最后运行结果写出来是 XYBCDCBAIBAAP1912 给定一单链表的表头指针和指向其中一个节点的指针,要求以该指针为头将原链表逆序排列,例如: N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N3,返回 N3->N2->N1->N5->N4->NULL N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N5,返回这个 N5->N4->N3->N2->N1->NULL N1->N2->N3->N4->N5->NULL pHEAD = N1,pSTART = N1,返回这个 N1->N5->N4->N3->N2->NULL 不允许额外分配存储空间,不允许递归,可以使用临时变量。 typedef struct node { int value; struct node *next; } node; void ReverseList(node **pHead, node *pStart){ node *t1, *t2, *it=*pHead; t1 = it->next; while(t1 != 0){ t2 = t1->next; t1->next = (it == pStart?0:it); it = t1; t1 = t2; } (*pHead)->next = it; *pHead = pStart; }附加了测试代码后的完整程序,抄了一部分 chaoslawful 的,呵呵 #include #include #include typedef struct node { int value;struct node *next; } node; void ReverseList(node **pHead, node *pStart){ node *t1, *t2, *it=*pHead; t1 = it->next; while(t1 != 0){ t2 = t1->next; t1->next = (it == pStart?0:it); it = t1; t1 = t2; } (*pHead)->next = it; *pHead = pStart; } void PrintList(node *pHead){ node *it=pHead; while(it){ printf("%d ",it->value); it=it->next; } } void ConstructList(node **pHead,...) { node *q=0; va_list ap; int i; va_start(ap, pHead); while (1){ i = va_arg(ap,int); if(i<0) break; if(!q){ *pHead=q=(node *)malloc(sizeof(node)); q->value=i; q->next=0; } else { q->next=(node *)malloc(sizeof(node)); q=q->next; q->value=i; q->next=0; }} } void SetStart(node *it, node **pStart, int i) { while(i-- > 0 && it->next != 0){ it = it->next; } *pStart = it; } int main(){ node *pHead=(node *)malloc(sizeof(node *)); node *pStart=(node *)malloc(sizeof(node *)); ConstructList(&pHead,1,2,3,4,5,-1); PrintList(pHead); SetStart(pHead, &pStart, 2); ReverseList(&pHead, pStart); PrintList(pHead); return 1; } 题目七: 1.写出 a*(b-c*d)+e-f/g*(h+i*j-k)的逆波兰表达式 2.面向对象语言中 public,proteced,private 的区别 3.SAX 和 DOM 的区别以及各自优缺点 4.进程和线程区别 5.假设现有一个功能,用户点击一个按钮后就会自动发送一封邮件到用户的邮箱。现在 用户反映没有受到邮件。你怎么去发现并解决问题 6.用 Java 写一个 Singleton 类 7.2 个有序 List,请用 Java 写一个合并函数合并他们,返回一个有序 List public List Merge(List a,List b){ } 题目八: 1.求下面函数的返回值(微软) 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("hello world"); 原因在于 foo( )和"hello 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 << "hello" << endl; 因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选的其它方案包括:返回一个流对象和返回一个流对象指针。但是对于返回一个流对象,程序必须重新(拷贝)构造一个新的流对象,也就是说,连续的两个<<操作符实际上是针对不同对象的!这无法让人接受。对于返回一个流指针则不能连续使用<<操作符。因此,返回一个流对象引用是惟一选择。这个唯一选择很关键,它说明了引用的重要性以及无可替代性,也许这就是 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<<"subscript error"; return error; } } (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. 引用与指针的区别是什么? 指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。此外,就是上面提到的对函数传 ref 和 pointer 的区别。 8. 什么时候需要引用? 流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。 以上 2-8 参考:http://blog.csdn.net/wfwd/archive/2006/05/30/763551.aspx 9. 结构与联合有和区别? 1. 结构和联合都是由多个不同的数据类型成员组成, 但在任何同一时刻, 联合中只存放了一个被选中的成员(所有成员共用一块地址空间), 而结构的所有成员都存在(不同成员的存放地址不同)。 2. 对于联合的不同成员赋值, 将会对其它成员重写, 原来成员的值就不存在了, 而对于结构的不同成员赋值是互不影响的。 10. 下面关于联合的题目的输出? a) #i nclude union { int i; char x[2]; }a; void main() { a.x[0] = 10;a.x[1] = 1; printf("%d",a.i); } 答案:266 (低位低地址,高位高地址,内存占用情况是 Ox010A) b) main() { union{ /*定义一个联合*/ int i; struct{ /*在联合中定义一个结构*/ char first; char second; }half; }number; number.i=0x4241; /*联合成员赋值*/ printf("%c%c ", number.half.first, mumber.half.second); number.half.first='a'; /*联合中结构成员赋值*/ number.half.second='b'; printf("%x ", number.i); getch(); } 答案: AB (0x41 对应'A',是低位;Ox42 对应'B',是高位) 6261 (number.i 和 number.half 共用一块地址空间) 11. 已知 strcpy 的函数原型:char *strcpy(char *strDest, const char *strSrc)其中 strDest 是目的字符串,strSrc 是源字符串。不调用 C++/C 的字符串库函数,请编写函数 strcpy。 答案: char *strcpy(char *strDest, const char *strSrc) { if ( strDest == NULL || strSrc == NULL) return NULL ; if ( strDest == strSrc) return strDest ; char *tempptr = strDest ; while( (*strDest++ = *strSrc++) != ) ; return tempptr ; } 12. 已知 String 类定义如下: class String { public: String(const char *str = NULL); // 通用构造函数 String(const String &another); // 拷贝构造函数~ String(); // 析构函数 String & operater =(const String &rhs); // 赋值函数 private: char *m_data; // 用于保存字符串 }; 尝试写出类的成员函数实现。 答案: String::String(const char *str) { if ( str == NULL ) //strlen 在参数为 NULL 时会抛异常才会有这步判断 { m_data = new char[1] ; m_data[0] = ' ' ; } else { m_data = new char[strlen(str) + 1]; strcpy(m_data,str); } } String::String(const String &another) { m_data = new char[strlen(another.m_data) + 1]; strcpy(m_data,other.m_data); } String& String::operator =(const String &rhs) { if ( this == &rhs) return *this ; delete []m_data; //删除原来的数据,新开一块内存 m_data = new char[strlen(rhs.m_data) + 1]; strcpy(m_data,rhs.m_data); return *this ; } String::~String() { delete []m_data ; } 13. .h 头文件中的 ifndef/define/endif 的作用? 答:防止该头文件被重复引用。 14. #i nclude 与 #i nclude "file.h"的区别? 答:前者是从 Standard Library 的路径寻找和引用 file.h,而后者是从当前工作路径搜寻并引用 file.h。