c++的string类的模拟实现 以及basic_string::_M_construct null not valid错误的原因

博主学完C后学C++,对string内部实现好奇。尝试用'\0'和'A'初始化string对象,前者编译通过执行出错,后者类型不匹配。通过模拟实现string类,发现问题出在InitStr方法未对参数判断,传入NULL指针致程序死掉,最终解决了错误。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

学完c以后刚开始学c++,然后遇见了string,就有点好奇它的内部实现,在c里面都是用char型的数组来表示字符串的,而c++有了string来表示字符串, 我就想在c里面字符串以’\0’结尾,那么我是不是可以用’\0’给string的对象初始化,结果编译通过了,但是执行的时候出问题了执行出错
然后我产生了一个邪恶的想法,既然’\0’编译通过了,那我直接赋’A’这样的单字符看编译器会怎么样,结果是我傻了,编译器直接告诉我’A’不是char*类型不匹配
接着我就纳闷了,遇事不决就百度,然后复制以上错误找了半天都没有找到满意的答案,就决定来模拟实现一下string
于是我就想,这个string内部应该是动态分配空间来的
然后我就定义了一个类

#include <iostream>
#include <cstring>	
#include <cstdlib>	//为了使用 exit函数 以及system函数
using namespace std;
class HString
{
    public:
        HString();
        HString(const char *);
        /** 打印字符串 */
        int StrPrint();
        /** 返回字符串长度 */
        int GetLength()
        {
            return m_length;
        }
        /** 重新赋值 */
        int ReSet(const char *);
        /** 重载=赋值 防止内存泄漏*/
        const HString &operator=(const HString &);
        /** 重载运算符= 使可以用字符串直接赋值 */
        const HString &operator=(const char *r_str)
        {
            this->ReSet(r_str);
            return *this;
        }
        virtual ~HString();

    protected:

    private:
        char *m_str;    //字符串 动态分配
        int m_length;   //字符串长度
        int m_size;     //当前申请空间的大小
        void InitStr(const char *);//初始化字符串
};

以下是类的实现

#include "HString.h"

HString::HString()
{
    m_str = nullptr;
    m_length = 0;
    m_size = 0;
}

void HString::InitStr(const char *initStr)
{
    if(nullptr == initStr)
    {
        cout << "HString::InitStr(const char *): initialization failed by " << __FILE__ << __LINE__ << endl;
        exit(4);
    }
    int len = strlen(initStr);
    m_length = 0;
    m_size = 0;
    m_str = new char[len + 1];
    if(nullptr != m_str)
    {
        m_length = len;
        m_size = len + 1;
        memset(m_str, 0, m_size);
        memcpy(m_str, initStr, m_size);
    }
}

HString::HString(const char *initStr)
{
    InitStr(initStr);
}

/** 重新赋值 */
int HString::ReSet(const char *p_str)
{
    if(nullptr == p_str)
    {
        cout << "HString::ReSet :The parameter is incorrect" << endl;
        return 1;
    }
    int len = strlen(p_str);
    if(len < 0)
    {
        cout << "HString::ReSet :get parameter is invailed" << endl;
        return 2;
    }
    if(len < m_size - 1) //如果在分配的字符串小于当前分配空间长度 就直接赋值
    {
        memset(m_str, 0, m_size);
        memcpy(m_str, p_str, len + 1);
        m_length = len;
    }
    else                //否则 空间再分配
    {
        if(nullptr == m_str) //如果对象字符串没有空间
        {
            InitStr(p_str); //进行初始化
        }
        else
        {
            char *p_temp = new char[len + 1];
            if(nullptr == p_temp)
            {
                cout << "relloc failed!" << endl;
                return 3;
            }
            else
            {
                memset(p_temp, 0, m_size);

                delete m_str;
                m_str = p_temp;
                p_temp = nullptr;
                m_size = len + 1;
                m_length = len;
                memcpy(m_str, p_str, m_size);
            }
        }

    }
    return 0;
}

/** 重载=赋值 防止内存泄漏*/
const HString & HString::operator=(const HString &other)
{
	if(this != &other)	//如果传入的不是自身
	{
		if(other.m_size <= this->m_size)
	    {
	        memset(this->m_str, 0, this->m_size);
	        memcpy(this->m_str, other.m_str, other.m_size);
	        this->m_length = other.m_length;
	    }
	    else
	    {
	        if(nullptr != this->m_str)
	        {
	            delete this->m_str;
	        }
	        this->m_str = new char[other.m_size];
	        memcpy(this->m_str, other.m_str, other.m_size);
	        this->m_length = other.m_length;
	        this->m_size = other.m_size;
	    }
	}
	   
    return *this;
}



int HString::StrPrint()
{
    if(0 == m_length || nullptr == m_str)
    {
        cout << "NULL" << endl;
        return 1;
    }
    cout << m_str << endl;
    return 0;
}

HString::~HString()
{
    if(nullptr != m_str)
    {
        delete m_str;
        m_str = nullptr;
    }
}

在尽我所能的模拟string的过程中,我发现了问题的所在,由于要实现重新赋值,所以一定要记录字符串长度,为了不内存泄漏以及空间的合法使用,就要记录当前申请空间的大小,量字符串长度要用到strlen函数,我在调试时发现程序死在strlenstrlen('\0')
然后我就想如果一个指针指向’\0’,那么它的地址是什么,因为我在初始对象传入的是char型的指针嘛,而我如果传’A’这样的单字符,编译器是会直接报错的,因为类型不匹配,而传’\0’却可以,
于是

欸嘿嘿
我终于发现了问题的所在,原来’\0’的地址就是nullptr
对strlen传入一个NULL指针,会由于NULL指向的空间不可被访问,而导致程序死掉,于是我发现我的InitStr方法没有对参数进行判断,然后马上改正,修改成了以上代码那样。
然后在给我自己写的类的对象赋值成’\0’
然后

找到错误的地方
发现在代码
这里跳出
14行处跳出
然后我就明白了
terminate called after throwing an instance of ‘std::logic_error’
what(): basic_string::_M_construct null not valid

错误的意思
可喜可贺~我的问题就那么解决了
这个类在申请堆区的空间的时候应该与string不一样,由此可能存在一些效率问题,因为我只是想探究一下这个错误,所以还有一些功能没有实现,请大佬轻喷ヾ(≧▽≦)o*

printf(“谢谢观看233!”);

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值