我在cpp中编写了一个简单的字符串类,但是当我添加一些新功能时,发生了一些不好的事情。当调用对象 s3 析构函数时,堆栈溢出。所以我花了一个小时来修复它,但我没有发现我的代码在本地出错,而且语法是正确的。因此,我要求我的朋友重新编码以帮助我修复它,但是当他在Mac上重新编码时。他的计划中没有发生任何不好的事情。所以我真的很担心它并且需要帮助解决它,如果我的代码真的有问题。如果没有,也许我应该对问题Visual Studio 2015发表评论。:(
这是MySTLString.h
class MySTLString
{
public:
// Default constructor
MySTLString();
// Constructs the string with count copies of character ch.
MySTLString(size_t count, char ch);
// Convert char consequence to MySTLString
MySTLString(const char *s);
// Copy constructor
MySTLString(const MySTLString &s);
// Destructor
~MySTLString();
using size_type = size_t;
using CharT = char; // the character type
using value_type = char; // the value_type
using reference = value_type; // reference
using const_reference = const value_type&; // const reference
// Returns reference to the character at specified location pos.
reference at(size_type pos);
// const_reference at(size_type pos) const; // I do not know how to convert a reference to const_reference when to return
// Returns reference to the first character
CharT& front();
const CharT& front() const;
// Returns reference to the last character, equivalent to operator[](size() - 1)
CharT& back();
const CharT& back() const;
// Returns pointer to the underlying array serving as character storage.
CharT* data();
const CharT* data() const;
// Operator assignment overloaded
MySTLString& operator=(const MySTLString &s);
// Operator [] overloaded
reference operator[](size_type pos);
const_reference operator[](size_type pos) const;
private:
char* data_;
int length_;
};
这是MySTLString.cpp
#include "MySTLString.h"
#include <cstring>
#include <iostream>
#include <stdexcept>
// Construct a empty string(zero size and unspecified capacity).
MySTLString::MySTLString():data_(nullptr), length_(0)
{
}
// Constructs the string with count copies of character ch.
MySTLString::MySTLString(size_t count, char ch)
{
length_ = count;
if (count == 0)
{
data_ = nullptr;
return;
}
else // when count is not 0
{
data_ = new char[length_];
for (size_t i = 0; i < count; ++i)
{
data_[i] = ch;
}
}
}
// Constructs the string with contents initialized
// with a copy of the null-terminated character string pointed to by s.
// The length of the string is determined by the first null character.
MySTLString::MySTLString(const char *s)
{
length_ = strlen(s);
if (length_ == 0)
{
data_ = nullptr;
return;
}
data_ = new char[length_];
strcpy(data_, s);
}
// Copy constructor.
// Constructs the string with the copy of the contents of other.
MySTLString::MySTLString(const MySTLString &s)
{
if (s.data_ == nullptr)
{
length_ = 0;
data_ = nullptr;
}
else
{
length_ = strlen(s.data_);
data_ = new char[length_];
strcpy(data_, s.data_);
}
}
// Destructor
// Free data_ pointer memory
MySTLString::~MySTLString()
{
if (data_ != nullptr)
{
delete []data_;
}
std::cout << "length_ = " << length_ << std::endl; // for test
}
// Returns a reference to the character at specified location pos.
// Bounds checking is performed, exception of type std::out_of_range will be thrown on invalid acess
MySTLString::reference MySTLString::at(size_type pos)
{
if (pos >= strlen(data_))
{
throw std::out_of_range("pos is cross-border!\n");
}
return data_[pos];
}
// Returns reference to the first character
MySTLString::CharT& MySTLString::front()
{
if (data_ == nullptr)
{
throw std::out_of_range("String is empty!\n");
}
return data_[0];
}
const MySTLString::CharT& MySTLString::front() const
{
return this->front();
}
// Returns reference to the last character
MySTLString::CharT& MySTLString::back()
{
if (data_ == nullptr)
{
throw std::out_of_range("String is empty!\n");
}
return data_[0];
}
const MySTLString::CharT& MySTLString::back() const
{
return this->back();
}
// Returns pointer to the underlying array serving as character storage.
// The pointer is such that the range[data(); data()+strlen(data_)] is valid
// in it correspond to the values stored in the string
MySTLString::CharT* MySTLString::data()
{
if (data_ == nullptr)
{
throw std::out_of_range("String is empty!\n");
}
return data_;
}
const MySTLString::CharT* MySTLString::data() const
{
return this->data();
}
// Operator= overloaded
// Replace the contents with a copy of str.
// If *this and str are the same object, this function has no effect
MySTLString& MySTLString::operator=(const MySTLString &s)
{
// If *this and str are the same object, this function return *this
if (this == &s)
{
return *this;
}
if (s.length_ == 0)
{
length_ = 0;
data_ = nullptr;
return *this;
}
char* temp = s.data_; // copy *s.data_
delete data_; // free old memory
data_ = new char[s.length_]; // copy data to data_ member
strcpy(data_, temp);
length_ = s.length_;
return *this; // return this object
}
// Operator[] overloaded
// Returns a reference to the character at specified location pos.
// No bounds checking is perfromed.
MySTLString::reference MySTLString::operator[](size_type pos)
{
return this->at(pos);
}
MySTLString::const_reference MySTLString::operator[](size_type pos) const
{
return this->operator[](pos);
}
这是TestMySTLString.cpp(PS:我已删除其他正常功能)
#include "MySTLString.h"
#include <iostream>
using std::cout;
using std::endl;
int main(void)
{
// test constructor that convert char consequence to MySTLString
MySTLString s3("qwe");
return 0;
}
这是调用析构函数的图片
这是调用析构函数时的堆栈信息图片
答案 0 :(得分:2)
const MySTLString::CharT& MySTLString::front() const
{
return this->front();
}
将导致无限递归,导致堆栈溢出。如果要重新使用非const版本的实现,则必须使用:
const MySTLString::CharT& MySTLString::front() const
{
return const_cast<MySTLString*>(this)->front();
}
对MySTLString::back()
和MySTLString::data()
进行相同的更改。
答案 1 :(得分:2)
虽然你确实有无限的递归调用(正如其他答案所解释的那样),但它并不是你要问的问题的答案。您遇到的错误是堆损坏错误。
Visual Studio的调试CRT将保护字节分配给堆分配的内存的左侧和右侧,并使用特定的字节模式填充它们。删除内存后,将这些保护字节与字节模式进行比较。如果它们不匹配,您将看到堆损坏对话框,因为您在分配的内存之外写入。
你的d中的错误不。 d&amp; tor只是检测到堆损坏的地方,因为它释放了堆分配的内存。
您的代码中存在多个具有相同模式的错误:strlen
返回不包括零终止符的字符数。当您strcpy
进入已分配的数组时,零终结符将被写入分配的内存之外。
您需要更改以下代码
MySTLString::MySTLString(const char *s)
{
length_ = strlen(s);
if (length_ == 0)
{
data_ = nullptr;
return;
}
data_ = new char[length_];
strcpy(data_, s);
}
到
MySTLString::MySTLString(const char *s)
{
length_ = strlen(s);
if (length_ == 0)
{
data_ = nullptr;
return;
}
data_ = new char[length_ + 1]; // Allocate enough memory to account for zero terminator
strcpy(data_, s);
}
确保更新其他事件,并相应地调用strlen
。
nullptr
之前,您无需检查delete[]
。虽然没有害处,但它也不是必需的。只需在d&#39; tor。delete[] data_;
即可
答案 2 :(得分:1)