Итак, n дней назад, Cranium предложил тему для нашей супер рублики «Разминка для мозгов».
ТЗ звучит так:
аккуратно написать класс, реализующий строку со счётчиком (или «строка в стиле Pascal») без использования STL и других библиотек. Примечание: строка со счётчиком НЕ завершается '\0'.
Посмотреть и оценить моё решение можно здесь.( devcpp проект )
Давайте делиться оценками и решениями. Вместе мы сделаем идеальный класс для представления строк со счётчиком! :)
Переделал немного класс, теперь у класса PascalString есть собственный класс для обработки исключений и оператор [] для доступа к элементам строки. Новая версия доступна здесь.
PascalString PascalString::operator= ( const PascalString &ob )
{
if ( !(*this == ob) ) //<-- Сравнение строк зачем? Может имелось ввиду this!=&ob ?
{
if ( str )
free( str );
len = ob.len;
str = (char*)malloc( len );//Если здесь не удалось выделить память, то старая строка будет потеряна
_memcpy( str, ob.str, len );//Здесь, да и нигде вообще, нет проверок успешного выделения памяти
}
return *this;//функция возвращает копию объекта, поэтому цепочка str1=str2=str3 работать не будет, ну и лишнее копирование присутствует
}
И вообще, может использовать operator new, вместо malloc, мы же всё-таки на c++ работаем?
у Вас operator[] не ссылку же возвращает, так что будет ненужное копирование, ну и получим rvalue(xvalue)
char& PascalString::operator[] ( int index )
{
if ( index < 0 || index >= len )
{
PascalStringExeption e;
throw e;
}
else
return str[index];
}
Вроде ссылку возвращает, или я что-то напутал?
/<— Сравнение строк зачем? Может имелось ввиду this!=&ob ?
Да, там имелось ввиду *this != ob, просто на момент когда я писал оператор присваивания, оператор != сравнивал количество символов, а тут сравнение нужно для того, что бы избежать присвоение строки, если строка, которой присваивают новое значение уже равна этому значению.
//функция возвращает копию объекта, поэтому цепочка str1=str2=str3 работать не будет, ну и лишнее копирование присутствует
This is sample from operator= and operator +=
Great! operator += work!
Great! operator += work!
This is sample from operator= and operator +=
Great! operator += work!
str = This is end message
str2 = This is end message
str3 = This is end message
str4 = This is end message
//Если здесь не удалось выделить память, то старая строка будет потеряна
И вообще, может использовать operator new, вместо malloc, мы же всё-таки на c++ работаем?
Да, с памятью серьёзный прокол получился. Сейчас переделаю, выложу новую версию.
malloc я использовал потому, что можно использовать realloc
Некоторые методы класса (как правило конструкторы и операторы приведения типа) могут вызываться неявно. Тестовая печать помогает проследить что же происходит на самом деле.
Не понял финта с перехватом исключения bad_alloc и последующим выбросом исключения PascalStringExeption. Какой смысл?
Ну что бы у класса было своё исключение. Вдруг кто-нибудь попробует такой код:
PascalString str = "this is string";
int *i;
try
{
i = new int[/*тут находится такое большое число, что память не выделится*/];
str += "aaaaaaa...aaaa";//тут находится строка такой длинны, что память опять не выделится.
}
catch ( bad_alloc )
{
cout << "Bad alloc exeption!" << endl;
//Неоднозначность. Что породило исключение?
//выделение памяти или добавление строки?
}
operator+ лучше делать другом, чем членом (замечательная фраза!).
То есть придётся создавать два оператора? Один метод, а другой дружественный?
Мне почему-то кажется, что ты не воспользовался советом из моего предыдущего поста по поводу отладочной печати.
Ну просто ещё не успел :). Ведь версия 3.0 ещё не знала макросов.
Кстати про макросы. Как они работают? Что выводит надпись +++ initializations +++?
UPD P.S.: Версия 4.0 в разработке, и я обещаю, там будут использоваться ваши функции и макросы( если я пойму как они работают ) для отладки :)
Внимание! Это довольно старый топик, посты в него не попадут в новые, и их никто не увидит. Пишите пост, если хотите просто дополнить топик, а чтобы задать новый вопрос — начните новый.
Всем привет.
Итак,
n
дней назад, Cranium предложил тему для нашей супер рублики «Разминка для мозгов».ТЗ звучит так:
Посмотреть и оценить моё решение можно здесь.(
devcpp
проект )Давайте делиться оценками и решениями. Вместе мы сделаем идеальный класс для представления строк со счётчиком! :)
Переделал немного класс, теперь у класса
PascalString
есть собственный класс для обработки исключений и оператор[]
для доступа к элементам строки. Новая версия доступна здесь.Сразу в глаза операторы сравнения бросаются. Попробуйте:
почему operator += возвращает временный объект?
Действительно, косяк вышел.
Вот исправленный оператор сравнения:
Разве?:
у Вас operator[] не ссылку же возвращает, так что будет ненужное копирование, ну и получим rvalue(xvalue)
длины строк сравниваете, а не сами строки?
И вообще, может использовать operator new, вместо malloc, мы же всё-таки на c++ работаем?
const? Ок! Почему тогда
без const? И где const версия для
без этого Вы не сможете сравнивать и «складывать» константные строки, а так же «брать» отдельные символы константных строк.
да, а что ещё делать?
Вроде ссылку возвращает, или я что-то напутал?
Да, там имелось ввиду
*this != ob
, просто на момент когда я писал оператор присваивания, оператор!=
сравнивал количество символов, а тут сравнение нужно для того, что бы избежать присвоение строки, если строка, которой присваивают новое значение уже равна этому значению.Вот тестовый код:
Вот что он даёт в результате:
Да, с памятью серьёзный прокол получился. Сейчас переделаю, выложу новую версию.
malloc
я использовал потому, что можно использоватьrealloc
Это я напутал. Думал про operator+=, а написал operator[]
тогда придется обойти 100500 символов для сравнения и если последние окажутся не равные, то придется снова обходить 100500 символов для присваивания.
ну так усложните тест:
или вот:
Вышла новая версия класса
PascalString
:).Исправлены:
operator=
), теперь он работает как надо( по моим тестам )Версию 3.0 можно скачать здесь.
Для полноты ощущений, предлагаю добавить в код следующие фрагменты.
В объявление класса:
И, соответственно, в реализацию:
Переменная класса
counter
, посредством вызоваiReg()
в конструкторах, подсчитывает количество созданных объектов (деструктор не уменьшаетcounter
!).Переменная экземпляра класса
id
, посредством того же вызоваiReg()
в конструкторах, получает уникальную метку для каждого экземпляра.Функция
iLog()
показывает потроха экземпляра класса: ид, содержимое строки, длину строки, плюс пользовательский комментарий.UPD. В
iLog()
добавил манипулятор перехода в 10-чную систему после 16-ричной.Cranium, а зачем это?
А, понял. Для отладки.( ведь так? )
Это — так называемая «тестовая печать».
Некоторые методы класса (как правило конструкторы и операторы приведения типа) могут вызываться неявно. Тестовая печать помогает проследить что же происходит на самом деле.
porshe, по версии 3.0.
Улучшения по сравнению со второй версией очевидные. Но есть и над чем поработать.
(1) Не понял финта с перехватом исключения
bad_alloc
и последующим выбросом исключенияPascalStringExeption
. Какой смысл?(2) Вместо
Можно просто написать
Если добавить конструктор:
(3) При компиляции даёт ошибку на таком тесте:
operator+
лучше делать другом, чем членом (замечательная фраза!).(4) Мне почему-то кажется, что ты не воспользовался советом из моего предыдущего поста по поводу отладочной печати.
Ещё могу предложить два макроса:
Они выводят на консоль выполняемые операторы (которые передаются в качестве аргументов.
Если ты попользуешься этими средствами, то можно будет обсудить логи работы тестов, а также «узкие места» в классе.
Лог получается примерно такой (фрагмент):
Ну что бы у класса было своё исключение. Вдруг кто-нибудь попробует такой код:
То есть придётся создавать два оператора? Один метод, а другой дружественный?
Ну просто ещё не успел :). Ведь версия 3.0 ещё не знала макросов.
Кстати про макросы. Как они работают? Что выводит надпись
+++ initializations +++
?UPD P.S.: Версия 4.0 в разработке, и я обещаю, там будут использоваться ваши функции и макросы( если я пойму как они работают ) для отладки :)
Создавать свой инстанс исключения на каждый чих — плохая практика.
В твоем случае, логичнее было бы использовать bad_alloc. То место, где исключение было выброшено, будет записано в traceback.
А пользователям твоего класса будет проще обрабатывать исключения.