欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C++编程中类class的编写(二):含指针数据的类

程序员文章站 2022-07-16 22:15:46
...
从上一篇文章一中,我们提到过编写的类分为2种,在这里再对含指针数据的类来进行考量,对于含指针数据的类,必须要进行自己编写拷贝构造和拷贝赋值(为了防止浅拷贝也就是2个对象的指针数据指向同一块内存单元),同时也必须使用析构函数(对于指针进行了new或者malloc进行动态分配,必须回收资源),那么由此以编写简单的string类做一个总结:
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;