Задача - создать класс string. Я прописал конструкторы, перегрузил операции, и программа вроде работает нормально, но в последней строке возникает ошибка выполнения "CRT detected that the application wrote to memory after end of heap buffer". Заголовочный файл класса прилагаю, при необходимости могу прикрепить реализацию.
#pragma once
#include <iostream>
using namespace std;
class my_string
{
private:
char *string_chars;
size_t len;
public:
my_string(const char *string_chars);
my_string(const size_t len);
my_string(const my_string &obj);
my_string();
~my_string() { delete[] this->string_chars; }
my_string operator+(const my_string &b) const;
my_string operator+(const char *b) const;
my_string &operator=(const my_string &b);
my_string &operator=(const char *b);
my_string &operator+=(const my_string &b);
my_string &operator+=(const char *b);
bool operator<(const my_string &b) const;
bool operator<(const char *b) const;
bool operator>(const my_string &b) const;
bool operator>(const char *b) const;
bool operator==(const my_string &b) const;
bool operator==(const char *b) const;
bool operator!=(const my_string &b) const;
bool operator!=(const char *b) const;
void print() const { cout << this->string_chars << endl; }
const char *get_str() const { return this->string_chars; }
size_t get_len() const { return this->len; }
size_t first_sym(const char a) const;
};
UPD: Прилагаю реализацию
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string.h>
#include "String.h"
using namespace std;
my_string::my_string(const char *string_chars)
{
this->len = strlen(string_chars);
this->string_chars = new char[this->len + 1];
strcpy(this->string_chars, string_chars);
this->string_chars[this->len] = 0;
}
my_string::my_string(const size_t len)
{
this->len = len;
this->string_chars = new char[this->len + 1];
for (size_t i = 0; i <= len; i++)
{
this->string_chars[i] = 0;
}
}
my_string::my_string(const my_string &obj)
{
strcpy(this->string_chars, obj.get_str());
this->len = obj.get_len();
}
my_string::my_string()
{
this->len = 0;
this->string_chars = new char[2];
for (int i = 0; i < 2; i++)
{
this->string_chars[i] = 0;
}
}
my_string my_string::operator+(const my_string &b) const
{
size_t len = this->len + b.get_len();
char *result = new char[len + 1];
strcpy(result, this->string_chars);
strcat(result, b.string_chars);
result[len] = 0;
my_string *res = new my_string(result);
delete[] result;
return *res;
}
my_string my_string::operator+(const char *b) const
{
size_t len = this->len + strlen(b);
char *result = new char[len + 1];
strcpy(result, this->string_chars);
strcat(result, b);
result[len] = 0;
my_string *res = new my_string(result);
delete[] result;
return *res;
}
my_string &my_string::operator=(const my_string &b)
{
if (&b != this)
{
this->len = b.get_len();
strcpy(this->string_chars, b.string_chars);
}
return *this;
}
my_string &my_string::operator=(const char *b)
{
if (b != this->string_chars)
{
this->len = strlen(b);
strcpy(this->string_chars, b);
}
return *this;
}
my_string &my_string::operator+=(const my_string &b)
{
this->len += b.get_len();
strcat(this->string_chars, b.string_chars);
return *this;
}
my_string &my_string::operator+=(const char *b)
{
this->len += strlen(b);
strcat(this->string_chars, b);
return *this;
}
bool my_string::operator<(const my_string &b) const
{
if (strcmp(b.string_chars, this->string_chars))
{
return true;
}
else
{
return false;
}
}
bool my_string::operator<(const char *b) const
{
if (strcmp(b, this->string_chars))
{
return true;
}
else
{
return false;
}
}
bool my_string::operator>(const my_string &b) const
{
if (strcmp(this->string_chars, b.string_chars))
{
return true;
}
else
{
return false;
}
}
bool my_string::operator>(const char *b) const
{
if (strcmp(this->string_chars, b))
{
return true;
}
else
{
return false;
}
}
bool my_string::operator==(const my_string &b) const
{
if (!strcmp(this->string_chars, b.string_chars) && this->len == b.len)
{
return true;
}
else
{
return false;
}
}
bool my_string::operator==(const char *b) const
{
if (!strcmp(this->string_chars, b) && this->len == strlen(b))
{
return true;
}
else
{
return false;
}
}
bool my_string::operator!=(const my_string &b) const
{
if (abs(strcmp(this->string_chars, b.string_chars)) || this->len != b.len)
{
return true;
}
else
{
return false;
}
}
bool my_string::operator!=(const char *b) const
{
if (abs(strcmp(this->string_chars, b)) || this->len != strlen(b))
{
return true;
}
else
{
return false;
}
}
size_t my_string::first_sym(const char a) const
{
char *sym = strchr(this->string_chars, a);
if (sym != nullptr)
{
return sym - this->string_chars;
}
return -1;
}
Подобные классы где динамически выделяется память, должны определять явно конструктор копирования. В вашем классе он не объявлен, а потому его отсутствие может быть причиной возникновения ошибки, так как созданный компилятором неявно конструктор копирования просто выполняет поочередно копирование значений членов класса, что может в результате привести к тому, что два объекта класса будут иметь указатели, указывающие на одну и ту же область памяти, которую деструкторы этих объектов попытается в итоге удалить дважды.
Этот конструктор с параметром
my_string(const int len);
^^^^
было бы лучше объявить как
my_string( size_t len );
^^^^^^
Данные операторы
my_string &operator+(const my_string &b) const;
my_string &operator+(const char *b) const;
объявлены не корректно. Так как сами функции-члены класса являются константными, значит объект класса, которой расположен в левой части от знака + не изменяется. Отсюда следует, что из операторов возвращается ссылка на временные объекты, созданные внутри операторов, что ведет к неопределенному поведению программы.
Эту функцию
char *get_str() const { return this->string_chars; }
так как она константная лучше объявить с возвращаемым значением const char *
, или перегрузить эту функцию для константных и не константных объектов.
Определение этой функции
int first_sym(const char a) const { return strchr(this->string_chars, a) - this->string_chars; }
некорректно, так как в случае отсутствия символа a
в строке функция strchr
вернет указатель NULL
. К тому же тип возвращаемого значения должен быть не int
, а, по крайней мере, ptrdiff_t
. Конечно лучше было бы объявить тип возвращаемого значения size_t
, и если символ не найден в строке, то возвращать значение -1
.
Что касается возникающей ошибки, то она, скорей всего, возникает из-за неправильных реализаций операторов, которые вы в своем вопросе не привели, а также неизвестно предложение, в результате выполнения которого возникла эта ошибка. Например, возможно, что вы забыли выделить память для завершающего нуля в операторах суммирования, или "проскакиваете" этот ноль при копировании строк.
EDIT: Так как вы изменили свой ответ, исправив некоторые недостатки, которые я указал (что является плохой идеей, так как это может привести к несоответствию ответа и вопроса), и дополнили его реализацией, то
В данном конструкторе последнее предложение лишнее, и может быть удалено.
my_string::my_string(const char *string_chars)
{
this->len = strlen(string_chars);
this->string_chars = new char[this->len + 1];
strcpy(this->string_chars, string_chars);
this->string_chars[this->len] = 0;
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
Этот конструктор может быть написан проще
my_string::my_string(const size_t len)
{
this->len = len;
this->string_chars = new char[this->len + 1]();
}
Лучше этот конструктор объявить со спецификатором функции explicit
explicit my_string::my_string( size_t len)
{
this->len = len;
this->string_chars = new char[this->len + 1]();
}
А иначе получится так, что вы строки можете складывать с целыми числами в виду наличия этого конструктора преобразования.
Или было бы еще лучше объявить его со вторым параметром, который задает символ заполнитель. Например,
#include <cstring>
//...
my_string::my_string( size_t len, char c )
{
this->len = len;
this->string_chars = new char[this->len + 1]();
std::memset(this->string_chars, ( unsigned char )c, len );
}
Данный конструктор копирования неверный
my_string::my_string(const my_string &obj)
{
strcpy(this->string_chars, obj.get_str());
this->len = obj.get_len();
}
Память, куда копируется строка, не была выделена.
В этом конструкторе по умолчанию достаточно выделить память для одного символа
my_string::my_string()
{
this->len = 0;
this->string_chars = new char[1]();
}
Данный оператор некорректный и можт привести к утечке памяти. Нет никакой необходимости создавать объект в динамической памяти
my_string my_string::operator+(const my_string &b) const
{
size_t len = this->len + b.get_len();
char *result = new char[len + 1];
strcpy(result, this->string_chars);
strcat(result, b.string_chars);
result[len] = 0;
my_string *res = new my_string(result);
delete[] result;
return *res;
}
Он может быть определен следующим образом
my_string my_string::operator+(const my_string &b) const
{
size_t len = this->len + b.get_len();
char *result = new char[len + 1];
strcpy(result, this->string_chars);
strcat(result, b.string_chars);
my_string res(result);
delete[] result;
return res;
}
То же самое справедливо и для второго перегруженного оператора operator +
.
Простые и составные операторы присваивания определены некорректно. В них в общем случае имеет место выход за пределы выделенной памяти. Необходимо удалять ранее созданную строку, если она не равна по длине присваиваемой, и создавать новую с соответствующей длиной.
Все операторы сравнения могут быть написаны проще. К тому они также написаны неверно. Например, вместо этого неверно написанного оператора
bool my_string::operator<(const my_string &b) const
{
strcmp(b.string_chars, this->string_chars))
{
return true;
}
else
{
return false;
}
}
Должно быть
bool my_string::operator<(const my_string &b) const
{
return strcmp( this->string_chars, b.string_chars ) < 0;
}
Кофе для программистов: как напиток влияет на продуктивность кодеров?
Рекламные вывески: как привлечь внимание и увеличить продажи
Стратегії та тренди в SMM - Технології, що формують майбутнє сьогодні
Выделенный сервер, что это, для чего нужен и какие характеристики важны?
Современные решения для бизнеса: как облачные и виртуальные технологии меняют рынок
Клиентское приложение на C++ передаёт на HTTP-сервер данные - идентификатор клиента (чтобы обозначить присутствие), кадры с веб-камеры (по запросу),...
Имеется массив mass[] и число int qНеобходимо записать q по разрядам в массив
Задался вопросом, а зачем в данном примере io_service в разных потоках