Ошибка удаления из кучи - PullRequest
0 голосов
/ 11 января 2019

Я делаю строку с 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 байтов, что привело к повреждению кучи.

1 Ответ

0 голосов
/ 11 января 2019
  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;
}

Обратите внимание, что мы не меняем size_ перед вызовом reserve, поэтому он не будет выходить за пределы. Также обратите внимание, что мы сообщаем reserve правильное количество байтов, которое нам нужно зарезервировать.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...