从堆中删除失败

Failure deleting from heap

本文关键字:失败 删除      更新时间:2023-10-16

>我正在使用 SSO 制作一个字符串,当字符串超过 23 个字符时,我想将指向堆内存的指针存储在堆栈数组中。我遇到的问题是,当我尝试删除堆上分配的内存时出现错误。我知道我在正确的地址上调用删除,但它失败了。有谁知道为什么?代码如下:

class kstring
{
public:
  kstring()
  {
  }
  kstring(const char* str) 
  {
    size_ = std::strlen(str);
    if (size_ + 1 > capacity_)
      reserve(capacity_ + 1);
    std::strcpy(data(), str);
  }
  void reserve(size_t capacity)
  {
    if (capacity <= capacity_ || capacity <= 24)
      return;
    char* alloc = new char[capacity];
    std::cout << "Alloc: " << static_cast<void*>(alloc) << std::endl;
    std::copy(data(), data() + size_, alloc);
    if (on_heap())
      delete[] data();
    std::memcpy(data_, &alloc, sizeof(char*));
    capacity_ = capacity;
  }
  char* data()
  {
    return capacity_ > 24 ? heap_ptr() : data_;
  }
  size_t size() const noexcept
  {
    return size_;
  }
  size_t capacity() const noexcept
  {
    return capacity_;
  }
  char& operator[](size_t n)
  {
    return data()[n];
  }
  ~kstring()
  {
    if (on_heap())
    {
      std::cout << "Deleting: " << static_cast<void*>(data()) << std::endl;
      delete[] data();
    }
  }
  bool on_heap()
  {
    return capacity_ > 24;
  }
  char* heap_ptr()
  {
    char* ptr = nullptr;
    std::memcpy(&ptr, data_, sizeof(char*));
    return ptr;
  }
  char data_[24] = {0};
private:
  size_t size_ = 0;
  size_t capacity_ = 24;
};
int main()
{
  {
    kstring str("Lorem ipsum dolor sit amet, consectetur adipiscing elit");
  }
  return 0;
}

解决方案:在构造函数中,我分配了 capacity_ + 1 字节而不是 size_ + 1 字节,从而导致堆损坏。

  kstring(const char* str) 
  {
    size_ = std::strlen(str);
    if (size_ + 1 > capacity_)
       reserve(capacity_ + 1);
    std::strcpy(data(), str);
  }

这有很多问题。

首先,您将size_设置为立即的大小。但是如果你看一下 reserve ,它会尝试复制字符串中已有内容的size_字节。

其次,您尝试保留的金额是 capacity_ + 1 .但是没有理由认为这足以容纳您将要复制的字符串。

这可能更接近您想要的:

kstring(const char* str) 
{
  size_t new_size = std::strlen(str) + 1;
  if (new_size > capacity_)
     reserve(new_size + 1);
  std::strcpy(data(), str);
  size_ = new_size - 1;
}

请注意,在调用 reserve 之前我们不会更改size_,因此它不会越界访问。还要注意,我们告诉reserve我们需要保留的正确字节数。