1.假设String类有如下私有成员:
class String
{
private:
char*str; //points to string allocated by new
//...
};
a.下述默认构造函数有什么问题?
String::String(){}
b.下述构造函数有什么问题?
String::String(const char* s)
{
str=s;
len=strlen(s);
}
c.下述构造函数有什么问题?
String::String(const char*a)
{
strcpy(str,s);
len=strlen(s);
}
答:
a的问题在于,char*str由于需要通过new来分配内存,因此构造函数、复制构造函数、析构函数,都有对应的new或者delete。而这里至少需要声明是空指针,才可以,否则在遇见delete时,会提示出错。
b的问题在于str不能直接指向传递的字符串s。这可能导致多个对象指向同一个内存地址,或者指向一个由new分配的内存地址但这个地址之后又被delete释放(因此使用该对象会出错)。而len这行代码应该放在最先。
c的问题在于,应该先用len=strlen(s),然后str=new char[len+1];,然后再使用strcpy()函数。
2.如果您定义了一个类,其指针成员是使用new初始化的,请指出可能出现的3个问题,以及如何纠正这些问题。
答:
可能①:只有一个构造函数使用了new来分配,其他比如默认构造函数、复制构造函数、构造函数未使用new来分配内存。
解决:应同时都使用。
可能②:析构函数没有使用delete释放内存。
解决:析构函数应该使用delete释放内存。
可能③:未自定义默认赋值运算符(面向对象的)。
解决:赋值运算符应自定义,且使用new来分配内存,strcpy()函数来拷贝字符串内容。
可能④:指针直接指向某作为参数的字符串。
解决:指针应该指向由new分配,由strcpy()拷贝后的内存地址。
3.如果没有显式的提供类方法,编译器将自动生成哪些类方法?请描述这些隐式生成的函数的行为。
答:
①默认构造函数。无赋值,单纯初始化各个数据成员;
②析构函数。无任何操作;
③复制构造函数。按值将被复制的对象传递给被初始化的对象;
④赋值运算符(把一个对象赋值给另一个对象的)。逐成员赋值传递;
⑤地址运算符。(返回this指针的值)
4.找出并改正下述类声明中的错误:
class nifty
{
//data
char personality[];
int talents;
//methods
nifty();
nifty(char*s);
ostream & operator<<(ostream&os, nifty&n);
}
nifty:nifty()
{
personality=NULL;
talents=0;
}
nifty:nifty(char*s)
{
personality = new char [strlen(s)];
personality =s;
talents = 0;
}
ostream & nifty:operator<<(ostream & os, nifty &n)
{
os<<n;
}
答:
错误0:类名首字母一般大写,但不是强制规定。
错误①:看上下文,char personality[]应该想要声明的是char指针,而不是一个不确定字符数的字符串,因此应该用char * personality; char指针和字符串占用的内存空间是不一样的
错误②:公共部分没有写public,在类方法和数据之间加上。
错误③:ostream&那个函数,应该是友元函数,应该在之前加上friend
错误④:作用域解析运算符是“::”,而不是“:”,两个类方法都应该加上。
错误⑤:nifty::nifty(char*s)看意思是要复制指针的字符串给自己。一是new分配的空间少1,二是没有正确使用拷贝,三不太确定talents要初始化为什么,貌似是要初始化为0。因此修改为:
nifty::nifty(char * s)
{
personality = new char[strlen(s)+1];
strcpy(personality, s);
talents=0;
}
错误⑥友元函数无需加作用域解析运算符,函数内部写错。应该改为:
ostream & operator<<(ostream & os, nifty & n)
{
os<<n.personality <<"," <<n.talents;
}
错误⑦类定义结束后没有分号
错误⑧nifty:nifty(char*s)一般写成nifty::nifty(const char *s)
错误⑨ostream & nifty:operator<<(ostream & os, nifty &n)
一般写为:ostream & nifty:operator<<(ostream & os, const nifty &n)——加上cosnt
5.对于下面的类声明:
class Golfer
{
private:
char * fullname; // points to string containing golfer's name
int games; // holds number of golf games played
int * scores; // points to first element of array of golf scores
public:
Golfer();
Golfer(const char * name, int g=0);
//creates empty dynamic array of g elements if g > 0
Golfer(const Golfer & g);
~Golfer();
};
a.下面各条语句将调用哪些类方法
Golfer nacy; //#1
Golfer lulu("Little Lulu"); //#2
Golfer roy("Roy Hobbs",12); //#3
Golfer * par = new Golfer; //#4
Golfer next = lulu; //#5
Golfer hazzard = "Weed Thwacker"; //#6
*par = nancy; //#7
nancy = "Nancy Putter"; //#8
b.很明显,类需要有另外几个方法才能更有用,但是,类需要哪些方法才能防止数据被损坏呢?
答:
a.
1#将调用默认构造函数Golfer()
2#将调用构造函数Golfer(const char * name, int g=0);
3#将调用构造函数Golfer(const char * name, int g=0);
4#将调用默认构造函数Golfer();
5#将调用复制构造函数Golfer(const Golfer & g);
6#将调用构造函数Golfer(const char * name, int g=0);
7#将什么也不调用,单纯将指针指向类对象。
7#将调用赋值运算符(隐式生成的),我没注意到par指针在4#声明了
8#将调用构造函数Golfer(const char * name, int g=0);和隐式生成的赋值运算符
b.
为了防止数据损坏,有以下方法:
①防止拷贝,将赋值运算符(面向对象拷贝给对象的)/复制构造函数,放在私有部分;
②自定义赋值运算符/复制构造函数,让其在复制的时候,避免按值传递,让其通过new、strcpy()函数等方式拷贝数据。