造轮子:新建一个属于自己的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 << "\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;
}

原文地址:https://www.cnblogs.com/Sseakompp/p/12108671.html

时间: 2024-07-31 19:25:10

造轮子:新建一个属于自己的String类的相关文章

1——自我实现一个简洁版的String类

在C++中有C没有的string字符串类型,string类型的数据其实是一个指向字符串首地址的指针变量,因此在string类的默认成员函数拷贝构造和赋值运算符的重载就会涉及到深浅拷贝的问题,一不小心要么就是内存泄露要么就是多次释放同一块空间导致程序崩溃,下面就来模拟实现一个简洁版的String类: 既然是指向一个字符串的指针,因此类的成员变量就需要有一个char*类型的指针: #include <iostream> #include <string.h> using namespa

一个简单实现的string类

为了复习c++知识,简单的实现一个string类,类名为CMyString 环境说明:windows 7 64位 开发工具:Visual Studio 2015 CMyString类的头文件CMyString.h 1 #include <iostream> 2 3 #ifndef __C_MY_STRING__ 4 #define __C_MY_STRING__ 5 6 class CMyString 7 { 8 public: 9 //默认构造函数 10 CMyString(); 11 //

实现一个简洁版的String类

浅拷贝: 深拷贝: 注意事项:在浅拷贝中复制构造必须构造一个匿名对象在进行交换,在赋值操作符中传参时不能使用引用.

实现以一个简洁版的string类

浅拷贝: 分析:如果在赋值运算时不使用引用会造成程序崩溃,在调用复制构造时是s1的_str会出现一个随机值,本来应该是被析构过的应为空,所以在测试时出了作用域再去析构时就会出现错误.

自己动手写Java String类

很早想造个轮子了,听说不想造轮子的程序员不是好程序员,用惯了Apache的众多开源项目,却没看过开源一句代码.最近看了几篇卖焦虑的文章之后,突然变得慌张起来,于是在百忙之中难得一点闲第一次打开了JDK的源码包.初读:晦涩难懂,3千多行代码,全英文注释,有一些陌生的语法和调用陌生的类足矣吓退一个中年程序员.再度:只读个轮廓,这String.class也就是注释写的全写得多,核心代码不多嘛!三读:哇喔,整个String类核心变量就一个char[]数组嘛: private final char val

黑马程序员——String类

------<a href="http://www.itheima.com" target="blank">Java培训.Android培训.iOS培训..Net培训</a>.期待与您交流! ------- String类 (一). 认识String 字符串是一个特殊的对象,一旦初始化就不可改变. 有两种方式定义一个字符串对象: (1)String str = “abc”; //它在执行时是在字符串常量池中创建一个对象,并把地址赋给引用型变量

JAVA API(一)String类和StringBuffer类

1.String类和StringBuffer类 在程序中经常会用到字符串,所谓的字符串就是指一连串的字符,它是由多个单个字符连接而成的.字符串中可以包含任意字符,这些字符必须包含在一对双引号""之内,如"abc".在Java中定义了String和StringBuffer类来封装字符串,并提供了一系列操作字符串的方法,它们都位于java.lang包中,因此不需要导包就可以直接使用.下面将对String类和StringBuffer类详细讲解. 1.1String类的初始

String类常用方法。

一,字符数组与字符串. 一个字符串可以变成一个字符数组,同样,一个字符数组可以变成一个字符串. 在String类中提供了以下操作方法. 1)将字符串变成字符数组:public char[] toCharArray(); 2)字符数组变成字符串:public String(char [] value) public String(char[] value,int offset,int count);//表示从offset位置开始的count个字符,组合成为一个字符串. public class S

C++ string类及其函数的讲解

文章来源于:http://www.cnblogs.com/hailexuexi/archive/2012/02/01/2334183.html C++中string是标准库中一种容器,相当于保存元素类型为char的vector容器(自己理解),这个类提供了相当丰富的函数来完成对字符串操作,以及与C风格字符串之间转换,下面是对string一些总结<引用> 一,C语言的字符串 在C语言里,对字符串的处理一项都是一件比较痛苦的事情,因为通常在实现字符串的操作的时候都会用到最不容易驾驭的类型——指针.