C++编程中类class的编写(二):含指针数据的类
class String{
private:
char* data;
public:
String(const char* ctr=0);//构造函数声明式
String(const String& rhs);//拷贝构造
String& operator = (const String& rhs); //拷贝赋值
~String();//析构
};
对于一个含有指针的类,我们很理所当然的写出上面这样的类,有需要时再添加成员函数,那考虑一下为什么拷贝构造和拷贝赋值里的参数是const reference,传reference是为了快,提高效率,在这两个函数里面,我们传入的值当然是不希望被改变的,也是不需要改变的,只是把他的值给出去,那当然是const.
对构造函数进行编写:
inline
String::String(const char* ctr){
if(ctr){
data = new char[strlen(ctr)+1];
strcpy(data,ctr);
}
else{
data = new char[1];
*data = '\0';
}
}
我们发现这里编写的定义式和在类中的声明式不一样,当然这也是我在编译时发现了错误改正的,编译器提示既然在声明式中缺省赋值了ctr就不要再定义式中重复赋值,所以这里没有其值,当然inline还是要写,建议编译器去inline,能不能行看编译器处理。
紧接着函数会对要进行构造的初值进行判断,如果没有,就给出一个\0结束,有的话就取它的长度开辟空间(注意\0也需要给一个字节),然后字符串拷贝。
析构函数:
inline
String::~String(){
delete[] data;
}
这个函数很明白,释放自己分配的资源或者打开的文件,这里只有data开辟了内存空间所以释放data,开辟的时候是array new,释放的时候就使用array delete.
拷贝构造:
inline
String::String(const String& rhs){
data = new char[strlen(rhs)+1];
strcpy(data,rhs);
}
很简单,就是为了防止浅拷贝,所以分配内存,复制就可以。
拷贝赋值:
inline
String& String::operator = (const String& rhs){
if(this == &rhs)
return *this;
delete[] data;
data = new char[strlen(rhs)+1];
strcpy(data,rhs);
return *this;
}
这里主要有2个注意点,第一就是如果this和rhs指向了同一个地方,我们就不能直接把rhs删除,如果删除,那接着this去开辟空间的时候,我该拿什么值,不知道了,已经没有了,所以需要进行一个判断,防止程序出错。
第二就是我们会想拷贝赋值,我直接对this操作了,那this不就有值了么,还返回干嘛,是这样没错,但是我们经常会有一种用法就是s3=s2=s1="hello world";那这时候如果没有返回值,s2=s1时就会出错,s1拿void给s2?,很明显的如果我要成功使用上述语句,我需要返回一个值给下面的人用,当然你可以不返回,那用户就麻烦了,需要static_cast<String>这样强制类型转换,万一出错呢,接口要友好。
当然在这里我们也会考虑就是把string类输出的情况,这时候就要重载运算符<<,把它放入成员函数明显不合适,那我肯定需要一个传入的参数也就是右边的值,在<<右边我们通常放string,左边放ostream,在成员函数里面重载<<的话,我就需要传入ostream,那我写在程序里就会是s1 << cout,不符合日常习惯,所以一般就使用非成员函数了。
代码如下:
inline
ostream& operator << (ostream* os,const String& rhs){
os << rhs.data;//错误了,在非成员函数里怎么能取对象的private,之前是同一类的所有对象可以看错friend
}
所以这时候,我们需要在String类里面加一个获得private数据的成员函数,这也是为什么我说有需要的时候就要写。
class String{
...
char* get_data() const { return data;}
}
用const函数是因为它不改变所有的值,这时候上面的运算符重载就可以写成os << rhs.get_data();成功获得数据,为什么返回的是ostream&,和上面考虑的是一个道理,一个是目的对象是一直存在内存的,所以可以用引用,返回ostream还是为了可以连续传出,比如cout << s1 << endl << s2 << endl;