1.头文件介绍

1.类定义及成员变量

class string
{
public:
    typedef char* iterator;
    typedef const char* const_iterator;
private:
    char* _str;
    size_t _size;
    size_t _capacity;
    char _buff[16];
public:
    static const size_t npos;
};

1.定义迭代器类型,便于进行字符串遍历。

2.成员变量:

        1._str:指向动态分配字符串内存的指针

        2._size:字符串的当前长度

        3._capacity:字符串容量

        4._buff[16]:小字符串优化(SSO)的缓冲区,当字符串长度小于16时使用栈内存 

3.静态常量npos:通常定义为-1,因为它为size_t类型,所以是size_t最大值

拓展:buff的作用

第一种:短字符串(SSO启用)

std::string s ="short"

长度为5<16,存在栈上_buff[16]中,没有在堆开辟空间,速度极快

第二种:长字符串(SSO关闭)

std::string s = "this is a long string more than 15 chars";

长度大于15,会调用new[]/delete[]进行动态内存管理,在堆上开辟空间,速度较慢。

2.迭代器接口

iterator begin();
const_iterator cbegin() const;
iterator end();
const_iterator cend() const;

提供迭代器接口,就可以使用范围for遍历字符串,如for(auto e : s1)

3.构造函数、析构函数、拷贝赋值

string(const char* str = "");
string(const string& s);
~string();
string& operator=(const string& s);
string& operator=(string tmp);
void swap(string& s);

构造函数:支持C字符串构造,默认构造(不传参会使用缺省值)

析构函数:释放动态申请的内存

拷贝构造:实现深拷贝,防止浅拷贝造成的程序崩溃

拷贝赋值运算符:使用swap函数拷贝并交换内容

4.容量和访问操作

size_t size() const;
size_t capacity() const;
const char* c_str() const;
void reserve(size_t n=0);
char& operator[](size_t pos);
const char& operator[](size_t pos) const;

size():返回字符串长度,有这个重载<<就不需要变成友元函数。

capacity():返回字符串容量

c_str():返回char* 指针

reserve():可以提前预留合适空间,避免反复扩容

operator[]:支持读写和只读字符串内容

5.字符串修改

void push_back(char c);
void append(const char* str);
string& operator+=(char c);
string& operator+=(const char* str);
void pop_back();
string& insert(size_t pos, char ch);
string& insert(size_t pos, const char* str);
string& erase(size_t pos, size_t len = npos);
void clear();

功能介绍

追加操作:push_back(),append(),operator+=(),在字符串末尾添加字符或者字符串

删除操作:pop_back()尾删一个字符,erase()在指定位置,删除指定字符,不指定默认删完插入操作:insert()在指定位置插入一个字符,或者一个字符串、

清空操作:clear()清空字符串内容

6.查找及提取子串操作

size_t find(char ch, size_t pos = 0)  const;
size_t find(const char* str, size_t pos = 0)  const;
string substr(size_t pos = 0, size_t len = npos) const;

查找:find()查找指定位置的字符或者字符串,找到返回pos,否则返回npos

提取字串:substr()在指定位置,提权n个字符

7.比较运算符

bool operator<(const string& s) const;
bool operator<=(const string& s) const;
bool operator>(const string& s) const;
bool operator>=(const string& s) const;
bool operator==(const string& s) const;
bool operator!=(const string& s) const;

实现字符串的比较,跟C语言的strcmp类似

8.流操作符及全局函数

ostream& operator<<(ostream& out, const string& s);
istream& operator>>(istream& in, string& s);
istream& getline(istream& in, string& s, char delim = '\n');
void swap(string& x, string& y);

流操作符:用于输出/输入

getline():读取一行代码,遇到\n停止,可以改变delim,改变停止位置

全局swap():转发调用成员函数,提高效率

2.函数实现

1.构造函数和析构函数

string::string(const char* str)
	:_size(strlen(str))
{
	_capacity = _size;
	_str = new char[_size + 1];
	memcpy(_str, str, _size + 1);
}

默认构造,用字符串常量构造string,不传参默认只有一个\0

在初始化列表定义,是按声明顺序进行定义的。所以为了提升效率,只使用一次strlen,只定义_size,_capacity直接用_size的值,_str要多开辟一个空间,存放\0,因为_size和_capacity都是不包括\0的。memcpy复制字符串给string,因为要有\0所以+1

string::string(const string& s)
{
	_str = new char[s._capacity + 1];
	memcpy(_str, s._str, s._size + 1);
	_size = s._size;
	_capacity = s._capacity;
}

拷贝构造,为了防止浅拷贝,所以需要开空间,进行深拷贝,否则程序会崩溃。但是这个是传统写法。

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

这个是现代写法,用s._str构造一个tmp,交换内容后,tmp会调用析构销毁自己,但是效率没有什么区别,都需要深拷贝,但是注意!不能写string tmp(s),这样会无限递归死循环。

string::~string()
{
	delete[] _str;
	_str = nullptr;
	_size = _capacity = 0;
}

析构函数:释放空间,指针置空,容量大小归零

string& string::operator=(const string& s)
{
	if (this != &s)
	{
		char* tmp = new char[s._capacity + 1];
		memcpy(tmp, s._str, s._size + 1);
		delete[] _str;
		_str = tmp;
		_size = s._size;
		_capacity = s._capacity;
	}
	return *this;
}

重载赋值运算符:深拷贝赋值,防止浅拷贝问题,将目标字符串完整拷贝给当前对象。这个是传统写法。

string& string::operator=(const string& s)
{
	if (this != &s)
	{
		string tmp(s);
		swap(tmp);
	}
	return *this;
}
string& string::operator=(string tmp)
{
	swap(tmp);
	return *this;
}

这是现代写法,代码更短

memcpy、memmove、strcpy的对比

对比维度 strcpy memcpy memmove
核心功能 专门用于字符串拷贝 通用内存块拷贝 通用内存块拷贝(支持重叠)
拷贝依据 遇到 \0(字符串结束符)停止 指定长度 n 拷贝,不识别 \0 指定长度 n 拷贝,不识别 \0
内存重叠处理 不处理(重叠拷贝结果未定义) 不处理(重叠拷贝结果未定义) 完美处理(支持源和目标内存重叠)
适用数据类型 字符串(char*) 任意数据类型(int、char、结构体等) 任意数据类型(int、char、结构体等)
结束标志 自动拷贝到 \0 并追加 \0 无结束标志,必须指定长度 无结束标志,必须指定长度
安全性 易缓冲区溢出(不检查长度) 需手动指定长度,可控性强 需手动指定长度,可控性强
典型场景 字符串赋值、拼接 数组、结构体、二进制数据拷贝 内存区域有重叠的数据移动
函数原型 char* strcpy(char* dest, const char* src); void* memcpy(void* dest, const void* src, size_t n); void* memmove(void* dest, const void* src, size_t n);

2.迭代器函数

string::iterator string::begin()
{
	return _str;
}
string::const_iterator string::cbegin() const
{
	return _str;
}

string::iterator string::end()
{
	return _str + _size;
}
string::const_iterator string::cend() const
{
	return _str + _size;
}

STL风格的迭代器,支持范围for

3.基本访问和容量函数

const char* string::c_str() const
{
	return _str;
}
size_t string::size() const
{
	return _size;
}
size_t string::capacity() const
{
	return _capacity;
}
char& string::operator[](size_t pos)
{
	assert(pos < _size);
	return _str[pos];
}
const char& string::operator[](size_t pos) const
{
	assert(pos < _size);
	return _str[pos];
}

4.扩容函数

void string::reserve(size_t n)
{
	if (n > _capacity)
	{
		char* tmp = new char[n + 1];
		memcpy(tmp, _str, _size + 1);
		delete[]_str;
		_str = tmp;
		_capacity = n;
		cout << "capacity:" << _capacity << endl;
	}
}

根据输入的值,创建新空间,并复制和释放旧空间,所以不要频繁扩容,效率低。

5.字符串修改函数

void string::push_back(char c)
{
	if (_size >= _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	_str[_size] = c;
	++_size;
	_str[_size] = '\0';
}

void string::append(const char* str)
{
	size_t len = strlen(str);
	if (len + _size > _capacity)
	{
		size_t newcapacity = 2 * _capacity > len + _size ? 2 * _capacity : len + _size;
		reserve(newcapacity);
	}
	memcpy(_str + _size, str, len + 1);
	_size += len;
}

void string::clear()
{
	_str[0] = '\0';
	_size = 0;
}

string& string::operator+=(char c)
{
	push_back(c);
	return *this;
}
string& string::operator+=(const char* str)
{
	append(str);
	return *this;
}

void string::pop_back()
{
	assert(_size > 0);
	--_size;
	_str[_size] = '\0';
}

push_back:末尾追加单个字符,容量不足则自动扩容,补字符串结束符
append:末尾拼接 C 风格字符串,按需扩容后批量拷贝字符
clear:清空字符串内容,重置长度为 0
operator+=(char):重载 +=,实现单个字符尾部追加
operator+=(const char):重载 +=,实现字符串尾部拼接
pop_back:删除末尾字符,非空才可执行,重置结束符位置

6.插入和删除函数

string& string::insert(size_t pos, char ch)
{
	assert(pos <= _size);
	if (_size >= _capacity)
	{
		size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
		reserve(newcapacity);
	}
	size_t end = _size + 1;
	while (end > pos)
	{
		_str[end] = _str[end - 1];
		--end;
	}
	_str[pos] = ch;
	++_size;
	_str[_size] = '\0';
	return *this;
}
string& string::insert(size_t pos, const char* str)
{
	assert(pos <= _size);
	size_t len = strlen(str);
	if (len + _size >= _capacity)
	{
		size_t newcapacity = 2 * _capacity > len + _size ? 2 * _capacity : len + _size;
		reserve(newcapacity);
	}
	size_t end = _size + len;
	while (end > pos + len - 1)
	{
		_str[end] = _str[end - len];
		--end;
	}
	for (size_t i = 0; i < len; i++)
	{
		_str[pos + i] = str[i];
	}
	_size += len;
	return *this;
}

string& string::erase(size_t pos, size_t len)
{
	assert(pos < _size);
	if (len == npos || len >= (_size - pos))
	{
		_size = pos;
		_str[_size] = '\0';
	}
	else
	{
		size_t i = pos + len;
		memmove(_str + pos, _str + i, _size + 1 - i);
		_size -= len;
	}
	return *this;
}

insert(pos, char):指定位置插入单个字符,按需扩容,后移字符,更新长度
insert(pos, str):指定位置插入字符串,按需扩容,整体后移,拷贝插入
erase(pos, len):删除指定位置字符,删至末尾 / 指定长度,前移覆盖,更新结束符

7.查找及提取字串

size_t string::find(char ch, size_t pos)  const
{
	for (size_t i = pos; i < _size; i++)
	{
		if (_str[i] == ch)
		{
			return i;
		}
	}
	return npos;
}
size_t string::find(const char* str, size_t pos)  const
{
	const char* p = strstr(_str + pos, str);
	if (p == nullptr)
	{
		return npos;
	}
	else
	{
		return p - _str;
	}
}

find(char, pos):从指定位置查找字符,找到返回下标,找不到返回 npos
find(const char, pos):从指定位置查找子串,找到返回首字符下标,找不到返回 npos

string string::substr(size_t pos, size_t len) const
{
	if (len == npos || len >= _size - pos)
	{
		len = _size - pos;
	}
	string ret;
	ret.reserve(len);
	for (size_t i = 0; i < len; i++)
	{
		ret += _str[pos + i];
	}
	return ret;
}

从pos位置开始,截取len个字符返回新字符串;长度不足则截取到末尾

8.比较运算符

bool string::operator<(const string& s) const
{
	size_t i1 = 0, i2 = 0;
	while (i1 < _size && i2 < s._size)
	{
		if (_str[i1] < s[i2])
		{
			return true;
		}
		else if (_str[i1] > s[i2])
		{
			return false;
		}
		else
		{
			++i1;
			++i2;
		}
	}
	return i2 < s._size;
}

逐字符字典序比较:逐个对比字符,当前字符串更小时返回 true;前缀相同时,短字符串更小。

bool string::operator==(const string& s) const
{
	size_t i1 = 0, i2 = 0;
	while (i1 < _size && i2 < s._size)
	{
		if (_str[i1] != s[i2])
		{
			return false;
		}
		else
		{
			++i1;
			++i2;
		}
	}
	return i1 == _size && i2 == s._size;
}

判断字符串是否相等:逐字符比较,长度和所有字符都相同才返回 true。

一般来说,只用写小于和等于,其他复用即可,如下

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

9.流操作符及全局函数

以下都是全局函数

ostream& operator<<(ostream& out, const string& s)
{
	//有效防止无法打印字符串中有效\0后的字符
	//out << s.c_str;
	for (size_t i = 0; i < s.size(); i++)//可以不用友元,访问内部_size了
	{
		out << s[i];
	}
	return out;
}

重载输出运算符:逐字符打印字符串有效内容,不受\0影响,完整输出。

istream& operator>>(istream& in, string& s)
{
	s.clear();

	char buff[128];
	int i = 0;
	char ch = in.get();
	while (ch != '\n' && ch != ' ')
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

重载输入 >>,清空字符串后用 128 字节缓冲区批量读取字符,以空格 / 回车结束,支持高效读取任意长度字符串并赋值。

设置 128 字节缓冲区用来暂存读取字符,避免逐个字符频繁拼接触发多次内存扩容,有效提升数据读取效率,同时限制单次存储长度规避数组越界风险,分段缓存的方式也能够正常处理超长输入内容。

istream& getline(istream& in, string& s, char delim )
{
	s.clear();

	char buff[128];
	int i = 0;
	char ch = in.get();
	while (ch != delim)
	{
		buff[i++] = ch;
		if (i == 127)
		{
			buff[i] = '\0';
			s += buff;
			i = 0;
		}
		ch = in.get();
	}
	if (i > 0)
	{
		buff[i] = '\0';
		s += buff;
	}
	return in;
}

功能:

1.自定义实现按指定分隔符读取整行字符串,支持读取带空格的完整一行内容。
2.执行流程:先清空字符串原有内容,通过 128 字节缓冲区暂存读取的字符,逐字符读取输入直到遇到指定分隔符停止,缓冲区满时自动拼接字符串并重置,读取结束后将缓冲区剩余字符追加到字符串,最终返回输入流对象。
3.核心特点:以指定字符作为读取结束标志,支持读取包含空格的字符串,通过缓冲区提升读取效率、减少扩容,兼容超长字符串读取。

void swap(string& x, string& y)
{
	x.swap(y);
}

全局交换函数,调用自身的交换方法,高效交换两个字符串对象的所有内容。

3.文件

头文件

#pragma once
#include <iostream>
#include<string>
#include<algorithm>
#include<assert.h>
using namespace std;

namespace ray
{
	class string
	{
	public:
		typedef char* iterator;
		typedef const char* const_iterator;
		iterator begin();
		const_iterator cbegin() const;
		iterator end();
		const_iterator cend() const;
		string(const char* str = "");
		string(const string& s);
		~string();
		size_t size() const;
		size_t capacity() const;
		const char* c_str() const;
		void reserve(size_t n=0);
		char& operator[](size_t pos);
		const char& operator[](size_t pos) const;
		void push_back(char c);
		void clear();
		void append(const char* str);
		string& operator+=(char c);
		string& operator+=(const char* str);
		string& operator=(const string& s);
		string& operator=(string tmp);
		void swap(string& s);
		void pop_back();
		string& insert(size_t pos, char ch);
		string& insert(size_t pos, const char* str);
		string& erase(size_t pos, size_t len = npos);
		size_t find(char ch, size_t pos = 0)  const;
		size_t find(const char* str, size_t pos = 0)  const;
		string substr(size_t pos = 0, size_t len = npos) const;
		bool operator<(const string& s) const;
		bool operator<=(const string& s) const;
		bool operator>(const string& s) const;
		bool operator>=(const string& s) const;
		bool operator==(const string& s) const;
		bool operator!=(const string& s) const;

	private:
		char*  _str;
		size_t _size;
		size_t _capacity;
		char _buff[16];
	public:
		static const size_t npos;
	};

	ostream& operator<<(ostream& out, const string& s);
	istream& operator>>(istream& in, string& s);
	istream& getline(istream& in, string& s, char delim = '\n');
	void swap(string& x, string& y);
}

源文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"string.h"
using namespace std;

namespace ray
{
	const size_t string::npos = -1;

	string::string(const char* str)
		:_size(strlen(str))
	{
		_capacity = _size;
		_str = new char[_size + 1];
		memcpy(_str, str, _size + 1);
	}

	/*string::string(const string& s)
	{
		_str = new char[s._capacity + 1];
		memcpy(_str, s._str, s._size + 1);
		_size = s._size;
		_capacity = s._capacity;
	}*/
	string::string(const string& s)
	{
		string tmp(s._str);
		swap(tmp);
	}

	string::~string()
	{
		delete[] _str;
		_str = nullptr;
		_size = _capacity = 0;
	}

	const char* string::c_str() const
	{
		return _str;
	}
	size_t string::size() const
	{
		return _size;
	}
	size_t string::capacity() const
	{
		return _capacity;
	}

	string::iterator string::begin()
	{
		return _str;
	}
	string::const_iterator string::cbegin() const
	{
		return _str;
	}

	string::iterator string::end()
	{
		return _str + _size;
	}
	string::const_iterator string::cend() const
	{
		return _str + _size;
	}

	void string::reserve(size_t n)
	{
		if (n > _capacity)
		{
			char* tmp = new char[n + 1];
			memcpy(tmp, _str, _size + 1);
			delete[]_str;
			_str = tmp;
			_capacity = n;
			cout << "capacity:" << _capacity << endl;
		}
	}

	char& string::operator[](size_t pos)
	{
		assert(pos < _size);
		return _str[pos];
	}
	const char& string::operator[](size_t pos) const
	{
		assert(pos < _size);
		return _str[pos];
	}

	void string::push_back(char c)
	{
		if (_size >= _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcapacity);
		}
		_str[_size] = c;
		++_size;
		_str[_size] = '\0';
	}

	void string::append(const char* str)
	{
		size_t len = strlen(str);
		if (len + _size > _capacity)
		{
			size_t newcapacity = 2 * _capacity > len + _size ? 2 * _capacity : len + _size;
			reserve(newcapacity);
		}
		memcpy(_str + _size, str, len + 1);
		_size += len;
	}

	void string::clear()
	{
		_str[0] = '\0';
		_size = 0;
	}

	string& string::operator+=(char c)
	{
		push_back(c);
		return *this;
	}
	string& string::operator+=(const char* str)
	{
		append(str);
		return *this;
	}

	void string::pop_back()
	{
		assert(_size > 0);
		--_size;
		_str[_size] = '\0';
	}

	string& string::insert(size_t pos, char ch)
	{
		assert(pos <= _size);
		if (_size >= _capacity)
		{
			size_t newcapacity = _capacity == 0 ? 4 : 2 * _capacity;
			reserve(newcapacity);
		}
		size_t end = _size + 1;
		while (end > pos)
		{
			_str[end] = _str[end - 1];
			--end;
		}
		_str[pos] = ch;
		++_size;
		_str[_size] = '\0';
		return *this;
	}
	string& string::insert(size_t pos, const char* str)
	{
		assert(pos <= _size);
		size_t len = strlen(str);
		if (len + _size >= _capacity)
		{
			size_t newcapacity = 2 * _capacity > len + _size ? 2 * _capacity : len + _size;
			reserve(newcapacity);
		}
		size_t end = _size + len;
		while (end > pos + len - 1)
		{
			_str[end] = _str[end - len];
			--end;
		}
		for (size_t i = 0; i < len; i++)
		{
			_str[pos + i] = str[i];
		}
		_size += len;
		return *this;
	}

	string& string::erase(size_t pos, size_t len)
	{
		assert(pos < _size);
		if (len == npos || len >= (_size - pos))
		{
			_size = pos;
			_str[_size] = '\0';
		}
		else
		{
			size_t i = pos + len;
			memmove(_str + pos, _str + i, _size + 1 - i);
			_size -= len;
		}
		return *this;
	}

	size_t string::find(char ch, size_t pos)  const
	{
		for (size_t i = pos; i < _size; i++)
		{
			if (_str[i] == ch)
			{
				return i;
			}
		}
		return npos;
	}
	size_t string::find(const char* str, size_t pos)  const
	{
		const char* p = strstr(_str + pos, str);
		if (p == nullptr)
		{
			return npos;
		}
		else
		{
			return p - _str;
		}
	}

	string string::substr(size_t pos, size_t len) const
	{
		if (len == npos || len >= _size - pos)
		{
			len = _size - pos;
		}
		string ret;
		ret.reserve(len);
		for (size_t i = 0; i < len; i++)
		{
			ret += _str[pos + i];
		}
		return ret;
	}

	bool string::operator<(const string& s) const
	{
		size_t i1 = 0, i2 = 0;
		while (i1 < _size && i2 < s._size)
		{
			if (_str[i1] < s[i2])
			{
				return true;
			}
			else if (_str[i1] > s[i2])
			{
				return false;
			}
			else
			{
				++i1;
				++i2;
			}
		}
		return i2 < s._size;
	}
	bool string::operator<=(const string& s) const
	{
		return *this < s || *this == s;
	}
	bool string::operator>(const string& s) const
	{
		return !(*this <= s);
	}
	bool string::operator>=(const string& s) const
	{
		return !(*this < s);
	}
	bool string::operator==(const string& s) const
	{
		size_t i1 = 0, i2 = 0;
		while (i1 < _size && i2 < s._size)
		{
			if (_str[i1] != s[i2])
			{
				return false;
			}
			else
			{
				++i1;
				++i2;
			}
		}
		return i1 == _size && i2 == s._size;
	}
	bool string::operator!=(const string& s) const
	{
		return !(*this == s);
	}

	//传统写法
	/*string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			char* tmp = new char[s._capacity + 1];
			memcpy(tmp, s._str, s._size + 1);
			delete[] _str;
			_str = tmp;
			_size = s._size;
			_capacity = s._capacity;
		}
		return *this;
	}*/
	//现代写法
	string& string::operator=(const string& s)
	{
		if (this != &s)
		{
			string tmp(s);
			swap(tmp);
		}
		return *this;
	}
	string& string::operator=(string tmp)
	{
		swap(tmp);
		return *this;
	}
	void string::swap(string& s)
	{
		std::swap(_str, s._str);
		std::swap(_size, s._size);
		std::swap(_capacity, s._capacity);
	}

	ostream& operator<<(ostream& out, const string& s)
	{
		//有效防止无法打印字符串中有效\0后的字符
		//out << s.c_str;
		for (size_t i = 0; i < s.size(); i++)
		{
			out << s[i];
		}
		return out;
	}
	istream& operator>>(istream& in, string& s)
	{
		s.clear();

		char buff[128];
		int i = 0;
		char ch = in.get();
		while (ch != '\n' && ch != ' ')
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}

	istream& getline(istream& in, string& s, char delim )
	{
		s.clear();

		char buff[128];
		int i = 0;
		char ch = in.get();
		while (ch != delim)
		{
			buff[i++] = ch;
			if (i == 127)
			{
				buff[i] = '\0';
				s += buff;
				i = 0;
			}
			ch = in.get();
		}
		if (i > 0)
		{
			buff[i] = '\0';
			s += buff;
		}
		return in;
	}

	void swap(string& x, string& y)
	{
		x.swap(y);
	}

}

大家可以试试自己写一下,对类和对象的一些知识和命名空间的使用会有所了解。希望大家一键三连。谢谢!

更多推荐