C++零基础(08)——自定义string类

C++ 标准库提供了 string 类,跟方便字符串的处理。

一、string基本使用

1. C风格字符串对比

char *name = NULL;
name = new char[100];
strcpy(name, "张三疯");
cout << name << endl;
delete []name;

C风格字符串,需要自己申请内存,释放内存,使用strcpy(字符串拷贝)等库函数进行处理。

而,string字符串相比较就比较简单:

string nameStr;
nameStr = "张三疯";
cout << nameStr << endl;

string对象,不需要使用者管理内存,使用 =直接赋值,简单直观。

2. string运算符使用

需引入头文件#include <string>

string a = "hello";
string b = " world";
    
string c;
c = a+b; // = 重载
cout << c << endl;// << 重载
    
cout << (a > b) << endl; // > 重载
cout << (a < b) << endl; // < 重载
cout << (a == c) << endl; // == 重载
cout << a[0] << endl; // [] 重载

上述使用了,=<<<>==[]运算符。如果是C风格的字符串,相对来说比较麻烦,这里则不做对比。

二、自定义string类

string类功能很强大,我们可以利用前几节所学知识,自实现一个基础的MyString类。

1、构造和析构函数

class MyString {
    
public:
    // 构造方法
    MyString(); // ①
    MyString(const MyString &str);// ②
    MyString(const char *str);// ③
    ~MyString();// ④
    
    // 成员方法 ⑤
    unsigned long getLength() const;
    char* charStr() const;
    
private:
    unsigned long _length; // 字符串长度
    char *_buf; // 缓存
};
  • ①用来实例化默认对象,如MyString s;MyString *ps = new MyString;
  • ②拷贝构造函数,用于一个已知对象创建另一个对象。
  • ③使用常量字符串创建一个对象,如MyString a = "hello";MyString b(" world");
  • ④析构函数,当对象被释放或销毁的时候调用。
  • ⑤此两个成员方法,是只读的,为了保证私有成员的安全性。

其实现如下:

MyString::MyString() {
    // 初始化长度和缓存
    _length = 0;
    _buf = new char[_length+1];
    assert(_buf);
    memset(_buf, 0, _length+1);
}

// 拷贝构造函数
MyString::MyString(const MyString &str) {
    _length = str.getLength();
    
    _buf = new char[_length+1];
    assert(_buf);
    memcpy(_buf, str.charStr(), _length);
    _buf[_length] = 0;
}

MyString::MyString(const char *str) {
    _length = strlen(str);
    
    _buf = new char[_length+1];
    assert(_buf);
    memcpy(_buf, str, _length);
    _buf[_length] = 0;
}

MyString::~MyString() {
    _length = 0;
    if (_buf != NULL) {
        delete []_buf;
        _buf = NULL;
    }
}

// 获取字符串长度,const表示函数内部不允许修改成员
unsigned long MyString::getLength() const{
    return _length;
}

char* MyString::charStr() const {
    assert(_buf);
    return _buf;
}

2. 运算符重载

public成员方法:

// 运算符重载
// + 拼接 // ①
MyString operator + (const MyString &str); // ①.1
MyString operator + (const char *str);// ①.2
    
// = 赋值 ②
void operator = (const MyString &str); // ②.1
void operator = (const char *str); // ②.2
    
// < > == ③
bool operator  < (const MyString &str);
bool operator  > (const MyString &str);
bool operator  == (const MyString &str);
    
// [] ④
char operator [] (unsigned long index);
  • ①分别实现两个对象拼接和,一个对象与常量字符串拼接,如:
MyString a = "hello"; 
MyString b(" world");
a + b; // ①.1
a + " world"; // ①.2
  • ②分别实现将一个已知对象赋值给一个已知对象和,将一个常量字符串赋值给一个已知对象,如:
MyString a = "hello"; 
MyString b;
b = a;// ②.1
b = "Hello" // ②.2
  • ③两个对象进行比较,如a > b;a < b;a == b;
  • ④下标索引,获取第N个下标对应的值。如cout << a[0] << endl;

具体实现如下:

// + 拼接
MyString MyString::operator + (const MyString &str) {
    assert(_buf&&str._buf);
    unsigned long tmpLen = _length+str.getLength()+1;
    char *tmpStr = new char[tmpLen];
    memcpy(tmpStr, _buf, _length);
    memcpy(tmpStr+_length, str.charStr(), str.getLength()+1);
    tmpStr[tmpLen] = 0;
    MyString s(tmpStr);
    delete []tmpStr;
    return s;
}

MyString MyString::operator + (const char *str) {
    assert(_buf&&str);
    unsigned long strLen = strlen(str);
    unsigned long tmpLen = _length+strLen+1;
    char *tmpStr = new char[tmpLen];
    memcpy(tmpStr, _buf, _length);
    memcpy(tmpStr+_length, str, strLen+1);
    tmpStr[tmpLen] = 0;
    MyString s(tmpStr);
    delete []tmpStr;
    return s;
}

// = 赋值
void MyString::operator = (const MyString &str) {
    _length = str.getLength()+1;
    _buf = (char*)realloc(_buf, _length); // 在原内存上扩展或减小, 也可能重新分配一内存并释放之前的内存。
    assert(_buf);
    memcpy(_buf, str.charStr(), _length);
    _buf[_length] = 0;
}

void MyString::operator = (const char *str) {
    _length = strlen(str)+1;
    _buf = (char*)realloc(_buf, _length); // 在原内存上扩展或减小, 也可能重新分配一内存并释放之前的内存。
    assert(_buf);
    memcpy(_buf, str, _length);
    _buf[_length] = 0;
}

// 比较
bool MyString::operator  < (const MyString &str) {
    assert(_buf && str.charStr());
    int res = strcmp(_buf, str.charStr());
    
    if (0 > res) {
        return true;
    } else {
        return false;
    }
}
                            
bool MyString::operator  > (const MyString &str) {
    assert(_buf && str.charStr());
    int res = strcmp(_buf, str.charStr());
    
    if (0 < res) {
        return true;
    } else {
        return false;
    }
}
                            
bool MyString::operator  == (const MyString &str) {
    assert(_buf && str.charStr());
    int res = strcmp(_buf, str.charStr());
    
    if (0 == res) {
        return true;
    } else {
        return false;
    }
}

// 下标索引
char MyString::operator [] (unsigned long index) {
    if (index >= _length) {
        return 0;
    }
    
    return _buf[index];
}

// 输出  
std::ostream& operator<<(std::ostream& out, const MyString& s) {
    out << s.charStr();
    return out;
}

上述代码中,最后一个方法,输出,是对<<运算符的重载,所以不是MyString类的成员方法。

总结:

  • string类极大的方便了我们对字符串的操作,其中还有许多其他功能成员方法,可以自行查阅其他资料。
  • 实现基础的MyString类主要是对,构造和析构函数的熟练运用,已经对运算符重载的熟练运用。
  • 上述功能,写法各一,可以多多练习,再加上自己的理解进行改进。

本章示例代码

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值