用char数组模拟STL之string

关于字符串,其实字符串是一个抽象的概念,在底层,它是通过char数组来实现的,为了用char数组来模拟字符串,我们需要一个标记来界定字符串的结束位置,因此需要在char数组中字符串的最后一个字符所在位置的后面加一个’\0’。

原因是,可以这么想,字符和字符串的区别在于最后有没有空字符,如‘S’是一个字符,而“S”是一个字符串,它其实相当于’S’ + ‘\0’。

那末,为什么字符串的最后要加上一个空字符’\0’呢?
对于C++来讲,很多处理string的函数,都是逐个处理字符串中的字符,直到遇到空字符为止,如果错误地处理一个没有空字符的“字符串”,如使用cout,系统将在打印完这个“字符串”之后继续将内存中随后的各个字节解释成要打印的字符,这样就会发生错误。
所以,可以说,空字符’\0’是字符串的一个标志。

有些情况下用所谓的字符串来赋值给char数组,系统会自己给你添加’\0’,如:

char arr[10];
scanf("%s", arr);//这种情况下系统就会给你自己添加一个'\0'

又如:

char arr[6] = {"hello"};
char arr[] = {"hello"};//这两种情况系统也会自动给你加'\0'
char arr[5] = {"hello"};//这种情况就要注意了
//有的编译器会自动给你修补不足的空间,并增加一个'\0'.
//然而有的不会,因为'\0'也是一个字符,也要占据一个位置.
//我们的建议是最好不要写这种危险的,碰运气的代码。

所谓自动加’\0’的问题是:你写在双引号中的,编译程序会认为是字符串,会自动为你添加上一个字符串结尾符号’\0’,而不是运行时添加的。

char arr[]={ 'h','e','l','l','o','\0' };
//如果想用字符的方式初始化应当这么写

关于上面两种情况,我们总结一下:自己写char数组,肯定要加上’\0’。scanf(“%s”, arr)这种情况自动加上是因为scanf函数内部做了处理。自己处理要自己加上。char arr[] = {“hello”};这种情况也会自动加上,但归根结底还是写编译器的人帮你处理好了。字符串最后加’\0’是一种规范,不是想当然的自动有个’\0’在那里。

所以在处理的时候,加不加’\0’,会不会自动加,需不需要自己加,只是依照情境,在具体的情境下,代码是依照什么样的逻辑,便有什么样的处理方式。

//String.h:
#ifndef SSCPP2014_STRING_H
#define SSCPP2014_STRING_H

#include <iostream>

class String {
  private:
    char *_buff;
    int _length, _size;  // _size is of the allocated memory

  public:
    // constructors
    String();
    explicit String(const char *src);
    String(const String &src);
    // destructor
    ~String();
    // member methods
    void assign(const char *src);
    void append(const char &other);
    void clear();
    int compare(const String &other) const;
    const char* c_str() const;
    bool empty() const;
    int find(const String &other, int pos = 0) const;
    int length() const;
    String substr(const int &pos, const int &count) const;
    // overload operators
    char& operator[](const int &index);
    void operator=(const String &other);
    void operator=(const char *src);
    String operator+(const String &other) const;
    bool operator<(const String &other) const;
    bool operator<=(const String &other) const;
    bool operator>(const String &other) const;
    bool operator>=(const String &other) const;
    bool operator==(const String &other) const;
    bool operator!=(const String &other) const;
    // friend methods
    friend std::ostream& operator<<(std::ostream& out, const String &str);
};

#endif
//
//  String.cpp:
//  C++
//
//  Created by 舒俊淮 on 16/7/8.
//  Copyright © 2016年 Shujh. All rights reserved.
//
#include "String.h"
#include "string"
#include "cstring"
using namespace std;
const int MAX_SIZE = 101;
String::String() {
    _size = MAX_SIZE;
    _length = 0;
    _buff = new char[_size];
}

String::String(const char *src) {
    _size = MAX_SIZE;
    _length = strlen(src);
    _buff = new char[_size];
    for (int i = 0; i < _length; ++i)
        _buff[i] = src[i];
    _buff[_length] = '\0';
    //这里要注意最后必须加上'\0'
    //可以这么理解,是为了让strlen(char*)对应str.size()
    //如果不这么做,就会因为字符个数的不对应导致内存错误
}

String::String(const String &src) {
    _size = src._size;
    _length = src._length;
    _buff = new char[_size];
    for (int i = 0; i < _length; ++i)
        _buff[i] = src._buff[i];
    _buff[_length] = '\0';
}

String::~String() {clear();}

void String::assign(const char *src) {
    clear();
    _buff = new char[_size];
    _length = strlen(src);
    for (int i = 0; i < _length; ++i)
        _buff[i] = src[i];
    _buff[_length] = '\0';
}

void String::append(const char &other) {
    if (_buff == NULL) {
        _buff = new char[_size];
    }
    if (_length < _size) {
        _buff[_length++] = other;
        _buff[_length] = '\0';
    }
}

void String::clear() {
    if (_buff) {
        delete []_buff;
        _buff = NULL;
        _length = 0;
    }
}

int String::compare(const String &other) const {
    if (*this == other) return 0;
    if (*this > other) return 1;
    if (*this < other) return -1;
}

const char* String::c_str() const {return _buff;}

bool String::empty() const {return _length == 0;}

int String::find(const String &other, int pos) const {
    string toFind, tobeF;
    for (int i = 0; i < _length; ++i)
        toFind += _buff[i];
    for (int i = 0; i < other._length; ++i)
        tobeF += other._buff[i];
    int res = toFind.find(tobeF, pos);
    if (res == string::npos) return _length;
    return res;
}

int String::length() const {return _length;}

String String::substr(const int &pos, const int &count) const {
    String sub;
    for (int i = pos; i < pos + count; ++i)
        sub.append(_buff[i]);
    return sub;
}

char& String::operator[](const int &index) {return _buff[index];}

void String::operator=(const String &other) {
    assign(other._buff);
}

void String::operator=(const char *src) {
    assign(src);
}

String String::operator+(const String &other) const {
    String result(*this);
    for (int i = 0; i < other._length; ++i)
        result.append(other._buff[i]);
    return result;
}

bool String::operator<(const String &other) const {
    if (*this == other) return false;
    int min = _length < other._length ? _length : other._length;
    for (int i = 0; i < min; ++i) {
        if (_buff[i] < other._buff[i]) return true;
        if (_buff[i] > other._buff[i]) return false;
    }
    return _length < other._length ? true : false;
}

bool String::operator<=(const String &other) const {
    if (*this == other || *this < other) return true;
    return false;
}

bool String::operator>(const String &other) const {
    if (*this <= other) return false;
    return true;
}

bool String::operator>=(const String &other) const {
    if (*this == other || *this > other) return true;
    return false;
}

bool String::operator==(const String &other) const {
    if (_length != other._length) return false;
    for (int i = 0; i < _length; ++i)
        if (_buff[i] != other._buff[i])
            return false;
    return true;
}

bool String::operator!=(const String &other) const {
    return !(*this == other);
}

ostream& operator<<(std::ostream& out, const String &str) {
    for (int i = 0; i < str._length; ++i)
        out << str._buff[i];
    return out;
}

这里关于上面的_length和_size说明一下:
有初学者可能不清楚声明一个_size的意图,认为仅仅用_length就可以表示字符串的长度了,_size的存在是画蛇添足,事实并非如此,注意到我们一开始是开辟了_size大小的空间,但是这些空间并不使用,或者说,并没有完全使用,在需要的时候,直接使用已开辟的空间,而不用自己再多去new一次,_size表示总的可用的空间有多少,_length表示已经用了多少,这样做的好处是减少了使用new的次数,大大减少了程序运行的时间。因为使用new操作符分配内存需要在堆中查找足够大的剩余空间,这个操作速度是很慢的,而且有可能出现无法分配内存的异常(空间不够)。许多STL的容器如vector都是此原理。

//test.cpp:
#include <iostream>
#include <cstring>
#include "String.h"
#include <string>
using namespace std;

String a, b("MFGZ!");
String c = b;

void display() {
  cout << a.empty() << " " << a.length() << " " << a << endl;
  cout << b.empty() << " " << b.length() << " " << b << endl;
  cout << c.empty() << " " << c.length() << " " << c << endl;
}

int main() {
  string aa, bb, cc;
  display();
  c[0] = 'K';
  display();
  cin >> aa >> cc;
  a.assign(aa.c_str());
  c.assign(cc.c_str());
  display();
  b.clear();
  display();
  for (int i = 0 ; i < 10; ++i) {
      char t;
      cin >> t;
      a.append(t);
      b.append(t);
      c.append(t);
  }
  display();
  b = c;
  display();
  b = a + c;
  display();
  cout << a.find(String("1993")) << endl;
  cout << b.find(String("HYOUKA")) << endl;
  cout << c.find(String("RIKKA")) << endl;
  cout << a.substr(0, 3) << endl;
  cout << b.substr(3, 8) << endl;//26
  cout << c.substr(6, 1) << endl;//27
  cout << (a > b) << (a >= b) << (a < b) << (a <= b) << (a == b) << endl;
  cout << a.compare(b) << b.compare(a) << endl;
  cout << (a > c) << (a >= c) << (a < c) << (a <= c) << (a == c) << endl;
  cout << a.compare(c) << c.compare(a) << endl;
  b = a;
  cout << (a > b) << (a >= b) << (a < b) << (a <= b) << (a == b) << endl;
  cout << a.compare(b) << b.compare(a) << endl;
  cout << a.compare(a) << endl;
  return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值