Форум программистов, компьютерный форум CyberForum.ru

С++ для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Рейтинг: Рейтинг темы: голосов - 22, средняя оценка - 4.68
YourLastSong
1 / 1 / 0
Регистрация: 12.12.2010
Сообщений: 112
#1

Использование собственного класса строк - C++

15.01.2012, 23:22. Просмотров 3065. Ответов 67
Метки нет (Все метки)

Здравствуйте, уважаемые господа.

Есть реализация собственного класса строк:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
class MyString
{
    private:
        size_t length;
        char *str;
 
    public:
        MyString ();
        MyString (const char *c);
        MyString (const MyString& t);
        ~MyString ();
 
        MyString& operator= (const MyString& t);
        MyString operator+ (const MyString& t) const;
        MyString operator+ (const char *c) const;
        bool operator== (const MyString& t) const;
        bool operator!= (const MyString& t) const;
        void operator+= (const MyString& t);
        char& operator[] (size_t i);
 
        char& at (size_t i);
        void get_input (std::istream& in);
        size_t size () const;
        size_t get_length () const;
 
        friend std::ostream& operator<< (std::ostream& out, const MyString& t);
        friend std::istream& operator>> (std::istream& in, MyString& t);
};
 
MyString::MyString ()
{
    length = 0;
    str = NULL;
}
 
MyString::MyString (const char *c)
{
    length = strlen (c) + 1;
    str = new char[length];
    strcpy (str, c);
    str[length-1] = '\0';
}
 
MyString::MyString (const MyString& t)
{
    length = strlen (t.str) + 1;
    str = new char[length];
    strcpy (str, t.str);
    str[length-1] = '\0';
}
 
MyString::~MyString ()
{
    if (str != NULL)
    {
        delete[] str;
    }
}
 
MyString& MyString::operator= (const MyString& t)
{
    length = strlen (t.str) + 1;
    str = new char[length];
    strcpy (str, t.str);
    str[length-1] = '\0';
 
    return *this;
}
 
MyString MyString::operator+ (const MyString& t) const
{
    char* newStr = new char[strlen (str) + strlen (t.str) + 1];
    strcpy (newStr, str);
    strcat (newStr, t.str);
    newStr[length-1] = '\0';
 
    MyString returnObj (newStr);
    delete[] newStr;
 
    return returnObj;
}
 
bool MyString::operator== (const MyString& t) const
{
    return (strcmp(str, t.str) == 0);
}
 
bool MyString::operator!= (const MyString& t) const
{
    return (strcmp(str, t.str) != 0);
}
 
void MyString::operator+= (const MyString& t)
{
    length = strlen (str) + strlen (t.str) + 1;
    char *newStr = new char[length];
    strcpy (newStr, str);
    strcat (newStr, t.str);
    strcpy (str, newStr);
    str[length-1] = '\0';
    delete[] newStr;
}
 
char& MyString::operator[] (size_t i)
{
    return str[i];
}
 
char& MyString::at (size_t i)
{
    if (i >= 0 && i < length)
    {
        return str[i];
    }
}
 
void MyString::get_input (std::istream& in)
{
    while (true)
    {
        char c = in.get ();
 
        if (c == '\n')
        {
            break;
        }
 
        if (length == 0)
        {
            length = 2;
        }
        else
        {
            ++length;
        }
 
        char* newStr = new char[length];
        if (str != NULL)
        {
            strcpy (newStr, str);
        }
        newStr[length-2] = c;
        newStr[length-1] = '\0';
 
        delete[] str;
 
        str = newStr;
    }
}
 
size_t MyString::size () const
{
    return length;
}
 
size_t MyString::get_length () const
{
    if (length == 0)
    {
        return 0;
    }
    else
    {
        return (length - 1);
    }
}
 
std::ostream& operator<< (std::ostream& out, const MyString& t)
{
    out << t.str;
    return out;
}
 
std::istream& operator>> (std::istream& in, MyString& t)
{
    t.get_input (in);
    return in;
}
Всё ли здесь так, как надо?

Что бы вы предложили добавить сюда и каким образом?

Заранее благодарю за возможные ответы.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
15.01.2012, 23:22
Здравствуйте! Я подобрал для вас темы с ответами на вопрос Использование собственного класса строк (C++):

Запись в собственного класса бинарный файл собственного - C++
есть Свой тип данных дробь. Надо реализовать запись и загрузку в\из бинарного файла. #ifndef RATION_H #define RATION_H #include...

Использование собственного функтора со связывателями - C++
Не могу правильно написать функтор, чтобы его можно было использовать с биндом. Вот пример на сравнение строк без учета регистра: ...

Создание собственного класса - C++
Создать класс для работы со стеком, организованным как связный однонаправленный список. В классе описать методы добавления элемента в стек...

Ввод и вывод строки собственного класса - C++
Добрый вечер. Пишу свой класс строк, беда в том, что во вводе что-то, видимо, не так..В общем, строка вводится, но не выводится:( Все...

Увеличение размера массива из элементов собственного класса - C++
Добрый день, столкнулся с проблемой, у меня есть собственный класс myClass{ myClass(){ len = 0; ii = new...

Создание списка объектов собственного класса (SQL) - C++
очень нужно! объясните, пожалуйста, доходчиво, а лучше с примером, как создать список из объектов класса, к примеру, &quot;human&quot;, используя...

Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
rangerx
1933 / 1542 / 141
Регистрация: 31.05.2009
Сообщений: 2,912
15.01.2012, 23:35 #2
Для начала добавить константные версии
C++
1
2
3
char& operator[] (size_t i);
//...
char& at (size_t i);
и избавиться от дублирования кода
C++
1
2
3
4
length = strlen (t.str) + 1;
str = new char[length];
strcpy (str, t.str);
str[length-1] = '\0'; // эта строка, кстати говоря, лишняя
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
15.01.2012, 23:35 #3
поверхностно:
если есть
C++
1
2
                MyString (const char *c);
                MyString (const MyString& t);
то должен быть
C++
1
MyString& operator= (const char* c);
а не только
C++
1
MyString& operator= (const MyString& t);
operator+ , operator== , operator!= - не думаю что должны быть реализованы, как функции-члены

зачем 2 ф-ии, которые логически должны были бы делать одно и то же?
C++
1
2
                size_t size () const;
                size_t get_length () const;
YourLastSong
1 / 1 / 0
Регистрация: 12.12.2010
Сообщений: 112
15.01.2012, 23:41  [ТС] #4
должен быть
MyString& operator= (const char* c);
а не только
MyString& operator= (const MyString& t);
Зачем?

Выражения вида

MyString str;
str = "str";

работают так, как надо.

operator+ , operator== , operator!= - не думаю что должны быть реализованы, как функции-члены
Почему?
ValeryLaptev
Эксперт С++
1040 / 819 / 48
Регистрация: 30.04.2011
Сообщений: 1,659
15.01.2012, 23:45 #5
Например, вот поэтому:
C++
1
2
mystring str = "fff";
mystring s = "левый аргумент-не типа mystring" + str;
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
15.01.2012, 23:46 #6
Цитата Сообщение от YourLastSong Посмотреть сообщение
Зачем?

Выражения вида

MyString str;
str = "str";

работают так, как надо.
чтобы не было лишнего создания временного обьекта


Цитата Сообщение от YourLastSong Посмотреть сообщение
Почему?
потому что им не нужен доступ к закрытым членам класса, а понятие инкапсуляции еще никто не отменял в с++ + пример выше
silent_1991
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
15.01.2012, 23:56 #7
Начнём с интерфейса:
1. Вы можете присвоить строке другую строку или С-строку, но не можете присвоить ей одиночный символ типа char.
2. Та же история с оператором конкатенации и операторами сравнения. Вы перегрузили оператор сложения для константной С-строки (чего можно было и не делать, ведь есть конструктор-преобразователь, принимающий С-строку), но с одиночными символами опять промашка. К тому же, следует отдельно учесть случай, если символ стоит слева от операции присваивания. То же относится к операторам сравнения.
3. Операторов отношения вообще не наблюдается, что может стать серьёзным недостатком в некоторых случаях.
4. Оператор индексации перегружен только для неконстантного случая, т.е. если строка создана константной, обратиться в конкретному её символу не представляется возможным. То же с методом at.

Теперь по технической стороне вопроса:
1. В конструкторах лучше всё же использовать списки инициализации. Идеология С++ предполагает, что в теле конструктора происходит работа более серьёзная, чем простая инициализация.
2. В деструкторе проверка на NULL лишняя, поскольку оператор delete вполне законно применять к пустому указателю. Кстати говоря, в С++, в отличие от С, предпочтительнее использовать 0, а не NULL (а в новом стандарте, разумеется, надо пользоваться nullptr).
3. Грубейшая ошибка в операторе присваивания. В случае, если строку присваивается самой себе, поведение программы не определено (велика вероятность падения в рантайме). Поясню: мало того, что вы не очищаете старую память (происходит утечка памяти), вы сначала затираете указатель str вновь выделенной памятью (это исключает падение в рантайме, но от этого не легче), а затем делаете strcpy. Если объект слева от знака равно и справа от него один и тот же, то вы сначала в этом объекте выделяете под строку новую память, заполненную мусором, а потом копируете её саму в себя. Как видно, вместо того, чтобы не получить никаких действий в строке str = str;, строка на самом деле будет затёрта, да ещё и память утечёт. Чтобы этого избежать, надо проверять, не совпадают ли адреса присваиваемого объекта и того объекта, которому производится присваивание.
4. В операторе конкатенации производится куча лишних действий в одной безобидной строке: MyString returnObj (newStr); На самом деле куда легче удалить память по указателю str и присвоить ему newStr, в котором уже сформирована результирующая строка. Вернуть достаточно копию *this.
5. Оператор + лучше реализовать через уже написанный operator+=. К чему дублировать код?
6. Метод at в стандартной библиотеке работает следующим образом: если переданный ему индекс не выходит за границу последовательности, возвращается элемент по этому индексу. Иначе кидается исключение std::out_of_range. У вас же во втором случае вообще ничего не происходит, даже никакой фиктивный результат не возвращается и сообщение не выводится. Отсюда и варнинг при компиляции (функция возвращает значение не на всех путях выполнения). Либо кидайте исключение, либо сделайте метод клоном operator[], либо вообще исключите его из класса.
7. Я бы на вашем месте хранил длину строки без учёта завершающего символа. Иначе вам практически везде приходится отнимать единицу. И необходимости в методах size и get_length отпадёт, будете просто возвращать длину. И вообще, к чему пользователю знать реальный размер и длину, когда они всегда отличаются на 1? И в дополнение к этому, я бы при создании пустой строки создавал строку из одного элемента, и инициализировал её '\0'. Это избавит от лишних проверок, поскольку у вас в любом случае будет выделена строка, пусть и нулевой (реально единичной) длины.
8. Метод get_input - это ужас. На каждой итерации выделять память под строку длиной на 1 больше предыдущей - страшно расточительно. Самое простое тут - на каждой итерации выделять строку размером в два раза (или на определённый квант) больше предыдущей итерации. По завершении ввода просто перевыделите строку окончательно длины и скопируете туда символы из предварительной строки большей длины.

Фух, вроде пока всё, что нашёл. Дерзайте.

Добавлено через 1 минуту
Цитата Сообщение от retmas Посмотреть сообщение
потому что им не нужен доступ к закрытым членам класса
Почему?
retmas
Жарю без масла
859 / 741 / 164
Регистрация: 13.01.2012
Сообщений: 1,694
16.01.2012, 00:01 #8
Цитата Сообщение от silent_1991 Посмотреть сообщение
Почему?
потому что operator+ легко реализуется с помощью operator+=, тем самым используя только открытый интерфейс
YourLastSong
1 / 1 / 0
Регистрация: 12.12.2010
Сообщений: 112
16.01.2012, 00:02  [ТС] #9
Спасибо.

Забыл написать выброс исключения, заметил уже потом.
silent_1991
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
16.01.2012, 00:02 #10
retmas, ага, вот об этом надо говорить отдельно. Я лично напрочь забыл про +=, когда читал ваш комментарий, хотя только что об этом написал в своём
Toshkarik
1140 / 857 / 51
Регистрация: 03.08.2011
Сообщений: 2,384
Завершенные тесты: 1
16.01.2012, 00:42 #11
Я вот все хотел спросить, а зачем отдельная функция at? Нельзя разве сделать проверку в перегруженном операторе []?
silent_1991
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
16.01.2012, 00:45 #12
Toshkarik, дополнительная проверка + исключения = потеря производительности. Поэтому оператор индексации ведёт себя так же, как аналогичный оператор для сырых массивов - без всяких проверок. Нужна надёжность - надо использовать at, нужна скорость, привычный синтаксис - оператор индексации.
Toshkarik
1140 / 857 / 51
Регистрация: 03.08.2011
Сообщений: 2,384
Завершенные тесты: 1
16.01.2012, 00:52 #13
Я так и думал, но ведь исключения обрабатываются только если не пройдена проверка? Неужели эта простая проверка может сильно повлиять на производительность?
silent_1991
Эксперт С++
4963 / 3039 / 149
Регистрация: 11.11.2009
Сообщений: 7,027
Завершенные тесты: 1
16.01.2012, 00:54 #14
Toshkarik, если у вас в строке содержится 10000 символов, и вы в цикле проходите по строке, то получаете уже 10000 проверок. Добавим вложенный цикл - получим квадратичную зависимость.
Toshkarik
1140 / 857 / 51
Регистрация: 03.08.2011
Сообщений: 2,384
Завершенные тесты: 1
16.01.2012, 00:54 #15
Ясно, спасибо.
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
16.01.2012, 00:54
Привет! Вот еще темы с ответами:

Использование в качестве поля класса указатель на объект другого класса - C++
Ошибка в названии - &quot;указателЯ&quot; Вот, пытаюсь освоить ООП (пока только учусь): создаю класс person (человек) и car (модель машины). ...

Использование объекта одного класса при создании нового класса - C++
Добрый вечер. У меня такая проблема. Есть готовый класс L2 - список, на его основе нужно создать дек. Я поступил так: в приватной части...

Std::vector добавить новый элемент собственного класса без использования конструктора копирования - C++
Всем привет! Есть один класс, который я хочу хранить в std::vector. Создать массив, с типом своего класса, грубо говоря. Естественно,...

Использование перегруженного оператора ввода/вывода в поток базового класса для объекта производного класса - C++
Здравствуйте! Можно ли использовать перегруженный оператор ввода/вывода в поток из базового класса для объекта производного класса, если...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
Yandex
Объявления
16.01.2012, 00:54
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2017, vBulletin Solutions, Inc.
Рейтинг@Mail.ru