Здравствуйте, _AK_, Вы писали:
_AK>Здравствуйте, Lorenzo_LAMAS, Вы писали:
_AK>>>
_AK>>>nvString a,b,c;
_AK>>>a+b=c;
_AK>>>
L_L>>Предлагаю попробовать такое L_L>>
L_L>>std::string a, b, c;
L_L>>a + c = b;
L_L>>
_>>>>Моя первая реализация String
_AK>>>Лучше чтобы она была бы ещё и последней.
L_L>>Зачем так? Человек попросил указать ошибки, а ему — такие гадости.
_AK>не гадости, а намёк: std::string...
Надо сначала свои шишки набить. имхо.
HgLab: Mercurial Server and Repository Management for Windows
А>Дело не в ошибке. Стандартом не определяется что delete на 0 не реагирует. Каждый реализует по своему разумению. Просто в последнее время стало считатся хорошим тоном проверять в операторе delete на 0. В более ранних версиях компиляторов проверки небыло.
5.3.5.2
.... if the value of the operand of delete is the null pointer the operation has no effect
Здравствуйте, AndrewJD, Вы писали:
AJD>Здравствуйте, SergeMukhin, Вы писали:
SM>>Здравствуйте, roma_k, Вы писали:
_>>>Оцените пожалуйста, интересны все мои ошибки, особенно в реализации операторов !!!
SM>>3. вместо int надо size_t (где речь идет о длинах) AJD>Почему ?
например strlen выдает size_t, и так для переносимости.
SM>>5. где поддержка UNICODE? AJD>Должна быть?
Здравствуйте, SergeMukhin, Вы писали:
SM>Здравствуйте, kaa_t, Вы писали:
__>>>Можно писать и так : __>>>
__>>>delete p;
__>>>p=NULL;
__>>>
_>>При таком варианте приходится полагатся, на разработчиков компиляторов,а также на разработчиков библиотек.
SM>ну вы даете! SM>1. причем тут компилятор? SM>2. неужели могут быть сомнения, что при реализации delete разработчики допустят такую ошибку?
Дело не в ошибке. Стандартом не определяется что delete на 0 не реагирует. Каждый реализует по своему разумению. Просто в последнее время стало считатся хорошим тоном проверять в операторе delete на 0. В более ранних версиях компиляторов проверки небыло.
SM>что значит устарело? так вроде всегда было!
Было, будет и нужно так делать. Просто теперь можно проверку не ставить (правда предворительно проверив свой компилятор)
Здравствуйте, Вадим Никулин, Вы писали:
ВН>Здравствуйте, Нахлобуч, Вы писали:
ВН>>>Я про это и говорю. Какой у тебя синтаксис operator [] ? Если char &operator [] ( int k ), то надо перечитать мой предыдущий пост. Если ты хочешь прокси-объект возвращать, то действительно можно обойтись без mutable. Только в этом случае на каждый чих ты будешь пересчитывать длину строки. А если она тебе никогда не понадобится?
Н>>Ну я про прокси-класс и писал. А пересчет (если он вообще потребуется) займет всего-то ничего: ВН> 1. Где прокси-класс? В примере не прокси-класс.
Я писал, что можно использовать прокси-класс.
ВН> 2. Все-равно пересчет — лишние инструкции для процессора. +Затраты на прокси.
Это да.
HgLab: Mercurial Server and Repository Management for Windows
Здравствуйте, roma_k, Вы писали:
_>Оцените пожалуйста, интересны все мои ошибки, особенно в реализации операторов !!! _>// Заголовок
class nvString
{
public:
// constructor
nvString();
// Construct a string from a char pointer
nvString(const char* inStr);
// Copy-constructor
nvString(const nvString& inStr);
// destructorvirtual ~nvString();
// Type cast to char pointerinline operator const char *() const
{ return m_buffer; }
// Indexing operatorinline char operator[](int i) const
{ return m_buffer[i]; }
// Сделай еще что-нибудь типа "GetAt()" с проверкой индекса
// Char pointer atribuition operator
const nvString& operator=(const char* inStr);
// Atribuition operatorconst nvString& operator=(const nvString& inStr);
// Такие два оператора, имхо, лучше делать глобальными
// Concatenation operator with a char pointer
nvString operator+(const char* inStr);
// Concatenation operator
nvString operator+(const nvString& inStr);
// Self-concatenation operator with a char pointerconst nvString&operator+=(const char* inStr);
// Self-concatenation operatorconst nvString&operator+=(const nvString& inStr);
void Clear(void);
// Find a sub-string in the stringinline int Find(const char *str) const
{
char *pos=strstr(m_buffer,str);
if (pos == 0)
return -1;
return (int)(pos-m_buffer);
}
// Find the first occurrence of a character in the stringinline int Find(char c) const
{
char *pos = strchr(m_buffer,c);
if (pos == 0)
return -1;
return (int)(pos-m_buffer);
}
// Find the last occurrence of a character in the stringinline int FindLast(char c) const
{
char *pos = strrchr(m_buffer,c);
if (pos == 0)
return -1;
return (int)(pos-m_buffer);
}
// Change the 'i'-th character of the string// Удобнее было бы еще и через индексирование сделать. Через прокси-класс :)inline void SetChar(int i,char c)
{
if (i < (int)strlen(m_buffer))
m_buffer[i] = c;
}
// Crop the first 'n' characters of the stringinline void CropBegin(int n)
{
if (n < (int)strlen(m_buffer))
{
strcpy(m_buffer,&m_buffer[n]);
}
}
// А сравнения с nvString?
// equal compare operator
inline int operator==(const char *str) const
{ return strcmp(m_buffer,str)==0; }
inline int operator!=(const char *str) const
{ return strcmp(m_buffer,str)!=0; }
inline int operator>(const char *str) const
{ return strcmp(m_buffer,str)>0; }
inline int operator<(const char *str) const
{ return strcmp(m_buffer,str)<0; }
inline int operator>=(const char *str) const
{ return strcmp(m_buffer,str)>=0; }
inline int operator<=(const char *str) const
{ return strcmp(m_buffer,str)<=0; }
// Compare with a char pointerinline int Compare(const char *str) const
{ return strcmp(m_buffer,str); }
// Compare the first 'n' characters of the string with a char pointerinline int Compare(const char *str,int n) const
{ return strncmp(m_buffer,str,n); }
// Compare with a char pointer, case-insensitive flavourinline int CompareNoCase(const char *str) const
{ return stricmp(m_buffer,str); }
// Change all characters to lower-caseinline void ToLower()
{ strlwr(m_buffer); }
// Change all characters to upper-caseinline void ToUpper()
{ strupr(m_buffer); }
// Return the length of the string in bytes// Это кэшировать надоinline int Length() const
{ return m_nLength; }
private:
char *m_buffer;
int m_nLength;
};
HgLab: Mercurial Server and Repository Management for Windows
Здравствуйте, roma_k, Вы писали:
_>Оцените пожалуйста, интересны все мои ошибки, особенно в реализации операторов !!!
1. чем этот String лучше других?
2. почему С текст не форматирован?
3. вместо int надо size_t (где речь идет о длинах)
4. почему StrDuplicate не в классе?
5. где поддержка UNICODE?
6. в += используется strcpy/strcat вместо memcpy*2
и вообще там получается ТРИ раза new, и пять копирований строк!!! не многовато ли?
Здравствуйте, roma_k, Вы писали:
_>Оцените пожалуйста, интересны все мои ошибки, особенно в реализации операторов !!!
Сходу найдено следующее. Если поискать чуть тщательней, то несомневаюсь что список можно расширить в два-три раза.
1) Лишние опреаторы (с учётом того что конструктор не-explicit).
2) Постоянное использование в качестве индекса типа int.
3) До глубины души умиляет SafeArrayDelete (явно внутри что-то типа: if (p) {delete p;})
4) exception-safety и не пахнет (new то может швынуть bad_alloc — не стоит забывать про это)
5) Класс позволяет писать следующие загадочные вещи:
1) почему деструктор виртуальный? у тебя нет ни одного виртуального метода.
2) лучше хранить длину строки в отдельной переменной, а не вызывать каждый раз strlen(). Очень неэффективно получается..
3) оператор индексирования сделан так, что с помощью него не изменить содержимого строки. То есть написать что-то типа такого не получится:
my_str[0] = 'a';
ИМХО, не очень удобно. Я бы предложил такую реализацию:
4) предлагаю функцию StrDuplicate() сделать статической и private класса.
5) не нашел определения функции SafeArrayDelete()...
6) оператор+= реализован очень уж неэффективно.. каждый раз вызывается функция StrDuplicate() [и не раз вызывается], а он выделяет память через new... в общем, задумайся над этим куском кода
7) еще я бы добавил конструктор, который принимает 2 параметра: символ и длину строки. Чтобы вся строка состояла из этих символов.
8) operator+= должен возвращать ссылку на объект твоего класса(а не void!). У тебя нельзя записать так:
my_str2 = my_str1 += "aaaaa";
9) добавь operator+=(const char).
10) добавь operator+(const char).
11) добавь operator=(const char).
12) может память выделять немного заранее? то есть сначала выделяем какой-то кусок памяти, его используем. Если не хватает памяти для какой-то операции, выделяем новый кусок памяти(например, в 2 раза больше, чем старый), копируем туда содержимое и освобождаем память под старый буффер. хоть и памяти будет жрать больше, работать будет быстрее. так реализовано(по аналогии) в std::string.
3) удачи
Здравствуйте, LIS, Вы писали:
LIS>Здравствуйте, _AK_, Вы писали:
_AK>>3) До глубины души умиляет SafeArrayDelete (явно внутри что-то типа: if (p) {delete p;})
LIS>А можно спросить — чем плохо использование такой конструкции?
Это не плохо, просто смысла в проверке никакого.
Здравствуйте, _nn_, Вы писали:
__>Здравствуйте, LIS, Вы писали:
LIS>>Здравствуйте, _AK_, Вы писали:
_AK>>>3) До глубины души умиляет SafeArrayDelete (явно внутри что-то типа: if (p) {delete p;})
LIS>>А можно спросить — чем плохо использование такой конструкции? __>Это не плохо, просто смысла в проверке никакого.
А как же проверить что объект был удален до этого?
Я вот делаю так:
#define DELETE_P if (p) { delete p; p = NULL; }
и потом когда еще раз пытаюсь удалить объект, то никаких exception не валится (хотя обработку все равно надо ставить).
Здравствуйте, LIS, Вы писали:
LIS>Здравствуйте, _nn_, Вы писали:
__>>Здравствуйте, LIS, Вы писали:
LIS>>>Здравствуйте, _AK_, Вы писали:
_AK>>>>3) До глубины души умиляет SafeArrayDelete (явно внутри что-то типа: if (p) {delete p;})
LIS>>>А можно спросить — чем плохо использование такой конструкции? __>>Это не плохо, просто смысла в проверке никакого.
LIS>А как же проверить что объект был удален до этого? LIS>Я вот делаю так:
LIS>
LIS>#define DELETE_P if (p) { delete p; p = NULL; }
LIS>
Можно писать и так :
delete p;
p=NULL;
LIS>и потом когда еще раз пытаюсь удалить объект, то никаких exception не валится (хотя обработку все равно надо ставить).
рекомендует избавиться от operator const char *. Он разбирает похожий пример, но для указателей.
Еще я не могу оценить смысл виртуальности деструктора.
Здравствуйте, Нахлобуч, Вы писали:
Н>Здравствуйте, roma_k, Вы писали:
_>>Оцените пожалуйста, интересны все мои ошибки, особенно в реализации операторов !!! _>>// Заголовок Н>
Н>class nvString
Н>{
Н>public:
Н> // Return the length of the string in bytes
Н> // Это кэшировать надо
Н> inline int Length() const
Н> { return m_nLength; }
Н>private:
Н> int m_nLength; // Может mutable ?
Н>};
Н>
Здравствуйте, SergeMukhin, Вы писали:
SM>Здравствуйте, roma_k, Вы писали:
_>>Оцените пожалуйста, интересны все мои ошибки, особенно в реализации операторов !!!
SM>3. вместо int надо size_t (где речь идет о длинах)
Почему ?
SM>5. где поддержка UNICODE?
Должна быть?
"For every complex problem, there is a solution that is simple, neat,
and wrong."
Здравствуйте, Вадим Никулин, Вы писали:
ВН>Здравствуйте, Нахлобуч, Вы писали:
Н>>Здравствуйте, roma_k, Вы писали:
_>>>Оцените пожалуйста, интересны все мои ошибки, особенно в реализации операторов !!! _>>>// Заголовок Н>>
Н>>class nvString
Н>>{
Н>>public:
Н>> // Return the length of the string in bytes
Н>> // Это кэшировать надо
Н>> inline int Length() const
Н>> { return m_nLength; }
Н>>private:
Н>> int m_nLength; // Может mutable ?
Н>>};
Н>>
А смысл? Там же нет константных функций, которые чего-то с длиной делают.
HgLab: Mercurial Server and Repository Management for Windows
_>При таком варианте приходится полагатся, на разработчиков компиляторов,а также на разработчиков библиотек.
ну вы даете!
1. причем тут компилятор?
2. неужели могут быть сомнения, что при реализации delete разработчики допустят такую ошибку?
_>Так надежней. Хотя и устарело...
Здравствуйте, Нахлобуч, Вы писали:
_>>>>Оцените пожалуйста, интересны все мои ошибки, особенно в реализации операторов !!! _>>>>// Заголовок Н>>>
Н>>>class nvString
Н>>>{
Н>>>public:
Н>>> // Return the length of the string in bytes
Н>>> // Это кэшировать надо
Н>>> inline int Length() const
Н>>> { return m_nLength; }
Н>>>private:
Н>>> int m_nLength; // Может mutable ?
Н>>>};
Н>>>
Н>А смысл? Там же нет константных функций, которые чего-то с длиной делают.
При любом изменении объекта m_nLength должно меняться. Например, написав
str[5] = 0;
у нас длина будет изменена. Но в операторе мы еще не знаем, как изменится длина, поэтому там мы можем написать
Тода лучше ввести operator [] который смог бы отличить чтение (константный) от записи (неконстантный оператор) и изменять m_nLength (если требуется) в последнем.
HgLab: Mercurial Server and Repository Management for Windows
Здравствуйте, Нахлобуч, Вы писали:
Н>Тода лучше ввести operator [] который смог бы отличить чтение (константный) от записи (неконстантный оператор) и изменять m_nLength (если требуется) в последнем.
Я про это и говорю. Какой у тебя синтаксис operator [] ? Если char &operator [] ( int k ), то надо перечитать мой предыдущий пост. Если ты хочешь прокси-объект возвращать, то действительно можно обойтись без mutable. Только в этом случае на каждый чих ты будешь пересчитывать длину строки. А если она тебе никогда не понадобится?
Здравствуйте, Вадим Никулин, Вы писали:
ВН>Здравствуйте, Нахлобуч, Вы писали:
Н>>Тода лучше ввести operator [] который смог бы отличить чтение (константный) от записи (неконстантный оператор) и изменять m_nLength (если требуется) в последнем.
ВН>Я про это и говорю. Какой у тебя синтаксис operator [] ? Если char &operator [] ( int k ), то надо перечитать мой предыдущий пост. Если ты хочешь прокси-объект возвращать, то действительно можно обойтись без mutable. Только в этом случае на каждый чих ты будешь пересчитывать длину строки. А если она тебе никогда не понадобится?
Ну я про прокси-класс и писал. А пересчет (если он вообще потребуется) займет всего-то ничего:
Здравствуйте, Нахлобуч, Вы писали:
ВН>>Я про это и говорю. Какой у тебя синтаксис operator [] ? Если char &operator [] ( int k ), то надо перечитать мой предыдущий пост. Если ты хочешь прокси-объект возвращать, то действительно можно обойтись без mutable. Только в этом случае на каждый чих ты будешь пересчитывать длину строки. А если она тебе никогда не понадобится?
Н>Ну я про прокси-класс и писал. А пересчет (если он вообще потребуется) займет всего-то ничего:
1. Где прокси-класс? В примере не прокси-класс.
2. Все-равно пересчет — лишние инструкции для процессора. +Затраты на прокси. Н>
Здравствуйте, ioni, Вы писали:
I>Здравствуйте, Аноним, Вы писали:
А>>Дело не в ошибке. Стандартом не определяется что delete на 0 не реагирует. Каждый реализует по своему разумению. Просто в последнее время стало считатся хорошим тоном проверять в операторе delete на 0. В более ранних версиях компиляторов проверки небыло.
I>5.3.5.2 I>.... if the value of the operand of delete is the null pointer the operation has no effect
Не все компиляторы подерживают стандарт. Стандарт в окончательной редакции был вупущен в начале 1998г + пару лет на доработку компиляторов.
Здравствуйте, kaa_t, Вы писали:
А>>>Дело не в ошибке. Стандартом не определяется что delete на 0 не реагирует. Каждый реализует по своему разумению. Просто в последнее время стало считатся хорошим тоном проверять в операторе delete на 0. В более ранних версиях компиляторов проверки небыло.
I>>5.3.5.2 I>>.... if the value of the operand of delete is the null pointer the operation has no effect
_>Не все компиляторы подерживают стандарт. Стандарт в окончательной редакции был вупущен в начале 1998г + пару лет на доработку компиляторов.
ну сейчас уже слава богу 2004
и даже vc6 с этим справляется
Здравствуйте, _nn_, Вы писали:
__>Здравствуйте, kaa_t, Вы писали:
_>>Здравствуйте, _nn_, Вы писали:
LIS>>>>>>
LIS>>>>>>#define DELETE_P LIS>>>>>>
_>>>>Так надежней. Хотя и устарело... __>>>Что имеется вииду под словом "надежней" ?
_>>Вопрос переносимости. __>В чем имено проблема с переносимостью ?
Kaa>Не все компиляторы подерживают стандарт. Стандарт в окончательной редакции был вупущен в начале 1998г.
С высокой долей вероятности можно утверждать что на компиляторе выпущеном до 1998г, проверки на 0 может не оказаться.
Также никто не может гарантировать, что на N компиляторе N версии выпущеном позднее, будет проверяться указатель (всегда есть вероятность обратного).
Здравствуйте, kaa_t, Вы писали:
_>Здравствуйте, _nn_, Вы писали:
__>>Здравствуйте, kaa_t, Вы писали:
_>>>Здравствуйте, _nn_, Вы писали:
LIS>>>>>>>
LIS>>>>>>>#define DELETE_P LIS>>>>>>
_>>>>>Так надежней. Хотя и устарело... __>>>>Что имеется вииду под словом "надежней" ?
_>>>Вопрос переносимости. __>>В чем имено проблема с переносимостью ?
Kaa>>Не все компиляторы подерживают стандарт. Стандарт в окончательной редакции был вупущен в начале 1998г. _>С высокой долей вероятности можно утверждать что на компиляторе выпущеном до 1998г, проверки на 0 может не оказаться. _>Также никто не может гарантировать, что на N компиляторе N версии выпущеном позднее, будет проверяться указатель (всегда есть вероятность обратного).
Полностью согласен, так как реализация не всегда соответсвует стандарту, однако вероятность того что неправильно будет отработанно delete 0 в компиляторе позже 1998 очень мала.
P.S.
На всякий случай можно проверить поведение delete 0 на конкретном компиляторе.
Или пользоватся макросом :
Здравствуйте, <Аноним>, Вы писали:
А>Здравствуйте, SergeMukhin, Вы писали:
SM>>Здравствуйте, kaa_t, Вы писали:
__>>>>Можно писать и так : __>>>>
__>>>>delete p;
__>>>>p=NULL;
__>>>>
_>>>При таком варианте приходится полагатся, на разработчиков компиляторов,а также на разработчиков библиотек.
SM>>ну вы даете! SM>>1. причем тут компилятор? SM>>2. неужели могут быть сомнения, что при реализации delete разработчики допустят такую ошибку?
А>Дело не в ошибке. Стандартом не определяется что delete на 0 не реагирует.
Неправда.
А>Каждый реализует по своему разумению. Просто в последнее время стало считатся хорошим тоном проверять в операторе delete на 0. В более ранних версиях компиляторов проверки небыло.
Неправда.
SM>>что значит устарело? так вроде всегда было! А>Было, будет и нужно так делать. Просто теперь можно проверку не ставить (правда предворительно проверив свой компилятор)
Здравствуйте, kaa_t, Вы писали:
_>Здравствуйте, ioni, Вы писали:
I>>Здравствуйте, Аноним, Вы писали:
А>>>Дело не в ошибке. Стандартом не определяется что delete на 0 не реагирует. Каждый реализует по своему разумению. Просто в последнее время стало считатся хорошим тоном проверять в операторе delete на 0. В более ранних версиях компиляторов проверки небыло.
I>>5.3.5.2 I>>.... if the value of the operand of delete is the null pointer the operation has no effect
_>Не все компиляторы подерживают стандарт. Стандарт в окончательной редакции был вупущен в начале 1998г + пару лет на доработку компиляторов.
Это тоже неправда. Текущий стандарт -- это третья версия языка. Сам язык существует уже очень давно. С самого начала delete не делал ничего с нулевыми указателями.