C/C++的基础终究还是不够牢实,今天随便写了一个String Class,反映出来的问题多多。先贴代码:
String.h
#i nclude <string.h>
#i nclude <stdio.h>
class String{
public:
String(const char* str = NULL);
String(const String& another);
~String(void);
String& operator =(const String& rhs);
void print();
private:
char *m_data;
};
String.cpp
#include "String.h"
String::String(const char* str)
{
if(str == NULL)
{
m_data = new char[1];
m_data[0] = '/0';
}
else
{
m_data = new char[strlen(str) + 1];
strcpy(m_data,str);
}
}
String::String(const String& another)
{
// delete[] m_data; 为什么没有这一句,这一句会出错。不会造成内存泄露吗?
//不会,因为拷贝构造函数只有在对象创建的时候才会调用,而此时m_data还没有被分配内存,所以不会造成内存泄露。
//如果强制删除m_data,会出现错误“_BLOCK_TYPE_IS_VALID(pHead->nBlockUse)”,这表明m_data还没有备分配内存 //就已经 被释放了
m_data = new char[strlen(another.m_data) + 1];
strcpy(m_data,another.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(void)
{
delete []m_data ;
}
void String::print()
{
if(m_data == NULL) printf("No Content!/n");
else {
for(char *c = m_data; *c != '/0'; c++)
printf("%c", *c);
printf("/n");
}
}
void main()
{
char char_a1[6] = "hello";
char char_a2[5] = {'a','b','c','e'};
/*调用常规构造函数*/
String s1(char_a1); s1.print();
String s2(char_a2); s2.print();
/*调用拷贝构造函数*/
String s3(s1); s3.print();
/* = 操作符函数*/
s3 = s2; s3.print();
}
代码无疑是正确的,但产生了几点思考,以QA的形式呈现出来:
Q1. m_data不是被申明为private级的么?为什么在String类拷贝构造和拷贝赋值的时候,能访问另外一个String类的私有成员变量?
A1. 这是一个简单得有些愚蠢的问题,Visualage总结得太好了:protection都是在class级别上的,而不是instance级别。由此延伸,我看到了这样一个例子:
class T
{
protected string aaa;
public T(string val)
{
aaa = val;
}
public string GetString(T t)
{
return t.aaa;
}
}
和以下两种用法:
1):
T t1 = new T("test");
Console.WriteLine(t1.GetString(t1));
2):
T t1 = new T("test1");
T t2 = new T("test2");
Console.WriteLine(t1.GetString(t2));
请选择答案并说明理由:
a) 编译不通过
b)第一种用法不能通过
c)第二种用法不能通过
d)都能通过
答案是d,不要说aaa限制成是protected,就是private,答案还是d。
这样的问题是概念层面上的,归根到底的解释来源于编译器,有的事情是你我都无法知道的……
Q2. 你有没有注意到我是这么写的:char char_a2[5] = {'a','b','c','e'}; 为什么不写成 char char_a2[4] = {'a','b','c','e'}; 呢?
A2. 这个问题也是涉及到很C++语言很基础的部分,因为在常规构造中,我们用到了strlen(str),strlen在string.h的定义大概是这样的:
int strlen(const char *c){
if( c == NULL) return 0;
int count = 0;
while( *(c++) != '/0') count++;
return count;
}
看出问题来了么?对,strlen函数要检查形参的结尾'/0'(所以,一般传给strlen的参 数都是字符串)。如果我们传入了一块不以'/0'结尾的字符数组,可以想象:理论上来说,我们不清楚strlen在什么时候能返回一个整数,而由此,我们 动态分配的内存空间的大小也是未知的,完全由compiler决定。
为了达到目的(当然也是为了配合调用了strlen的内存动态分配),我们最好也似乎必须传入以'/0'结尾的字符数组(或者字符串)。
于是,我们写成了char char_a2[5] = {'a','b','c','e'}; 这种写法等同于char char_a2[5] = {'a','b','c','e','/0'};编译器会自动用'/0'补齐“空位”。
勿在浮沙筑高台!强调编程基础的重要性,真是非常的重要啊!