堆分配存储串

string类型是天天用的玩意,但是自己实现时,用顺序存储还是链式存储更合适呢?

《数据结构》中介绍了三种方式:

1,定长顺序存储

    长度有限,貌似没大用处

3,块链存储

数组和链表的一种混用形式,原本直觉上认为这种方式较合理,但仔细思考,发现其便利性也仅体现在连接了。综合来说不如第2种。

2,堆分配存储

    这是今天要实现的方式。

    方法的选择上,理论上好歹也应该重载下加号运算符什么的,但运算符重载作为C++的专题留到以后再说。现在仅关心数据结构,实现部分教材中列出的方法。

串的长度直接记录下来,结尾没有补0作结束符。

效率上当然没法和STL提供的相比,仅是意思下练练手罢。


一开始还有种想法,string这玩意太平常,了解下干脆别浪费时间自己实现了,结果粗大事了。事实证明不动手还是不行啊。


以下是一份错误的代码:

#include <iostream>
using std::cout;
using std::endl;

class MyString
{
public:
    char *ch;
    int length;

    MyString();
    ~MyString();

    //给串赋字符串常量值
    void StrAssign(char *chars);

    //按序比较,>T返回值>0,= =,< <
    int StrCompare(MyString T);

    //S = S + S2;
    void Concat(MyString S2);

    //返回从pos起len长度的子串
    MyString SubString(int pos, int len);

    void Show();
};

//初始化为空串
MyString::MyString()
{
    ch = NULL;
    length = 0;
}

MyString::~MyString()
{
    delete ch;
    ch = NULL;
}

void MyString::StrAssign(char *chars)
{
    //不管三七二十一,先释放掉原有数据
    if (ch)
    {
        delete ch;
        ch = NULL;
    }
    
    //计算chars长度
    int i=0;
    while(chars[i] != NULL)
    {
        i++;
    }

    if (0 == i)     //注意若所赋串为空,length需归0
    {
        ch = NULL;
        length = 0;
    }
    else
    {
        ch = new char[i]();
        for(int j=0; j<i; ++j)
        {
            ch[j] = chars[j];
        }
        length = i;
    }
}

int MyString::StrCompare(MyString T)
{
    for(int i=0; (i<length) && (i<T.length); ++i)
    {
        if (ch[i] > (T.ch)[i])
        {
            return 1;
        }
        else if (ch[i] < (T.ch)[i])
        {
            return -1;
        } 
    }

    return length - T.length;
}

void MyString::Concat(MyString S2)
{
    //为空直接赋值
    if (ch == NULL)
    {
        ch = S2.ch;
        length = S2.length;
    }
    else
    {
        //备份原串
        char *tmp = new char[length]();
        for(int i=0; i<length; ++i)
        {
            tmp[i] = ch[i];
        }

        length = length + S2.length;

        delete ch;
        ch = new char[length]();

        int i = 0;
        //复制原串
        for(; i<(length - S2.length); ++i)
        {
            ch[i] = tmp[i];
        }
        delete tmp;
        tmp = NULL;

        //复制S2
        for(; i<length; ++i)
        {
            ch[i] = (S2.ch)[i - length + S2.length];
        }
    }
}

MyString MyString::SubString(int pos, int len)
{
    MyString result;
    if ((pos<=0)||(pos>length)||(len<0)||(len>length-pos+1))
    {
        cout<<"invalid param or empty String"<<endl;
        result.ch = NULL;
        result.length = 0;
        return result;
    }

    if (0 == len)
    {
        result.ch = NULL;
        result.length = 0;
    }
    else
    {
        result.ch = new char[len]();
        for(int i=0; i<len; ++i)
        {
            result.ch[i] = ch[i+pos-1]; //注意此处要-1
        }

        result.length = len;
    }

    return result;
}

void MyString::Show()
{
    if (NULL == ch)
    {
        cout<<"the string is empty"<<endl;
    }
    else
    {
         for(int i=0; i<length; ++i)
        {
            cout<<ch[i];
        }
        cout<<endl;
    }
}

int main()
{
    MyString s1;             s1.Show();
    s1.StrAssign("hello");   s1.Show();
    MyString s2;
    s2.StrAssign("abc");     s2.Show();
    
    if (s1.StrCompare(s2)>0)
    {
        cout<<"s1>s2"<<endl;
    }
    else if(s1.StrCompare(s2)<0)
    {
        cout<<"s1<s2"<<endl;
    }

    s1.Concat(s2);          s1.Show();

    s2.SubString(4,2);
    s2.SubString(2,2).Show();
}


倘若执行,会弹出一个我们熟悉的错误提示框——Debug Assertion Failed!

然后运行结果如下:

the string is empty

hello

abc

s1>s2


我现在刻意的练习C朝C++风格的转换,抛弃struct用class,暴露出了一些漏洞。很好。

为什么出错呢?原因是这样的:

由于我潜意识里希望作为参数传递进各方法的MyString对象不要被修改,而又避免使用对我来说较生疏的const等关键字,所以我采用了直接传递对象的方法,甚至连指针都懒得用。结果就反而弄出问题了!

注意,假如此时将StrCompare()的测试语句注释掉,会发现后面的Concat()是正常的。那明明Compare只是读取了字符作了一些比较,根本没作修改动作,为何还会影响到后面的语句呢?

秘密在于,我将s2作为参数传递进Compare()时,s2是被复制了一份的,当然ch及length都被默认的复制构造函数一一复制进去。当然这些都在预料之中没什么问题。

但是要注意!复制的这份形参,退出函数时,是要被处理的!比如这里的MyString,退出Compare()时是要被析构的!而我之前为了避免内存泄露,专门为MyString的析构函数编写了释放指针的语句。所以导致,形参被析构时,把原本被复制的s2也连累了,s2的指针被delete,资源就不见了。


所以假如把析构函数的语句注释掉,什么都不做,输出的结果就会变正常,不过这种解决方法不好,留下内存泄露的问题。


所以该用const的地方还是得用,而即便仅仅采用引用传递的方式也许反而不会产生问题。


然后我将代码的所有值传递修改为引用传递,运行后,前面部分均正常,SubString()仍报错。

原因也很简单,我很2B的在SubString()中定义了局部变量result,然后返回这个result,但是result是要被析构的啊,于是返回值就没法用了。


修改代码如下后,一切正常了。

/*
2014.4.18
堆分配存储串
*/

#include <iostream>
using std::cout;
using std::endl;

class MyString
{
public:
    char *ch;
    int length;

    MyString();
    ~MyString();

    //给串赋字符串常量值
    void StrAssign(char *chars);

    //按序比较,>T返回值>0,= =,< <
    int StrCompare(MyString *T);

    //S = S + S2;
    void Concat(MyString *S2);

    //返回从pos起len长度的子串
    void SubString(MyString *result, int pos, int len);

    void Show();
};

//初始化为空串
MyString::MyString()
{
    ch = NULL;
    length = 0;
}

MyString::~MyString()
{
    delete ch;
    ch = NULL;
}

void MyString::StrAssign(char *chars)
{
    //不管三七二十一,先释放掉原有数据
    if (ch)
    {
        delete ch;
        ch = NULL;
    }
    
    //计算chars长度
    int i=0;
    while(chars[i] != NULL)
    {
        i++;
    }

    if (0 == i)     //注意若所赋串为空,length需归0
    {
        ch = NULL;
        length = 0;
    }
    else
    {
        ch = new char[i]();
        for(int j=0; j<i; ++j)
        {
            ch[j] = chars[j];
        }
        length = i;
    }
}

int MyString::StrCompare(MyString *T)
{
    for(int i=0; (i<length) && (i<T->length); ++i)
    {
        if (ch[i] > (T->ch)[i])
        {
            return 1;
        }
        else if (ch[i] < (T->ch)[i])
        {
            return -1;
        } 
    }

    return length - T->length;
}

void MyString::Concat(MyString *S2)
{
    //为空直接赋值
    if (ch == NULL)
    {
        ch = S2->ch;
        length = S2->length;
    }
    else
    {
        //备份原串
        char *tmp = new char[length]();
        for(int i=0; i<length; ++i)
        {
            tmp[i] = ch[i];
        }

        length = length + S2->length;

        delete ch;
        ch = new char[length]();

        int i = 0;
        //复制原串
        for(; i<(length - S2->length); ++i)
        {
            ch[i] = tmp[i];
        }
        delete tmp;
        tmp = NULL;

        //复制S2
        for(; i<length; ++i)
        {
            ch[i] = (S2->ch)[i - length + S2->length];
        }
    }
}

void MyString::SubString(MyString *result, int pos, int len)
{
    if ((pos<=0)||(pos>length)||(len<0)||(len>length-pos+1))
    {
        cout<<"invalid param or empty String"<<endl;
        result->ch = NULL;
        result->length = 0;
    }

    if (0 == len)
    {
        result->ch = NULL;
        result->length = 0;
    }
    else
    {
        result->ch = new char[len]();
        for(int i=0; i<len; ++i)
        {
            result->ch[i] = ch[i+pos-1]; //注意此处要-1
        }

        result->length = len;
    }
}

void MyString::Show()
{
    if (NULL == ch)
    {
        cout<<"the string is empty"<<endl;
    }
    else
    {
         for(int i=0; i<length; ++i)
        {
            cout<<ch[i];
        }
        cout<<endl;
    }
}

int main()
{
    MyString s1;             s1.Show();
    s1.StrAssign("hello");   s1.Show();
    MyString s2;
    s2.StrAssign("abc");     s2.Show();
    
    if (s1.StrCompare(&s2)>0)
    {
        cout<<"s1>s2"<<endl;
    }
    else if(s1.StrCompare(&s2)<0)
    {
        cout<<"s1<s2"<<endl;
    }

    s1.Concat(&s2);          s1.Show();

    MyString result;
    s2.SubString(&result, 4, 2);
    s2.SubString(&result, 2, 2);
    result.Show();
}

/*
运行结果:
the string is empty
hello
abc
s1>s2
helloabc
invalid param or empty String
bc

注意点:
1,字符串比较过程的逻辑关系一定要想清楚
*/

当然,要避免参数值被改动,仅靠个人思维推断是不规范的,最好还是加上const修饰符。

另外把属性改回了private,传指针改成了传引用,最终的代码如下:

/*
2014.4.18
堆分配存储串
*/

#include <iostream>
using std::cout;
using std::endl;

class MyString
{
private:
    char *ch;
    int length;

public:
    MyString();
    ~MyString();

    //给串赋字符串常量值
    void StrAssign(const char *chars);

    //按序比较,>T返回值>0,= =,< <
    int StrCompare(const MyString &T);

    //S = S + S2;
    void Concat(const MyString &S2);

    //返回从pos起len长度的子串
    void SubString(MyString *result, int pos, int len) const;

    void Show();
};

//初始化为空串
MyString::MyString()
{
    ch = NULL;
    length = 0;
}

MyString::~MyString()
{
    delete ch;
    ch = NULL;
}

void MyString::StrAssign(const char *chars)
{
    //不管三七二十一,先释放掉原有数据
    if (ch)
    {
        delete ch;
        ch = NULL;
    }
    
    //计算chars长度
    int i=0;
    while(chars[i] != NULL)
    {
        i++;
    }

    if (0 == i)     //注意若所赋串为空,length需归0
    {
        ch = NULL;
        length = 0;
    }
    else
    {
        ch = new char[i]();
        for(int j=0; j<i; ++j)
        {
            ch[j] = chars[j];
        }
        length = i;
    }
}

int MyString::StrCompare(const MyString &T)
{
    for(int i=0; (i<length) && (i<T.length); ++i)
    {
        if (ch[i] > (T.ch)[i])
        {
            return 1;
        }
        else if (ch[i] < (T.ch)[i])
        {
            return -1;
        } 
    }

    return length - T.length;
}

void MyString::Concat(const MyString &S2)
{
    //为空直接赋值
    if (ch == NULL)
    {
        ch = S2.ch;
        length = S2.length;
    }
    else
    {
        //备份原串
        char *tmp = new char[length]();
        for(int i=0; i<length; ++i)
        {
            tmp[i] = ch[i];
        }

        length = length + S2.length;

        delete ch;
        ch = new char[length]();

        int i = 0;
        //复制原串
        for(; i<(length - S2.length); ++i)
        {
            ch[i] = tmp[i];
        }
        delete tmp;
        tmp = NULL;

        //复制S2
        for(; i<length; ++i)
        {
            ch[i] = (S2.ch)[i - length + S2.length];
        }
    }
}

void MyString::SubString(MyString *result, int pos, int len) const
{
    if ((pos<=0)||(pos>length)||(len<0)||(len>length-pos+1))
    {
        cout<<"invalid param or empty String"<<endl;
        result->ch = NULL;
        result->length = 0;
    }

    if (0 == len)
    {
        result->ch = NULL;
        result->length = 0;
    }
    else
    {
        result->ch = new char[len]();
        for(int i=0; i<len; ++i)
        {
            result->ch[i] = ch[i+pos-1]; //注意此处要-1
        }

        result->length = len;
    }
}

void MyString::Show()
{
    if (NULL == ch)
    {
        cout<<"the string is empty"<<endl;
    }
    else
    {
         for(int i=0; i<length; ++i)
        {
            cout<<ch[i];
        }
        cout<<endl;
    }
}

int main()
{
    MyString s1;             s1.Show();
    s1.StrAssign("hello");   s1.Show();
    MyString s2;
    s2.StrAssign("abc");     s2.Show();
    
    if (s1.StrCompare(s2)>0)
    {
        cout<<"s1>s2"<<endl;
    }
    else if(s1.StrCompare(s2)<0)
    {
        cout<<"s1<s2"<<endl;
    }

    s1.Concat(s2);          s1.Show();

    MyString result;  
    s2.SubString(&result, 4, 2);
    s2.SubString(&result, 2, 2);
    result.Show();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值