(C++ STL)string类的简单模拟实现与源码展示

一、前言

C++ 为了更安全、简单的使用字符串,对 C语言 中的字符串和相关函数进行封装,实现了 string 类。接下来将会实现 string 与一部分常用的函数。

参考:legacy.cplusplus.com网站的 string

具体实现:

  1. string部分构造函数、析构函数、赋值运算符重载。
  2. Iterator 中的 begin、end。
  3. Capacity 中的 size、resize、capacity、reserve、clear、empty。
  4. Element access 中的 operator[]。
  5. Modifiers 中的 operator+=、append、push_back、insert、erase、swap、pop_back 。
  6. String operations 中的 c_str、find。
  7. Non-member function overload 中的 relational operators、operator>>、operator<<。

二、string 的成员变量

在 string.h 中:

namespace my
{
	class string
	{
	public:

		static const size_t npos = -1;

	private:

		char* _str;
		size_t _capacity;
		size_t _size;

	};
}
  1. npos 表示的是 string 的结束位子,详细可参考:C++中string::npos的一些用法总结
  2. _str 字符指针用来存储 在堆上开辟的 字符串数据的 地址。
  3. _capacity 表示容器空间大小,因为 C语言 字符串末尾隐藏了一个 ’ \0 ',则真实存储字符的容量为 _capacity - 1
  4. _size 表示当前字符串大小(不含 ’ \0 ')。

三、string 的主要部分实现

考虑到代码复用,实现并不按照上述排列顺序,且只列举一部分重要的功能。

reserve

参考:std::string::reserve

C++ 对 string reserve函数规定:

  1. 原空间大小 小于 需求大小 时, 对原空间扩容到需求大小或其以上。
  2. 当 原空间 大于或等于 需求 时,非必要的缩减空间调整。

这里实现的空间增长为严格的 2 倍扩容,对于 序号 2 的情况采取直接忽视。

在 string.cpp 中:

void my::string::reserve(size_t n)
{
	if (n + 1 <= _capacity)			// 考虑末尾 '\0'
	{
		return;
	}

	size_t newCapacity = _capacity == 0 ? MY_STRING_INIT_CAPACITY : _capacity * 2;

	// n 仍然大于newCapacity时继续扩容
	// newCapacity <= n 等价 newCapacity < n + 1
	while (newCapacity <= n)	
	{
		newCapacity <<= 1;
	}

	char* str1 = nullptr;			// str1存储原空间地址	
	std::swap(str1, _str);	
	_str = new char[newCapacity];	// _str 新开空间后拷贝

	// _size >= _capacity 只出现在初始化时(这时 _size == _capacity == 0)
	// 以防止超出 _capacity 的非法内存访问
	memcpy(_str, str1, sizeof(char) * (_size >= _capacity ? _capacity : _size + 1));

	_capacity = newCapacity;
	delete[] str1;
}

Iterator

这里迭代器只对原生 char* 进行封装。

参考:std::string::begin
参考:std::string::end

这里只实现部分。

在 my::string 中:

        typedef char* iterator;

		iterator begin()
        {
            return _str;
        }

        iterator end()
        {
            return _str + _size;
        }

operator[]

参考:std::string::operator[]

在 my::string 类中

        char& operator[](size_t index)
        {
            return *(_str + index);
        }

        const char& operator[](size_t index) const
        {
            return *(_str + index);
        }

insert

这里只实现两个。

参考:std::string::insert

在 string.cpp 中:

// 在 pos 位置上插入 字符ch / 字符串str,并返回该string
my::string& my::string::insert(size_t pos, char ch)
{
	assert(pos <= _size);

	reserve(++_size);

	// 将 pos 原位置与之后字符向后移动一步,再用 ch 覆盖 pos 当前原字符
	for (size_t end = _size; end >= pos + 1; --end)
	{
		_str[end] = _str[end - 1];
	}
	_str[pos] = ch;

	return *this;
}

my::string& my::string::insert(size_t pos, const char* str)
{
	assert(pos <= _size);

	size_t len = strlen(str);
	if (len != 0)
	{
		reserve(_size += len);

	// 将 pos 原位置与之后字符向后移动len步,再用 str 覆盖 pos 和之后原字符串
		for (size_t end = _size; end >= pos + len; --end)
		{
			_str[end] = _str[end - len];
		}
		strncpy(_str + pos, str, len);
	}

	return *this;
}

erase

这里只实现一个。

参考:std::string::erase

在 string.cpp 中:

// 删除 pos ~ pos + len 位置上的元素,并返回该 string
my::string& my::string::erase(size_t pos, size_t len)
{
	assert(pos < _size);

	if (len > _size)		// len 默认值为 npos, 此时不能加,会溢出
	{
		len = _size;
	}
							// 若后面还有数据需移动
	for (size_t end = pos; end < _size - len; ++end)
	{
		_str[end] = _str[end + len];
	}
	_size = (_size - pos <= len ? pos : _size - len);
	_str[_size] = '\0';		// '\0' 需单独处理

	return *this;
}

find

这里只实现两个。

参考:std::string::find

在 string.cpp 中:

// 返回 ch 在 string 中第一次出现的位置
size_t my::string::find(char ch, size_t pos) const
{
	assert(pos <= _size);

	size_t index = pos;
	while (index < _size)
	{
		if (_str[index] == ch)
		{
			break;
		}

		++index;
	}

	return index == _size ? npos : index;
}

// 返回子串 str1 在 string 中第一次出现的位置
size_t  my::string::find(const char* str1, size_t pos) const
{
	size_t len1 = strlen(str1);

	assert(pos + len1 <= _size);

	size_t index = pos;
	while (index + len1 <= _size)
	{
		if (strncmp(_str + index, str1, len1) == 0)
		{
			break;
		}

		++index;
	}

	return index + len1 <= _size ? index : npos;
}

resize

参考:std::string::resize

在 my::string 中:

        void resize(size_t n, char ch = '\0')
        {
            if (n > _size)
            {
                reserve(n);

                for (int i = _size + 1; i < n; ++i)
                {
                    _str[i] = ch;
                }
            }

            _str[_size = n] = '\0';
        }

构造、析构、赋值重载

参考:std::string::string
参考:std::string::~string
参考:std::string::operator=

在 my::string 中:

        string(const char* str1 = "")
            :_str(nullptr)
            ,_size(0)
            ,_capacity(0)
        {
            size_t len1 = strlen(str1);
            reserve(len1);
            memcpy(_str, str1, sizeof(char) * (len1 + 1));
            _size = len1;
        }

        string(const string& str1)
            :_str(nullptr)
            ,_size(0)
            ,_capacity(0)
        {
            reserve(str1.capacity());
            memcpy(_str, str1._str, sizeof(char) * (str1._size + 1));
            _size = str1._size;
        }

        ~string()
        {
            if (_str != nullptr)
            {
                delete[] _str;
                _str = nullptr;
            }

            _size = _capacity = 0;
        }

        void swap(string& str1)
        {
            std::swap(_str, str1._str);
            std::swap(_size, str1._size);
            std::swap(_capacity, str1._capacity);
        }

        string& operator=(string str1)
        {
            swap(str1);

            return *this;
        }

四、string 源代码

在 string.h 中:

#pragma once

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include <cassert>

#define MY_STRING_INIT_CAPACITY 4

namespace my
{
    class string
    {
        friend std::ostream& operator<<(std::ostream& cout, const my::string& str1);

        friend std::istream& operator>>(std::istream& cin, my::string& str1);

    public:

        typedef char* iterator;

    public:

        string(const char* str1 = "")
            :_str(nullptr)
            ,_size(0)
            ,_capacity(0)
        {
            size_t len1 = strlen(str1);
            reserve(len1);
            memcpy(_str, str1, sizeof(char) * (len1 + 1));
            _size = len1;
        }

        string(const string& str1)
            :_str(nullptr)
            ,_size(0)
            ,_capacity(0)
        {
            reserve(str1.capacity());
            memcpy(_str, str1._str, sizeof(char) * (str1._size + 1));
            _size = str1._size;
        }

        ~string()
        {
            if (_str != nullptr)
            {
                delete[] _str;
                _str = nullptr;
            }

            _size = _capacity = 0;
        }

        void swap(string& str1)
        {
            std::swap(_str, str1._str);
            std::swap(_size, str1._size);
            std::swap(_capacity, str1._capacity);
        }

        string& operator=(string str1)
        {
            swap(str1);

            return *this;
        }

        //-----------------------------------------------------------

        iterator begin()
        {
            return _str;
        }

        iterator end()
        {
            return _str + _size;
        }

        //-----------------------------------------------------------

        void push_back(char ch)
        {
            insert(_size, ch);
        }

        void pop_back()
        {
            erase(_size - 1, 1);
        }

        string& operator+=(char ch)
        {
            return insert(_size, ch);
        }

        void append(const char* str1)
        {
            insert(_size, str1);
        }

        string& operator+=(const char* str1)
        {
            return insert(_size, str1);
        }

        void clear()
        {
            _str[_size = 0] = 0;
        }

        const char* c_str() const
        {
            return _str;
        }

        //-----------------------------------------------------------

        size_t size() const
        {
            return _size;
        }

        size_t capacity() const
        {
            return _capacity - 1;
        }

        bool empty() const
        {
            return _size == 0;
        }

        void resize(size_t n, char ch = '\0')
        {
            if (n > _size)
            {
                reserve(n);

                for (int i = _size + 1; i < n; ++i)
                {
                    _str[i] = ch;
                }
            }

            _str[_size = n] = '\0';
        }

        void reserve(size_t n);

        //-----------------------------------------------------------

        char& operator[](size_t index)
        {
            return *(_str + index);
        }

        const char& operator[](size_t index) const
        {
            return *(_str + index);
        }

        //-----------------------------------------------------------

        bool operator<(const string& str1)
        {
            return strcmp(c_str(), str1.c_str()) < 0;
        }

        bool operator>(const string& str1)
        {
            return strcmp(c_str(), str1.c_str()) > 0;
        }

        bool operator==(const string& str1)
        {
            return strcmp(c_str(), str1.c_str()) == 0;
        }

        bool operator<=(const string& str1)
        {
            return !(*this > str1);
        }

        bool operator>=(const string& str1)
        {
            return !(*this < str1);
        }

        bool operator!=(const string& str1)
        {
            return !(*this == str1);
        }

        //-----------------------------------------------------------

        size_t find(char ch, size_t pos = 0) const;

        size_t find(const char* str1, size_t pos = 0) const;

        string& insert(size_t pos, char ch);

        string& insert(size_t pos, const char* str);

        string& erase(size_t pos = 0, size_t len = npos);

        static const size_t npos = -1;

    private:

        char* _str;
        size_t _capacity;
        size_t _size;

    };
}

在 string.cpp 中:

#include "string.h"

void my::string::reserve(size_t n)
{
	if (n + 1 <= _capacity)			// 考虑末尾 '\0'
	{
		return;
	}

	size_t newCapacity = _capacity == 0 ? MY_STRING_INIT_CAPACITY : _capacity * 2;

	// n 仍然大于newCapacity时继续扩容
	// newCapacity <= n 等价 newCapacity < n + 1
	while (newCapacity <= n)	
	{
		newCapacity <<= 1;
	}

	char* str1 = nullptr;			// str1存储原空间地址	
	std::swap(str1, _str);	
	_str = new char[newCapacity];	// _str 新开空间后拷贝

	// _size >= _capacity 只出现在初始化时(这时 _size == _capacity == 0)
	// 以防止超出 _capacity 的非法内存访问
	memcpy(_str, str1, sizeof(char) * (_size >= _capacity ? _capacity : _size + 1));

	_capacity = newCapacity;
	delete[] str1;
}

// 返回c在string中第一次出现的位置
size_t my::string::find(char ch, size_t pos) const
{
	assert(pos <= _size);

	size_t index = pos;
	while (index < _size)
	{
		if (_str[index] == ch)
		{
			break;
		}

		++index;
	}

	return index == _size ? npos : index;
}

// 返回子串str1在string中第一次出现的位置
size_t  my::string::find(const char* str1, size_t pos) const
{
	size_t len1 = strlen(str1);

	assert(pos + len1 <= _size);

	size_t index = pos;
	while (index + len1 <= _size)
	{
		if (strncmp(_str + index, str1, len1) == 0)
		{
			break;
		}

		++index;
	}

	return index + len1 <= _size ? index : npos;
}

// 在 pos 位置上插入 字符ch / 字符串str,并返回该string
my::string& my::string::insert(size_t pos, char ch)
{
	assert(pos <= _size);

	reserve(++_size);

	// 将 pos 原位置与之后字符向后移动一步,再用 ch 覆盖 pos 当前原字符
	for (size_t end = _size; end >= pos + 1; --end)
	{
		_str[end] = _str[end - 1];
	}
	_str[pos] = ch;

	return *this;
}

my::string& my::string::insert(size_t pos, const char* str)
{
	assert(pos <= _size);

	size_t len = strlen(str);
	if (len != 0)
	{
		reserve(_size += len);

	// 将 pos 原位置与之后字符向后移动len步,再用 str 覆盖 pos 和之后原字符串
		for (size_t end = _size; end >= pos + len; --end)
		{
			_str[end] = _str[end - len];
		}
		strncpy(_str + pos, str, len);
	}

	return *this;
}

// 删除 pos ~ pos + len 位置上的元素,并返回该 string
my::string& my::string::erase(size_t pos, size_t len)
{
	assert(pos < _size);

	if (len > _size)		// len 默认值为 npos, 此时不能加,会溢出
	{
		len = _size;
	}
							// 若后面还有数据需移动
	for (size_t end = pos; end < _size - len; ++end)
	{
		_str[end] = _str[end + len];
	}
	_size = (_size - pos <= len ? pos : _size - len);
	_str[_size] = '\0';		// '\0' 需单独处理

	return *this;
}

std::ostream& my::operator<<(std::ostream& out, const my::string& str1)
{
	out << str1.c_str();
	return out;
}

std::istream& my::operator>>(std::istream& in, my::string& str1)
{
	str1.clear();

	// 缓冲
	char buf[100] = { 0 };
	char ch = in.get();
	int i = 0;

	while (ch != ' ' && ch != '\n')
	{
		buf[i++] = ch;

		if (i >= 99)
		{
			str1 += buf;
			i = 0;
		}

		ch = in.get();
	}
	if (i > 0)
	{
		buf[i] = '\0';
		str1 += buf;
	}

	return in;
}
  • 26
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值