问题1:
编写类String的构造函数、析构函数和赋值函数。
问题2:
Which of the following is true about "Copy Constructor" ?
A. They copy constructor into each other.
B. A default is provided, but simply does a member-wise copy.
C. They can't copy arrays into each other.
D. All of the above.
问题3:
Which of the following class DOES NOT need a copy constructor ?
A. A matrix class in which the actual matrix is allocated dynamically within the constructor and is deleted within its destructor.
B. A payroll class in which each object is provided with a unique ID.
C. A word class containing a string object and vector object of line and column location pairs.
D. A library class containing a list of book object.
问题4:
Which virtual function re-declarations of the Derived class are correct ?、
A. Base* Base::copy(Base*);
Base* Derived::copy(Derived*);
B. Base* Base::copy(Base*);
Derived* Derived::copy(Base*);
C. ostream& Base::print(int, ostream &);
ostream& Derived::print(int, ostream &);
D. void Base::eval() const;
void Derived::eval();
问题5:
以下程序存在问题么?该如何修改?
#include <new>
#include <iostream>
using namespace std;
class NameStr
{
public:
NameStr()
{
static const char s_szDefaultName[] = "Default name";
static const char s_szDefaultStr[] = "Default string";
strcpy(m_pName, s_szDefaultName);
strcpy(m_pData, s_szDefaultStr);
}
NameStr(const char *pName, const char *pData)
{
m_pName = new char [strlen(pName)];
m_pData = new char [strlen(pData)];
strcpy(m_pName, pName);
strcpy(m_pData, pData);
}
~NameStr() {}
void Print()
{
cout << "Name: " << m_pName << endl;
cout << "String: " << m_pData << endl;
}
private:
char *m_pName;
char *m_pData;
}
int _tmain(int argc, _TCHAR* argv[])
{
NameStr *pDefNss = NULL;
try
{
pDefNss = new NameStr[10];
NameStr ns("Kingsoft string", "This is for test.");
ns.Print();
}
catch(...)
{
cout << "Exception!" << endl;
}
delete pDefNss;
return 0;
}
=======================================================================================
问题1答案:
已知类String的原型为:
class String
{
public:
String(const char * str = NULL); // 普通构造函数
String(const String &other); // 拷贝构造函数
~String(); // 析构函数
String & operator = (const String & other); // 赋值函数
private:
char * m_data; // 用于保存字符串
};
编写String的上述四个函数。
1. String的析构函数
为了防止内存泄漏,我们还需要定义一个析构函数。当一个String对象超出它的作用域时,这个析构函数将会释放它所占的内存。代码如下:
String::~String()
{
delete [] m_data; // 由于m_data是内部数据类型,也可以写成delete m_data;
}
2. String的构造函数
这个构造函数可以帮助我们根据一个字符串常量创建一个MyString对象。这个构造函数首先分配了足量的内存,然后把这个字符串常量复制到这块内存,代码如下:
String::String(const char * str)
{
if (str == NULL) // 若能加NULL判断则更好
{
m_data = new char[1];
*m_data = '\0';
}
else
{
int length = strlen(str);
m_data = new char[length + 1];
strcpy(m_data, str);
}
}
strlen函数返回这个字符串常量的实际字符数(不包括NULL终止符),然后把这个新的字符串常量的所有字符赋值到我们在String对象创建过程中为m_data数据成员新分配的内存中。有了这个构造函数,我们可以像下面这样根据一个字符串常量创建一个新的String对象:
String str("hello");
3. String的拷贝构造函数
所有需要分配系统资源的用户定义类型都需要一个拷贝构造函数,这样我们可以使用这样的声明:
String s1("hello");
String s2 = s1;
拷贝构造函数还可以帮助我们在函数调用中以传值方式传递一个String参数,并且在当一个函数以值的形式返回String对象时实现“返回时复制”。
String::String(const String &other)
{
int length = strlen(other.m_data); // 若能加NULL判断则更好
m_data = new char [length + 1];
strcpy(m_data, other.m_data);
}
4. String的赋值函数
赋值函数可以实现字符串的传值活动:
String s1("hello");
String s2;
s2 = s1;
代码如下:
String & String::operator = (const String &other)
{
if (this == &other) // 检查自赋值
return *this;
delete [] m_data; // 释放原有的内存资源
int length = strlen(other.m_data);
m_data = new char [length + 1]; // 分配新的内存资源,并复制内容
strcpy(m_data, other.m_data);
return *this; // 返回对象的引用
}
问题2解析:
拷贝构造函数问题。
答案:B
问题3解析:
按照题意,寻找一个不需要拷贝构造函数的类。
A 选项定义拷贝构造函数。
B 选项中,不自定义拷贝构造函数的话,势必造成两个对象的ID不唯一。至于说自定义了拷贝构造函数之后,如何保证新对象的ID唯一,那是实现的问题。实现的方法多种多样,比如可以使用当前的系统tick数作为新ID。当然语义上有损失,不是完全意义上的拷贝,但在这儿只能在保持语义和实现目的之间来一个折中。
选C的原因是使用默认的拷贝构造函数,string子对象和vector子对象的类都是成熟的类,都有合适的赋值操作,拷贝构造函数以避免“浅拷贝”的问题。
D 选项显然是定义拷贝构造函数。
答案:C
问题4解析:
本题问的是哪个派生类的虚函数再声明是对的。
A是重载;B会导致编译错误;C是真正的多态;D是重载。
B选项在gcc测试下也可以算是一种多态,覆盖的虚函数必须返回与父类的同名函数一致的类型。derived class的虚函数的返回类型可以是base class中对应虚函数的返回类型的public derived class。所谓多态指针的一致性,是指“子类虚函数返回的多态指针的静态类型是父类虚函数所返回的多态指针的动态类型集合中的某个类型”。
答案:C
问题5解析:
本题有如下几个错误:
(1) 析构函数中应处理字符指针的释放。
(2) 应该编写拷贝构造函数与赋值函数,这是因为类中已经包含了需要深拷贝的字符指针。
(3) 这个构造函数:NameStr(const char *pName, const char *pData) 中,存在为字符指针与内存大小不匹配的错误,应在原来的基础上增加一个字节,用来保存结束符。如m_pName = new char [strlen(pName) + 1];,并在拷贝结束后手工增加结束符。另外,最好使用较安全的strncpy代替strcpy。
(4) 默认构造函数NameStr()中对未分配的内存空间的字符指针赋值,会引起异常。
(5) 缺少头文件tchar.h。
答案:
修改后的代码如下:
#include <tchar.h>
#include <cstring>
#include <new>
#include <iostream>
using namespace std;
class NameStr
{
public:
NameStr()
{
static const char s_szDefaultName[] = "Default name";
static const char s_szDefaultStr[] = "Default string";
m_pName = new char [strlen(s_szDefaultName) + 1];
m_pData = new char [strlen(s_szDefaultStr) + 1];
strcpy(m_pName, s_szDefaultName);
strcpy(m_pData, s_szDefaultStr);
}
NameStr(const char *pName, const char *pData)
{
m_pName = new char [strlen(pName) + 1];
m_pData = new char [strlen(pData) + 1];
strcpy(m_pName, pName);
strcpy(m_pData, pData);
}
~NameStr()
{
delete [] m_pName;
delete [] m_pData;
}
void Print()
{
cout << "Name: " << m_pName << endl;
cout << "String: " << m_pData << endl;
}
private:
char *m_pName;
char *m_pData;
};
int _tmain(int argc, _TCHAR* argv[])
{
NameStr *pDefNss = NULL;
try
{
pDefNss = new NameStr[10];
NameStr ns("Kingsoft string", "This is for test.");
ns.Print();
}
catch(...)
{
cout << "Exception!" << endl;
}
delete pDefNss;
return 0;
}