Создание Класса Строка (С++)
Определить конструктор с одним параметром целого типа – длина строки,
который можно использовать как конструктор умолчания.
Определить конструктор, который копирует в новую строку n первых символов другой
строки и который можно использовать как конструктор копирования.
Определить деструктор.
Определить преобразования из вещественного числа в строку, представляющую это
число, и из строки в вещественное число.
Собственно вот.. Заранее спасибо
{
// Конструктор по умолчанию убираем - у нас будет свой
String(size_t length = 0); // Конструктор, который можно использовать как конструктор по умолчанию
// Конструктор копирования также убираем - у нас свой
String(const String& s, size_t symbols = -1); // Конструктор, который можно использовать как конструктор копирования
operator float () const; // Оператор преобразования строка => число
String (double d); // Конструирование из числа
}
Соответственно, дорабатываем реализации методов.
#include <iostream.h>
#include <math.h>
#pragma hdrstop
#pragma argsused
#pragma warning (disable : 4996)
class blabla
{
private:
size_t maxlen;
size_t len;
char* S;
public:
blabla(size_t len = 0);
friend ostream& operator<<(ostream&, const blabla&);
friend istream& operator>>(istream&, blabla&);
blabla(const char* s);
blabla(const blabla &s, size_t symbols = -1);
~blabla() { delete [] S; }
size_t GetLen() const { return len; }
size_t GetMaxLen() const { return maxlen; }
operator double ();
blabla (double d);
};
istream& operator>>(istream& is, blabla& str)
{
const int N = 65536;
char temp[N] = {0};
size_t Length = 0,
Total = 0;
do
{
is.getline(temp + Total, N - Total);
Length = strlen(temp + Total);
if(Length == 0)
break;
Total += Length + 1;
temp[Total - 1] = '\n';
} while(Total < N);
temp[Total - 1] = 0;
str = temp;
return is;
}
ostream& operator<<(ostream& os, const blabla& str)
{
os << str.S;
return os;
}
blabla::blabla(size_t l)
:maxlen (l), len (0), S(new char [maxlen+1])
{
S[0] = 0;
}
blabla::blabla(const char* s)
:maxlen (strlen(s)), len(maxlen), S (new char[len+1])
{
strcpy (S, s);
}
blabla::blabla(const blabla &s, size_t symbols)
{
size_t slen = s.GetLen();
len = maxlen = ((symbols == -1) ? slen : ((symbols > slen) ?slen : symbols));
S = new char [len+1];
strncpy (S, s.S, len);
S[len] = 0;
}
blabla::operator double ()
{
return atof (S);
}
blabla::blabla (double d)
{
len = strlen(S);
}
int main(int argc, char* argv[])
{
blabla a;
cout << "Input string:\n";
cin >> a;
cout<<double(a);
return 0;
}
Кто может посмотреть код на наличие ошибки? что-то не так он работает х_Х
здесь получаем
[BCC32 Warning] File5.cpp(63): W8012 Comparing signed and unsigned values
-1 - signed
Гениальная строчка. К сожалению моего опыта далеко не хватает чтобы осознать зачем так надо было писать) уникальная конструкция.
size_t symbols = -1 определяется вначале, можно приравнять вначале 0 и сравнивать с 0-м
.....
len = maxlen = ((!symbols) ? slen : ((symbols > slen) ?slen : symbols));
Разработать реализацию класса String, позволяющего оперировать с
текстовыми строками, как с базовыми типами данных. Класс должен обеспечивать выполнение следующих операции:
- инициализация (оператор присваивания =) +
- конкатенация строк (оператор +) +
- сравнение строк (операторы <,>,<=,>=,==, !=)
- определение длины (метод length) +
- поиск подстроки текста (метод find)
- поиск и замена текста (метод replace)
- вывод в стандартный поток вывода (>>); +
- ввод из стандартного потока ввода (<<). +
Помогите плиз сделать остальное!
Разработать реализацию класса String, позволяющего оперировать с
текстовыми строками, как с базовыми типами данных. Класс должен обеспечивать выполнение следующих операции:
- инициализация (оператор присваивания =) +
- конкатенация строк (оператор +) +
- сравнение строк (операторы <,>,<=,>=,==, !=)
- определение длины (метод length) +
- поиск подстроки текста (метод find)
- поиск и замена текста (метод replace)
- вывод в стандартный поток вывода (>>); +
- ввод из стандартного потока ввода (<<). +
Помогите плиз сделать остальное!
То, что уже сделано - это то что помечено знаком +?
Чтобы вам помогли сделать все остальное, вам нужно показать что вы уже сделали.
Выложите имеющееся у вас определение класса, и функцию инициализации (чтобы было понятно как у вас реализовано хранение содержимого строки).
[BCC32 Warning] File5.cpp(63): W8012 Comparing signed and unsigned values
У Вас тут происходит сравнение беззнакового целого symbols (типа size_t) и -1. Вот компилятор и предупреждает.
Update: Упс, отстал от обсуждения, сорри. :)
То, что уже сделано - это то что помечено знаком +?
Чтобы вам помогли сделать все остальное, вам нужно показать что вы уже сделали.
Выложите имеющееся у вас определение класса, и функцию инициализации (чтобы было понятно как у вас реализовано хранение содержимого строки).
Не стал создавать новую тему, чтобы не было тематической тавтологии, обычно это чревато)
Да, то, что плюс, есть.
Вот код, без lenght и конкатенации, чтобы компактно вышло:
struct rep
{
char* s;
int n;
};
rep *p;
public:
string(char *);
string();
string(string &);
string& operator=(char *);
string& operator=(string &);
~string();
friend ostream& operator<<(ostream&, string&);
friend istream& operator>>(istream&, string&);
friend int operator==(string& x, char* s)
{return strcmp(x.p->s, s) == 0; }
friend int operator==(string& x, string& y)
{return strcmp(x.p->s, y.p->s) == 0; }
};
string::string()
{
p = new rep;
p->s = 0;
p->n = 1;
}
string::string(char* s)
{
p = new rep;
p->s = new char[ strlen(s)+1 ];
strcpy(p->s, s);
p->n = 1;
}
string::string(string& x)
{
x.p->n++;
p = x.p;
}
string::~string()
{
if (--p->n == 0) {
delete p->s;
delete p;
}
}
string& string::operator=(char* s)
{
if (p->n > 1) { // разъединить себя
p->n--;
p = new rep;
}
else if (p->n == 1)
delete p->s;
p->s = new char[ strlen(s)+1 ];
strcpy(p->s, s);
p->n = 1;
return *this;
}
string& string::operator=(string& x)
{
x.p->n++;
if (--p->n == 0) {
delete p->s;
delete p;
}
p = x.p;
return *this;
}
ostream& operator<<(ostream& s, string& x)
{
return s << x.p->s << "\n";
}
istream& operator>>(istream& s, string& x)
{
char buf[256];
s >> buf;
x = buf;
cout << x << "\n";
return s;
}
Насчет того что нужно сделать:
У вас уже есть операторы "==":
{return strcmp(x.p->s, s) == 0; }
friend int operator==(string& x, string& y)
{return strcmp(x.p->s, y.p->s) == 0; }
По аналогии с ними можно сделать остальные, вот только тип возвращаемого значения (и в новых операторах, и в имеющихся) надо бы сделать bool:
{return strcmp(x.p->s, y.p->s) == 0; }
friend bool operator<(string& x, string& y)
{return strcmp(x.p->s, y.p->s) < 0; }
friend bool operator>(string& x, string& y)
{return strcmp(x.p->s, y.p->s) > 0; }
friend bool operator<=(string& x, string& y)
{return strcmp(x.p->s, y.p->s) <= 0; }
friend bool operator>=(string& x, string& y)
{return strcmp(x.p->s, y.p->s) >= 0; }
friend bool operator<(string& x, string& y)
{return strcmp(x.p->s, y.p->s) < 0; }
// также можно сделать аналогичные операторы для сравнения string с char*
Поиск можно сделать так. Сделать функцию которая принимает на вход строку, которую нужно найти в текущей, и номер позиции начиная с которой надо искать. И еще нужно создать какое-либо значение, которое будет возвращаться в случае, если строка не найдена.
// ...
public:
// если компилятор не позволит здесь такое присваивание
// то сделать его после объявления класса:
static const int npos = -1;
int find(const char*, int = 0);
int find(const string&, int = 0);
// ...
};
// ...
int string::find(const char *s, int n)
{
const int len = strlen(p->s);
const int lenx = strlen(s);
if(lenx == 0 || lenx > len || n >= len || n + lenx > len)
return npos;
if(n < 0)
n = 0;
for(; n <= len - lenx; ++n)
{
int i = 0;
while(i < lenx && p->s[n + i] == s)
++i;
if(i == lenx)
return n;
}
return npos;
}
int string::find(const string& x, int n)
{
return find(x.p->s, n);
}
// пример вызова
int main()
{
// ...
n = str1.find(str2);
if(n != string::npos)
// ...
}
А вот насчет replace будет посложнее - надо подумать, может позже напишу (или кто-то другой)...
А пока еще замечания
1. Непонятно назначение другой переменной из структуры rep - переменная int n...
2. Конструктор копирования:
{
x.p->n++;
p = x.p;
}
Происходит присваивание значения указателя p текущего объекта к значению p объекта-аргумента. В итоге у вас получается, что после вызова конструктора копирования имеются два разных объекта string с общим содержимым, и впоследствии если в одном из них строка будет изменена, то и в другом тоже изменится (указатели-то на одно и то же указывают). Думаю что это неправильно, и надо как-то так:
{
p = new rep;
p->s = new char[strlen(x.p->s) + 1];
strcpy(p->s, x.p->s);
p->n = x.p->n;
p->n++;
}
По аналогии с ними можно сделать остальные
Спасибо, сделано, все работает, и метод find тоже.
это счетчик ссылок.
Насчет коструктора копирования - да, недодумал. Все-таки не зря мне казалось, что там что-то не так)
Благодарен за подробные объяснения.
Если Вы(или кто-то другой) поможете мне с replace - будет совсем замечательно)
И еще, я видимо неправильно написал конкатенацию строк, не могли бы Вы проверить?
{
string temp;
temp.length = strlen(p->s) + strlen(s);
temp = new char[temp.length + 1];
strcpy(temp, p->s);
strcat(temp, s);
return temp;
}
string& string::operator+(char* s)
{
string temp;
[COLOR="Red"]// length, я так понимаю, фунция класса string, а не переменная?
// почему тогда использование length как будто это переменная?[/COLOR]
temp.length = strlen(p->s) + strlen(s);
temp = new char[temp.length + 1];
[COLOR="Red"]// для функций strcpy и strcat оба аргумента должны иметь тип char*,
// а не string как это сделано здесь[/COLOR]
strcpy(temp, p->s);
strcat(temp, s);
return temp;
}
И я бы наверно так переписал оператор:
{
char * temp = new char[strlen(p->s) + strlen(s) + 1];
strcpy(temp, p->s);
strcat(temp, s);
string result(temp);
delete[] temp;
return result;
}
А по поводу функции replace - попробовал сделать с использованием уже имеющейся find и по аналогии с этой темой:
http://forum.codenet.ru/showthread.php?t=57967
Предвидя замечания Kogrom'а, скажу что писал эту функцию до того как занялся той темой (здесь просто времени не нашел чтобы запостить), при желании и здесь можно упростить (сделать более эффективный алгоритм), и сделать более удобочитаемым, но пока нет на это времени, да и может автору будет пока и этого достаточно...
// ...
public:
// ...
void replace(const char*, const char*, bool = false, int = 0);
void replace(const string&, const string&, bool = false, int = 0);
// ...
};
void string::replace(const char *s1, const char *s2, bool replaceAll, int n)
{
const int len = strlen(p->s);
const int len1 = strlen(s1);
const int len2 = strlen(s2);
if(len == 0 || len1 == 0 || len1 > len || n + len1 > len)
return;
int * numpos = new int[len / len1 + 1];
int pos1 = 0;
do
if((n = find(s1, n)) != npos)
numpos[pos1++] = n++;
while(n != npos && replaceAll);
if(!pos1)
{
delete[] numpos;
return;
}
char * newstr = new char[len + (len2 - len1) * pos1 + 1];
int i;
for(n = 0, i = 0, int pos2 = 0; i < len; ++n, ++i)
if(pos2 == pos1 || i < numpos[pos2])
newstr = p->s[n]
else
{
for(int j = 0; j < len2; ++i, ++j)
newstr = s2[j];
++pos2;
--i;
n += len1 - 1;
}
newstr = '\0';
delete[] p->s;
p->s = newstr;
delete[] numpos;
}
void string replace(const string& x1, const string& x2, bool replaceAll, int n)
{
replace(x1.p->s, x2.p->s, replaceAll, n);
}
И не могли бы вы объяснить, какой параметр что значит для метода replace?
Насчет эффективности можете не беспокоиться, я очень вам благодарен, что вы мне помогаете. Даже не надеялся, что так можно.
Ну это вам должно быть виднее. Задача-то ставилась не мне. И не мною. :)
Но вообще я думаю что логично было бы и оператор конкатенации перегрузить (раз уж вы перегружаете другие операторы) - чтобы можно было вызывать и в случае string + char (что вы уже пытались сделать и что я вам исправил), и в случае string + string. Какие-то трудности с этим - не знаете как сделать?
Если вы о параметрах вызова (то что передается в функцию):
- const char *s1 - строка char, которую будете заменять в исходной строке
- const char *s2 - строка char, на которую будете заменять в исходной строке
- bool replaceAll - нужно ли заменить все найденные вхождения строки s1 на строку s2, или только первое (если таковое есть)? true - все, false - только первое
- int n - с какой позиции (с какого символа) искать и, соответственно, заменять
// ...
// все три варианта равнозначны
testString.replace("str1", "str2", false, 0);
testString.replace("str1", "str2", false);
testString.replace("str1", "str2");
Можно убрать значения по умолчанию (сделав параметры обязательными), или поменять значения по умолчанию.
Да, у меня не получается =/ Вроде бы понимаю, но видимо не до конца.
Насчет надо-не надо, я думал, есть какие-то правила хорошего тона, т.е. раз уж все операторы перегружены, то и этот нужно)
Спасибо, что объяснили. Меня смущали значения, установленные по умолчанию.
Можно просто добавить видоизмененную версию оператора, в которой вместо аргуиента char* передается аргумент string:
{
char * temp = new char[strlen(p->s) + strlen(s.p->s) + 1];
strcpy(temp, p->s);
strcat(temp, s.p->s);
string result(temp);
delete[] temp;
return result;
}
А можно по-другому (аналогично перегруженным find и replace из моих предыдущих вариантов) - внутри новой версии оператора вызывать (для получения результата) оператор + который был ранее создан для аргумента char*:
{
return operator+(s.p->s);
}
Нужно ли или нет перегружать оператор для класса для разных типов аргумента оператора - зависит от класса (его конструкции и назначения), для которого пишется функция-оператор. Иногда это бывает полезно (как, похоже, в вашем случае), а иногда - не имеет смысла.
Извините за беспокойство.