Итак, 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.
А пользователям твоего класса будет проще обрабатывать исключения.