造轮子:新建一个属于自己的String类
Posted 炖丸子的猫
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了造轮子:新建一个属于自己的String类相关的知识,希望对你有一定的参考价值。
练习造轮子,新建一个属于自己的MyString类
首先来开启检测内存泄漏的函数
在main里添加
_CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
开启内存泄漏检测
int main() { _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); int *p = new int; return 0; }
如上,写一个一定会泄漏的语句,new了一个int指针,运行程序,在output窗口看到输出如下:
发现了4bytes的泄漏(对应int),那么泄漏检测成功
新建MyString类,为了完成一个字符串的功能,需要有一个char*的数组保存数据,再为了方便使用定义一个int类型的成员变量来表示当前MyString的长度
接下来定义MyString的构造函数,需要有默认构造函数,带参数的构造函数和拷贝构造函数,
代码如下:
class MyString { private: char* p; int strlength; public: MyString(); MyString(const char* a); MyString(const MyString& a); MyString(std::string s); ~MyString(); }
接下来完成构造函数和析构函数的实现
MyString::MyString() { strlength = 0; p = new char[strlength + 1]; p[0] = \'\\0\'; } MyString::~MyString() { delete p; } MyString::MyString(const char* a) { strlength = strlen(a); p = new char[strlength + 1]; memcpy(p, a, strlength); p[strlength] = \'\\0\'; } MyString::MyString(const MyString& a) { strlength = a.strlength; p = new char[strlength + 1]; memcpy(p, a.p, strlength); p[strlength] = \'\\0\'; } MyString::MyString(std::string s) { strlength = (int)s.length(); p = new char[strlength + 1]; memcpy(p, s.c_str(), strlength); p[strlength] = \'\\0\'; }
类比std::string,可以定义获得长度、是否为空和获得内容的函数(在类内)
int length() const { return strlength; }; bool empty() const { return strlength == 0; }; const char* c_str() { return p; };
为了查看是否正确,对<<流运算符进行重载,实现输出到屏幕,定义如下:
friend istream& operator>> (istream& in, MyString& str); friend ostream& operator<<(ostream& o, const MyString& a);
实现:
istream& operator>> (istream& in, MyString& str) { in >> str.p; str.strlength = strlen(str.p); return in; } ostream& operator<<(ostream& o, const MyString& str) { o << str.p; return o; }
尝试输入输出一下,在main中添加代码:
MyString s1; MyString s2("qwer"); MyString s3(s2); MyString s4(std::string("1234")); cout << "s1:\\t" << s1 << "\\ts1.length():\\t" << s1.length() << "\\ts1.empty():\\t" << s1.empty() << endl; cout << "s2:\\t" << s2 << "\\ts2.length():\\t" << s2.length() << "\\ts2.empty():\\t" << s2.empty() << endl; cout << "s3:\\t" << s3 << "\\ts3.length():\\t" << s3.length() << "\\ts3.empty():\\t" << s3.empty() << endl; cout << "s4:\\t" << s4 << "\\ts4.length():\\t" << s4.length() << "\\ts4.empty():\\t" << s4.empty() << endl; MyString s5; cin >> s5; cout << s5;
发现程序在cin的时候崩溃了。回头查看代码,发现在>>重载的时候没有为p开辟新的内存空间
加上开辟新的内存空间的代码,修改重载>>如下
istream& operator>> (istream& in, MyString& str) { char* tmp = new char[1024]; in >> tmp; str.strlength = strlen(tmp); str.p = new char[str.strlength + 1]; memcpy(str.p, tmp, str.strlength); str.p[str.strlength] = \'\\0\'; delete tmp; return in; }
重新运行,程序不崩溃了但是报了内存泄漏
1byte的内存泄漏,对应的是一个char类型的对象。
回看代码,原来是在cin>>s5的时候没有delete s5本来的内存空间,就直接new了一块内存给它,而s5是空值,刚好对应了1byte的内存,泄漏发生在这里,修改重载>>的代码
istream& operator>> (istream& in, MyString& str) { char* tmp = new char[1024]; in >> tmp; str.strlength = strlen(tmp); if (str.p) delete str.p; str.p = new char[str.strlength + 1]; memcpy(str.p, tmp, str.strlength); str.p[str.strlength] = \'\\0\'; delete tmp; return in; }
重新运行
这回没有内存泄漏了,继续往下编辑MyString
考虑MyString没有赋值函数,对它的=操作符进行重载
MyString& MyString::operator=(const MyString& a) { if(p) delete p; strlength = a.strlength; p = new char[strlength + 1]; memcpy(p, a.p, strlength); p[strlength] = \'\\0\'; return *this; }
在main中进行测试:
MyString s6 = s2; cout << "s6:\\t" << s6 << "\\ts6.length():\\t" << s6.length() << "\\ts6.empty():\\t" << s6.empty() << endl;
std::string的+=运算符很好用,这里也对它进行重载。=有了,但是好像+还没有写,重载如下
MyString operator+ (const MyString& str1, const MyString& str2) //friend function { MyString ret; ret.strlength = str1.strlength + str2.strlength; if (ret.p) delete ret.p; ret.p = new char[ret.strlength + 1]; memcpy(ret.p, str1.p, str1.strlength); memcpy(ret.p + str1.strlength, str2.p, str2.strlength); ret.p[ret.strlength] = \'\\0\'; return ret; }
测试一下+运算符
MyString hello("hello");
MyString world(std::string(", world!"));
cout << "test operator+\\t" << (hello + world) << endl;
然后就可以用+和=完成+=操作符
MyString& MyString::operator+= (const MyString& str) { *this = *this + str; return *this; }
测试一下
MyString helloworld = hello; helloworld += world; cout << "test operator+=\\t" << helloworld << endl;
下标运算符
char& MyString::operator[] (const int i) { assert(i >= 0 && i <= strlength); return p[i]; } const char& MyString::operator[] (const int i)const { assert(i >= 0 && i <= strlength); return p[i]; }
字典序排序(用到strcmp函数)
bool operator== (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) == 0; } bool operator!= (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) != 0; } bool operator< (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) < 0; } bool operator<= (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) <= 0; } bool operator> (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) > 0; } bool operator>= (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) >= 0; }
测试
获得子串(start开始,长度为n的子串)
MyString MyString::substr(int start, int n) { assert(start + n <= strlength); MyString ret; ret.strlength = n;
if(ret.p)
delete ret.p; ret.p = new char[n + 1]; memcpy(ret.p, p + start, n); ret.p[n] = \'\\0\'; return ret; }
往后添加字符串(跟+=一样)
MyString& MyString::append(const MyString& str) { *this += str; return *this; }
在中间添加
MyString& MyString::insert(int pos, const MyString& str) { assert(pos >= 0 && pos <= strlength); strlength += str.length(); char* tmp = new char[strlength + 1]; memcpy(tmp, p, pos); memcpy(tmp + pos, str.p, str.length()); memcpy(tmp + pos + str.length(), p + pos, length() - pos); tmp[strlength] = \'\\0\'; if (p) delete p; p = new char[strlength + 1]; memcpy(p, tmp, strlength + 1); delete tmp; return *this; }
替换字符串(在pos位置,将str的len长度替换原字符串)
MyString& MyString::assign(int pos, const MyString& str, int len) { assert(pos >= 0 && pos <= strlength); if (len > str.length() || len < 0) len = str.length(); strlength = pos + len; char* tmp = new char[strlength + 1]; memcpy(tmp, p, pos); memcpy(tmp + pos, str.p, len); tmp[strlength] = \'\\0\'; if (p) delete p; p = new char[strlength + 1]; memcpy(p, tmp, strlength + 1); delete tmp; return *this; }
擦除中间长度为len的字符串
MyString& MyString::erase(int pos, int len) { assert(pos >= 0 && pos <= strlength); if (pos + len > strlength || len < 0) len = strlength - pos; strlength -= len; char* tmp = new char[strlength + 1]; memcpy(tmp, p, pos); memcpy(tmp + pos, p + pos + len, strlength - pos); tmp[strlength] = \'\\0\'; if (p) delete p; p = new char[strlength + 1]; memcpy(p, tmp, strlength + 1); delete tmp; return *this; }
清空字符串
MyString& MyString::clear() { strlength = 0; if (p) delete p; p = new char[strlength + 1]; p[strlength] = \'\\0\'; return *this; }
测试一堆:
测试完成,暂时完成了造轮子的过程~
完整代码如下:
MyString.h
#pragma once #include <string> #include <iostream> #include <assert.h> using namespace std; class MyString { private: char* p; int strlength; public: MyString(); MyString(const char* a); MyString(const MyString& a); MyString(std::string s); ~MyString(); int length() const { return strlength; }; bool empty() const { return strlength == 0; }; const char* c_str() { return p; }; MyString& operator=(const MyString& a); MyString& operator+= (const MyString& str); MyString substr(int start, int n); MyString& append(const MyString& str); MyString& insert(int pos, const MyString& str); MyString& assign(int pos, const MyString& str, int len); MyString& erase(int pos, int len); MyString& clear(); friend istream& operator>> (istream& in, MyString& str); friend ostream& operator<<(ostream& o, const MyString& a); friend MyString operator+ (const MyString& str1, const MyString& str2); friend bool operator== (const MyString& str1, const MyString& str2); friend bool operator!= (const MyString& str1, const MyString& str2); friend bool operator< (const MyString& str1, const MyString& str2); friend bool operator<= (const MyString& str1, const MyString& str2); friend bool operator> (const MyString& str1, const MyString& str2); friend bool operator>= (const MyString& str1, const MyString& str2); char& operator[] (const int i); const char& operator[] (const int i)const; };
MyString.cpp
#include "pch.h" #include <iostream> #include "MyString.h" using namespace std; MyString::MyString() { strlength = 0; p = new char[strlength + 1]; p[0] = \'\\0\'; } MyString::~MyString() { delete p; } MyString::MyString(const char* a) { strlength = strlen(a); p = new char[strlength + 1]; memcpy(p, a, strlength); p[strlength] = \'\\0\'; } MyString::MyString(const MyString& a) { strlength = a.strlength; p = new char[strlength + 1]; memcpy(p, a.p, strlength); p[strlength] = \'\\0\'; } MyString::MyString(std::string s) { strlength = (int)s.length(); p = new char[strlength + 1]; memcpy(p, s.c_str(), strlength); p[strlength] = \'\\0\'; } MyString& MyString::operator=(const MyString& a) { if(p) delete p; strlength = a.strlength; p = new char[strlength + 1]; memcpy(p, a.p, strlength); p[strlength] = \'\\0\'; return *this; } MyString& MyString::operator+= (const MyString& str) { *this = *this + str; return *this; } istream& operator>> (istream& in, MyString& str) { char* tmp = new char[1024]; in >> tmp; str.strlength = strlen(tmp); if (str.p) delete str.p; str.p = new char[str.strlength + 1]; memcpy(str.p, tmp, str.strlength); str.p[str.strlength] = \'\\0\'; delete tmp; return in; } ostream& operator<<(ostream& o, const MyString& str) { o << str.p; return o; } MyString operator+ (const MyString& str1, const MyString& str2) //friend function { MyString ret; ret.strlength = str1.strlength + str2.strlength; if (ret.p) delete ret.p; ret.p = new char[ret.strlength + 1]; memcpy(ret.p, str1.p, str1.strlength); memcpy(ret.p + str1.strlength, str2.p, str2.strlength); ret.p[ret.strlength] = \'\\0\'; return ret; } bool operator== (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) == 0; } bool operator!= (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) != 0; } bool operator< (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) < 0; } bool operator<= (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) <= 0; } bool operator> (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) > 0; } bool operator>= (const MyString& str1, const MyString& str2) { return strcmp(str1.p, str2.p) >= 0; } char& MyString::operator[] (const int i) { assert(i >= 0 && i <= strlength); return p[i]; } const char& MyString::operator[] (const int i)const { assert(i >= 0 && i <= strlength); return p[i]; } MyString MyString::substr(int start, int n) { assert(start + n <= strlength); MyString ret; ret.strlength = n; if (ret.p) delete ret.p; ret.p = new char[n + 1]; memcpy(ret.p, p + start, n); ret.p[n] = \'\\0\'; return ret; } MyString& MyString::append(const MyString& str) { *this += str; return *this; } MyString& MyString::insert(int pos, const MyString& str) { assert(pos >= 0 && pos <= strlength); strlength += str.length(); char* tmp = new char[strlength + 1]; memcpy(tmp, p, pos); memcpy(tmp + pos, str.p, str.length()); memcpy(tmp + pos + str.length(), p + pos, length() - pos); tmp[strlength] = \'\\0\'; if (p) delete p; p = new char[strlength + 1]; memcpy(p, tmp, strlength + 1); delete tmp; return *this; } MyString& MyString::assign(int pos, const MyString& str, int len) { assert(pos >= 0 && pos <= strlength); if (len > str.length() || len < 0) len = str.length(); strlength = pos + len; char* tmp = new char[strlength + 1]; memcpy(tmp, p, pos); memcpy(tmp + pos, str.p, len); tmp[strlength] = \'\\0\'; if (p) delete p; p = new char[strlength + 1]; memcpy(p, tmp, strlength + 1); delete tmp; return *this; } MyString& MyString::erase(int pos, int len) { assert(pos >= 0 && pos <= strlength); if (pos + len > strlength || len < 0) len = strlength - pos; strlength -= len; char* tmp = new char[strlength + 1]; memcpy(tmp, p, pos); memcpy(tmp + pos, p + pos + len, strlength - pos); tmp[strlength] = \'\\0\'; if (p) delete p; p = new char[strlength + 1]; memcpy(p, tmp, strlength + 1); delete tmp; return *this; } MyString& MyString::clear() { strlength = 0; if (p) delete p; p = new char[strlength + 1]; p[strlength] = \'\\0\'; return *this; }
test.cpp
#include "pch.h" #include <iostream> #include "MyString.h" using namespace std; int main() { _CrtSetDbgFlag(_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF); MyString s1; MyString s2("qwer"); MyString s3(s2); MyString s4(std::string("1234")); cout << "s1:\\t" << s1 << "\\ts1.length():\\t" << s1.length() << "\\ts1.empty():\\t" << s1.empty() << endl; cout << "s2:\\t" << s2 << "\\以上是关于造轮子:新建一个属于自己的String类的主要内容,如果未能解决你的问题,请参考以下文章
「从零开始造 RPC 轮子系列」01 我为什么要去造一个轮子?
「从零开始造 RPC 轮子系列」01 我为什么要去造一个轮子?
「从零开始造 RPC 轮子系列」01 我为什么要去造一个轮子?