class S{
int size;
char *buff;
public:
S() {buff=0;size=0;}
S(const char *t) {size=strlen(t); buff=strdup(t);}
~S() {free(buff);}
char *data() {return buff;}
};
|
Нам хочется научится складывать разные строки друг с другом.
Первая очевидная реализация которую можно нарисовать, может выглядеть приблизительно так:
class S{
...
public:
...
S(const S& obj) { // copy
size=obj.size; buff=strdup(obj.buff);
}
~S() { // free
free(buff);
}
friend S operator+(S &s1,S &s2) { // cond
S res;
res.size=s1.size+s2.size;
res.buff=(char*)malloc(res.size+1);
strcpy(res.buff,s1.buff);
strcat(res.buff,s2.buff);
return res;
}
};
|
Использовать полученные операции очень просто:
S s1("one!");
S s2("two!");
S s3("tree!");
S s=s1+s2+s3;
|
new: one! new: two! new: tree! con: one! + two! copy: one!two! del: one!two! con: one!two! + tree! copy: one!two!tree! del: one!two!tree! del: one!two! one!two!tree! |
Довольно много лишних действий. Можестсво копирований, удалений и выделений памяти. Тем более что в 2003 Visual Studio и выше распределитель памяти был вырезан и выделение работает дико медленно.
Жуть?. Во многих реализациях STL всё на самом деле ещё хуже.
static Concat operator+(S &s1,S &s2) {return Concat(s1,s2);}
struct Concat{
S &s1;
S &s2;
Concat(S &s1,S &s2):s1(s1),s2(s2) {}
};
class S{
...
public:
...
void operator=(Concat &con) {
size=con.s1.size+con.s2.size;
buff=(char*)realloc(buff,size+1);
memcpy(buff,con.s1.buff,con.s1.size);
memcpy(buff+con.s1.size,con.s2.buff,con.s2.size);
buff[size]=0;
}
};
|
Пример использования всё такой же:
S s1("test1");
S s2("test2");
S s =s1+s2;
|
template<class T1,class T2>
struct Concat{
T1 &p1;
T2 &p2;
Concat(T1 &s1,T2 &s2):p1(s1),p2(s2) {}
int size() {return p1.size()+p2.size();}
char *set(char *t) {return p2.set(p1.set(t));}
};
class S{
...
public:
template<class T1,class T2>void operator=(Concat<T1,T2> &con) {
sz=con.size();
ref=(char*)realloc(ref,sz+1);
*con.set(ref)=0;
}
char *set(char *d)
{
memcpy(d,ref,sz);
return d+sz;
}
};
|
Пример использования:
String a("1"),b("22"),c("333"),d("4444"),e("55555"),f("666666");
String r=a+b+c+d+e+f;
printf("%s\n",r.data());
|
Итого, в результате сложения шести строк возникает некий объект, который содержит в себе информацию о всех строках. Строка принимающая этот объект, сразу выделяет нужную память и собирает итоговую строку. Для того чтобы убедиться что всё работает красиво, можно заглянуть в дизасемблер [3.asm]. Если включить оптимизацию по размеру, всё выглядит ещё красивее. Совсем идеальный код получается с помощью gcc (если избавиться от шаблонов, для получения работающего варианта).
template<class T1,class T2>
struct concatenator{
typedef std::string::iterator itt;
T1 &p1;
T2 &p2;
concatenator(T1 &s1,T2 &s2):p1(s1),p2(s2) {}
int size() {return p1.size()+p2.size();}
template<class T1,class T2>
itt set(concatenator<T1,T2>*obj,itt it) {
return set(&obj->p2,set(&obj->p1,it));
}
itt set(std::string*s,itt it) {
return std::copy(s->begin(),s->end(),it);
}
operator std::string() {
std::string obj;
obj.resize(p1.size()+p2.size());
set(this,obj.begin());
return obj;
}
};
template<class T>static std::concatenator<T,std::string>operator+(T &a,std::string &b) {
return std::concatenator<T,std::string>(a,b);
}
|
Пример использования:
std::string a("1"),b("22"),c("333"),d("4444"),e("55555"),f("666666");
std::string r=a+b+c+d+e+f;
|
Работает так же хорошо. Ни одного лишнего выделения памяти. Прилагается готовый вариант программы, а так же его усовершенствованный вариант (добавлена работа с char*). К сожалению ни один из них пока не заставил работать в gcc, последний так же не компилируется в Visual Studio 6.0.
[Proteus 11.02.2009] icq:133575351 lawnmower-man@mail.ru