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

学完c以后刚开始学c++,然后遇见了string,就有点好奇它的内部实现,在c里面都是用char型的数组来表示字符串的,而c++有了string来表示字符串, 我就想在c里面字符串以’\0’结尾,那么我是不是可以用’\0’给string的对象初始化,结果编译通过了,但是执行的时候出问题了c++|c++的string类的模拟实现 以及basic_string::_M_construct null not valid错误的原因
文章图片

然后我产生了一个邪恶的想法,既然’\0’编译通过了,那我直接赋’A’这样的单字符看编译器会怎么样,结果是我傻了,编译器直接告诉我’A’不是char*类型不匹配
接着我就纳闷了,遇事不决就百度,然后复制以上错误找了半天都没有找到满意的答案,就决定来模拟实现一下string
于是我就想,这个string内部应该是动态分配空间来的
然后我就定义了一个类

#include #include #include //为了使用 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函数,我在调试时发现程序死在strlenc++|c++的string类的模拟实现 以及basic_string::_M_construct null not valid错误的原因
文章图片

然后我就想如果一个指针指向’\0’,那么它的地址是什么,因为我在初始对象传入的是char型的指针嘛,而我如果传’A’这样的单字符,编译器是会直接报错的,因为类型不匹配,而传’\0’却可以,
于是
c++|c++的string类的模拟实现 以及basic_string::_M_construct null not valid错误的原因
文章图片

我终于发现了问题的所在,原来’\0’的地址就是nullptr
对strlen传入一个NULL指针,会由于NULL指向的空间不可被访问,而导致程序死掉,于是我发现我的InitStr方法没有对参数进行判断,然后马上改正,修改成了以上代码那样。
然后在给我自己写的类的对象赋值成’\0’
然后
c++|c++的string类的模拟实现 以及basic_string::_M_construct null not valid错误的原因
文章图片

发现在代码
c++|c++的string类的模拟实现 以及basic_string::_M_construct null not valid错误的原因
文章图片

14行处跳出
然后我就明白了
terminate called after throwing an instance of ‘std::logic_error’
what(): basic_string::_M_construct null not valid

错误的意思
可喜可贺~我的问题就那么解决了
这个类在申请堆区的空间的时候应该与string不一样,由此可能存在一些效率问题,因为我只是想探究一下这个错误,所以还有一些功能没有实现,请大佬轻喷ヾ(≧▽≦)o*
【c++|c++的string类的模拟实现 以及basic_string::_M_construct null not valid错误的原因】printf(“谢谢观看233!”);

    推荐阅读