造*:新建一个属于自己的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窗口看到输出如下:

造*:新建一个属于自己的String类

发现了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;
}

重新运行,程序不崩溃了但是报了内存泄漏

造*:新建一个属于自己的String类

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;
}

重新运行

造*:新建一个属于自己的String类

这回没有内存泄漏了,继续往下编辑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;

造*:新建一个属于自己的String类

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;

造*:新建一个属于自己的String类

 

然后就可以用+和=完成+=操作符 

MyString& MyString::operator+= (const MyString& str)
{
    *this = *this + str;
    return *this;
}

 测试一下

    MyString helloworld = hello;
    helloworld += world;
    cout << "test operator+=\t" << helloworld << endl;

造*:新建一个属于自己的String类

 

下标运算符

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;
}

测试

造*:新建一个属于自己的String类

 

获得子串(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;
}

测试一堆:

造*:新建一个属于自己的String类

 

测试完成,暂时完成了造*的过程~

 

完整代码如下:

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 << "\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;
}
上一篇:字符串封装


下一篇:C++ | 基于char*设计一个字符串类MyString