Реализовать класс, желающий имитировать std :: string, но застрять, когда push_back его в вектор - PullRequest
0 голосов
/ 15 октября 2019

Я реализовал класс String. Когда я помещаю объект String в Vector, программа застревает. Пожалуйста, помогите мне просмотреть его, если вам интересно.

String.h

#pragma once
#include <iostream>
#include <memory>
#include <string>

class String {
public:
   String();
   String(const char *);
   String(const String &);
   String(const std::string &);
   String(String &&) noexcept;
   String &operator=(String &&) noexcept;
   ~String();
   String &operator=(const String &);
   std::string to_string() const;

   friend std::ostream &operator<<(std::ostream &os, String s);

private:
   std::pair<char *, char *>
      alloc_n_copy(char *, char *);
   void free();
   static std::allocator<char> alloc;
   char *beg;
   char *end;
   size_t length;
};

String.cpp

#include "stdafx.h"
#include <cstring>
#include "String.h"

std::allocator<char> String::alloc = std::allocator<char>();

String::String()
   : beg(nullptr), end(nullptr), length(0) {}

String::String(const char *ptr) {
   std::cout << "Const char constructor execute" << std::endl;
   const char *tmp = ptr;
   while (*tmp++ != '\0') ++length;
   beg = alloc.allocate(length);
   end = std::uninitialized_copy(ptr, tmp, beg);
}

String::String(const std::string &s) {
   std::cout << "Const string constructor execute" << std::endl;
   strcpy_s(beg, s.size(), s.c_str());
   length = s.size();
   char *tmp = beg;
   end = tmp + length;
}

String::String(const String &s) {
   std::cout << "Copy constructor execute" << std::endl;
   beg = alloc.allocate(s.length);
   end = std::uninitialized_copy(s.beg, s.end, beg);
}

std::pair<char *, char *>
String::alloc_n_copy(char *beg, char *end) {
   auto newBeg = alloc.allocate(end - beg);
   return{ newBeg, std::uninitialized_copy(beg, end, newBeg) };
}

String &String::operator=(const String &s) {
   length = s.length;
   auto newStr = alloc_n_copy(s.beg, s.end);
   free();
   beg = newStr.first;
   end = newStr.second;
   return *this;
}

String::String(String &&s) noexcept : beg(s.beg), end(s.end), length(s.length) {
   std::cout << "Move constructor execute" << std::endl;
   s.beg = s.end = nullptr;
   s.length = 0;
}

String &String::operator=(String &&s) noexcept {
   if (this != &s) {
      beg = s.beg;
      end = s.end;
      length = s.length;
      s.beg = s.end = nullptr;
      s.length = 0;
   }
   return *this;
}

void String::free() {
   while (length-- >= 0) {
      alloc.destroy(end--);
   }
   alloc.deallocate(beg, length);
}

String::~String() {
   free();
}

std::string String::to_string() const {
   std::string s(beg);
   return s;
}

std::ostream &operator<<(std::ostream &os, String s) {
   std::string str(s.beg);
   os << str;
   return os;
}

main.cpp

int main()
{
   vector<String> v;
   String s1("abc");
   String s2("def");

   v.push_back(s1);
   v.push_back(s2);

   return 0;
}

результат:

Const char constructor execute
Const char constructor execute
Copy constructor execute
Move constructor execute

Я не знаю, почему второй push_back является конструкцией перемещения.

И когда push_back завершился, программа не может выйти. Не удалось ли освободить какой-либо ресурс?

Спасибо

1 Ответ

1 голос
/ 15 октября 2019

Причина, по которой ваша программа блокируется, заключается в том, что ваш деструктор никогда не завершается:

void String::free() {
   while (length-- >= 0) {
      alloc.destroy(--end);
   }
   alloc.deallocate(beg, length);
}

Поскольку length является типом без знака, length >= 0 всегда имеет значение true. Вы, вероятно, не хотите уменьшать значение length здесь, пока оно не будет использовано в качестве аргумента для alloc.deallocate(). Я предлагаю:

void String::free() {
   while (end > beg) {
      alloc.destroy(end--);
   }
   alloc.deallocate(beg, length);
}

Существуют и другие ошибки, например, не удается инициализировать length перед его использованием в конструкторе char const* (я не понимаю, почему вы просто не используетеstd::strlen()) и не удалось выделить в конструкторе std::string. Я рекомендую использовать хороший набор предупреждений (я использовал g++ -std=c++2a -Wall -Wextra -Wwrite-strings -Wno-parentheses -Wpedantic -Warray-bounds -Weffc++) и адресовать их всем. Приведенная выше проблема была легко идентифицирована следующим образом.

После компиляции предупреждений, затем запустите код под Valgrind или другим средством проверки памяти, чтобы понять некоторые нерешенные проблемы.

...