String类(C++练习二)

字符串类(String),熟悉内存管理与拷贝控制

    • 类定义
#include <iostream>
//#include <cstring>
using std::cout;
using std::cin;

class String{
    using iterator = char *;
    friend std::ostream &operator<< (std::ostream &, const String &);
    friend std::istream &operator>> (std::istream &, String &);
    friend String operator + (const String &, const String &);
    friend String operator + (const String &, const char *);        //对于非类内的函数,必须参数中存在类类型的参数;
    friend String operator + (const char *, const String &);        //否则会与默认类型的操作符冲突
    friend unsigned int getline(std::istream &, String &);
public:
    String(const char *rhs = "hello, world");    //构造函数,默认为"hello, world" 通常我们可以默认为NULL
    String(const String &);            //拷贝构造函数
    String &operator= (const String&);    //拷贝赋值运算符
    String &operator= (const char *);    //拷贝赋值运算符
    String & operator += (const String&);
    String & operator += (const char *);
    char operator[] (int);
    iterator begin();
    iterator end();
    String & push_back(const char);
    String & clear();

    ~String() { delete [] data; }    //析构函数    
private:
    char *data;
};
std::ostream & operator<< (std::ostream &, const String &);
std::istream &operator>> (std::istream &, String &);

inline String::String(const char *rhs)
{
    if (!rhs)                    //必须先检查rhs是否为NULL;对于NULL的拷贝,会去读取未知地址
        data = new char[1]{0};    //因为我们的析构函数调用了delete,所以必须开辟一个空间,
                                //否则代码在调用析构函数时会delete一个错误空间
    else {
        data = new char[strlen(rhs) + 1];
        strcpy(data, rhs);
    }
}

inline String& String::operator= (const String& rhs)    //拷贝赋值运算
{
    this->data = new char[strlen(rhs.data) + 1];
    strcpy(this->data, rhs.data);
    return *this;
}

inline String & String::operator= (const char *rhs)    //拷贝赋值运算符
{
    this->data = new char[strlen(rhs) + 1];
    strcpy(this->data, rhs);
    return *this;
}
    • 类方法实现
#include "String.h"

inline String::String(const String &rhs)            //拷贝构造函数
{
    data = new char[strlen(rhs.data) + 1];        //每次都要 +1 的原因是strlen不包含最后的'\0'
    strcpy(data, rhs.data);
}

std::ostream &operator<< (std::ostream &os, const String &rhs)
{
    os << rhs.data;
    return os;
}

std::istream& operator >> (std::istream &is, String &rhs)
{
    is >> rhs.data;
    return is;
}

String & String::operator += (const String &rhs)
{
    if (!rhs.data)            //如果要加的类的数据为NULL,则不需要处理了
        return *this;
    else if (!data)            //如果原来的数据为NULL,则直接把后来的数据拷贝过来;由于上面的判断,所以rhs.data != NULL;
    {
        this->data = new char[strlen(rhs.data) + 1];
        strcpy(this->data, rhs.data);
        return *this;
    }
    else                    //data != NULL && rhs.data != NULL
    {
        char *tmp = new char[strlen(this->data) + 1];
        strcpy(tmp, this->data);
        this->data = new char[strlen(data) + strlen(rhs.data) + 1];
        strcpy(data, tmp);
        strcat(data, rhs.data);
        free(tmp);
        return *this;
    }    
}

String & String::operator += (const char * rhs)
{
    if (!rhs)            //如果要加的类的数据为NULL,则不需要处理了
        return *this;
    else if (!data)            //如果原来的数据为NULL,则直接把后来的数据拷贝过来;由于上面的判断,所以rhs.data != NULL;
    {
        this->data = new char[strlen(rhs) + 1];
        strcpy(this->data, rhs);
        return *this;
    }
    else                    //data != NULL && rhs.data != NULL
    {
        char *tmp = new char[strlen(this->data) + 1];
        strcpy(tmp, this->data);
        this->data = new char[strlen(data) + strlen(rhs) + 1];
        strcpy(data, tmp);
        strcat(data, rhs);
        free(tmp);
        return *this;
    }
}

String operator + (const String &lhs, const String &rhs)
{
    String tmp(lhs);
    tmp += rhs;
    return tmp;
}

String operator + (const String &lhs, const char *rhs)
{
    String tmp(lhs);
    tmp += rhs;
    return tmp;
}

String operator + (const char *lhs, const String &rhs)
{
    String tmp(lhs);
    tmp += rhs;
    return tmp;
}

String::iterator String::begin()
{
    return data;
}

String::iterator String::end()
{
    return data + strlen(data);
}

char String::operator[] (int index)
{
    if (index > strlen(data) - 1 || index < 0)
    {
        cout << "index error";
        return 0;
    }
    else
    return *(data + index);
}

String & String::push_back(const char ch)
{
    char *tmp = new char[strlen(data) + 2];
    strcpy(tmp, data);    
    *(tmp + strlen(data)) = ch;            //data + strlen(data)是原来的'\0'处
    *(tmp + strlen(data) + 1) = '\0';
    data = new char[strlen(data) + 2];
    strcpy(data, tmp);
    //free(tmp);
    delete[] tmp;    //delete [] tmp = free(tmp) : 怀疑delete就是把free封装了
    return *this;
}

String & String::clear()
{
    delete[] data;
    data = new char[]{0};
    return *this;
}
unsigned int getline(std::istream &is, String & rhs)
{
    char tmp;
    rhs.clear();
    while (is.get(tmp))
    {       
        if (tmp && tmp != '\n')
            rhs.push_back(tmp);
        else
            break;
    }
    return strlen(rhs.data);
}
  • 先把开始的写好了,后面可以一直重用它,比如 += 和 +;
  • 析构函数中需要使用delete [] data,是因为很多操作中都需要开辟空间,所以在默认构造函数中虽然里面不放东西但是还需要开辟一个空间,因为当这个类由于某些原因调用析构函数时,需要有一块空间给delete使用
  • 使用智能指针应该也是可以的,这样的话就不需要delete了
posted @ 2015-11-01 20:27  dylqt  阅读(326)  评论(0编辑  收藏  举报