面试C++拾题
前些天,我面试了几个做C++的,这些天,也被人面试。
关于C++的面试方面,林锐博土的<<高质量C++/C编程指南>>里附带的一些试题被大量引证来测试应试人员的基础。很不幸,我也被做了一份,回来后,和林锐的标准答案一对,觉得,他的答案还是有些不足之处,下面我就说说那两个题,也许凭记忆能回忆起面试中的一些趣事。
1.已知strcpy函数的原型是
char *strcpy(char *strDest, const char *strSrc);
其中strDest是目的字符串,strSrc是源字符串。
不调用C++/C的字符串库函数,请编写函数 strcpy
林锐的标准答案如下:
char *strcpy(char *strDest, const char *strSrc);
{
assert((strDest!=NULL) && (strSrc !=NULL)); // 2分
char *address = strDest; // 2分
while( (*strDest++ = * strSrc++) != ‘\0’ ) // 2分
NULL ;
return address ; // 2分
}
我在写时,写出下面这样子的:
char *strcpy_x(char *strDest, const char *strSrc)
{
if((strDest==NULL) || (strSrc ==NULL))
return NULL; // 2分
if(strDest==strSrc)//add by xie
return strDest;
char *address = strDest; // 2分
while( (*strDest++ = * strSrc++) != '\0' ) // 2分
;
return address ; // 2分
}
我当时这样写的理由如下:
a. 没必要为一个strcpy函数中的参数有NULL就断言大错涌出,返回NULL值也是一个好见识
b. 为了防止写出strcpy(strDest,strSrc)(而strDest是等于strSrc,又很不凑巧strDest是大字串,这些代码又运行一种比较计时的环境下,所以判断是否相同应该是必须的。更重要的一点:林锐的这篇文章影响了好多公司招人,这样一写,我给别人留下的印象是不同的,你认为呢?:)
2. 相信大部人都看过甚至做过下面这个题:
已知类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;
// (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;
}
我记得我去年初买了<<inside C++ object modules>>时,里面也以String为例作了一个介绍,不过书到用时找不到,因为我已送给好学后进了。而电子版的,我当时在网上看了,但好像没当下来。我依稀记得里面的拷贝构造函数也要判断if(this == &other)的,不过我认为要是已实现了operator==函数的话,if(*this == other)会更好一些,因为例如象String的两个对象,内容一样,一个给另一个赋值时,用if(*this == other)的判断的话,就可避免下一些操作了。黑夜给了我黑色的眼,我睁一只眼闭一只眼都可以看出上面的标准答案里关于拷贝构造函数中没有这样的判断,对了,上文中的operator被误写成operate了,那些公司里的试题也是这样子的,让我看的时候愣了下。
大家可能会认为拷贝构造函数是构造函数,怎么会传进去它自己呢?肯定是别的对象了。很搞笑的是:String ss(ss),没错,String ss(ss=”xieyingjun”)也是对的写法(对了,我没就这个C++语意查看C++标准,要是根据C++98的标准,我的说法扯淡,也请付之一笑)。文后我附上本篇文章的测试代码。您也可以在C++Builder中先打上这两行,看看结果:AnsiString ss(ss=”xieyingjun”);ShowMessage(ss); 不过,很不幸,类似的代码用STL中的String或VC中的CString来实现,运行结果好象并非是一帆风顺。
#include <iostream>
char *strcpy_x(char *strDest, const char *strSrc)
{
if((strDest==NULL) || (strSrc ==NULL))
return NULL; // 2分
if(strDest==strSrc)//add by xie
return strDest;
char *address = strDest; // 2分
while( (*strDest++ = * strSrc++) != '\0' ) // 2分
;
return address ; // 2分
}
class String
{
friend std::ostream& operator <<(std::ostream& os, const String& arg); //add by xie
public:
String(const char *str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~ String(void); // 析构函数
String & operator =(const String &other); // 赋值函数
private:
char *m_data; // 用于保存字符串
};
// String的析构函数
String::~String(void) // 3分
{
delete [] m_data;
m_data=0; //add by xie
// 由于m_data是内部数据类型,也可以写成 delete m_data;
//note by xie:有次去华为,华为批题,写成delete m_data;不给分,一个人的见识就是这样得来的,:P
}
// 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分
{
if(this==&other)
return; //add by xie
int length = strlen(other.m_data);
m_data = new char[length+1]; // 若能加 NULL 判断则更好
strcpy(m_data, other.m_data);
}
// 赋值函数
String & String::operator =(const String &other) // 13分
{
// (1) 检查自赋值 // 4分
if(this == &other)
return *this;
// (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;
}
std::ostream& operator <<(std::ostream& os, const String& arg) //add by xie
{
os<<arg.m_data;
return os;
}
int main()
{
const char s1[]="hello world!";
char s2[100];
char s3[100];
strcpy_x(s3, strcpy_x(s2,s1));
std::cout<<"s3:"<<s3<<std::endl;
std::cout<<"s2:"<<s2<<std::endl;
String ss(ss=s1);
std::cout<<"ss:"<<ss<<std::endl;
/*此处会报一个内存不能read的错,具体原因引起,还要看C++标准关于这处的语意定义,再作修改,不过要是有人写出大量的不当类似实现代码也会造成内存泄露的。 */
return 0;
}
3.无聊的人生是充满智慧的人生。我突然无缘无故的想出这句很NB的话(不知是否原创啊:我不确定是否来自记忆或是一时汉词重组)。愿与很NB的各位共勉啊。
4.标题拾是捡起的意思,不是数字10的中文大写。