练习造轮子,新建一个属于自己的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#include 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#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#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 << "\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; MyString s6 = s2; cout << "s6:\t" << s6 << "\ts6.length():\t" << s6.length() << "\ts6.empty():\t" << s6.empty() << endl; MyString hello("hello"); MyString world(std::string(", world!")); cout << "test operator+\t" << (hello + world) << endl; MyString helloworld = hello; helloworld += world; cout << "test operator+=\t" << helloworld << endl; cout << "test []\t" << helloworld[4] << endl; MyString strA("A"); MyString strAB("AB"); cout << "test ==\t" << (strA == strAB) << endl; cout << "test !=\t" << (strA != strAB) << endl; cout << "test <\t" << (strA < strAB) << endl; cout << "test <=\t" << (strA <= strAB) << endl; cout << "test >\t" << (strA > strAB) << endl; cout << "test >=\t" << (strA >= strAB) << endl; cout << "test substr\t" << helloworld.substr(2, 3) << endl; helloworld.append(MyString("append!")); cout << "test append\t" << helloworld << endl; helloworld.insert(6, MyString("insert!")); cout << "test insert\t" << helloworld << endl; helloworld.assign(6, MyString("assign"), 5); cout << "test assign\t" << helloworld << endl; helloworld.erase(6, 4); cout << "test erase\t" << helloworld << endl; helloworld.clear(); cout << "test clear\t" << helloworld << "\thelloworld.empty()\t" << helloworld.empty() << endl; return 0; }