学完c以后刚开始学c++,然后遇见了string,就有点好奇它的内部实现,在c里面都是用char型的数组来表示字符串的,而c++有了string来表示字符串, 我就想在c里面字符串以’\0’结尾,那么我是不是可以用’\0’给string的对象初始化,结果编译通过了,但是执行的时候出问题了
文章图片
然后我产生了一个邪恶的想法,既然’\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函数,我在调试时发现程序死在strlen
文章图片
然后我就想如果一个指针指向’\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*
【c++|c++的string类的模拟实现 以及basic_string::_M_construct null not valid错误的原因】printf(“谢谢观看233!”);
推荐阅读
- C++|[C/C++]详解STL容器1--string的功能和模拟实现(深浅拷贝问题)
- c++|模拟实现string类
- RBE104TC 分析
- python|来聊聊SourceMap
- 简单搜索|Smallest Difference POJ-2718(全排列)
- 初识C语言|初识C语言(指针)
- CSCI1120
- QT|QT-数据可视化大屏1
- CSCI1120 游戏Chess