Почему строки VC++ не учитываются?
Стандарт STL не требует пересчета из std::string. Но на самом деле большинство реализаций C++ предоставляют пересчитанные строки с копированием при записи, что позволяет передавать строку по значению как примитивный тип. Кроме того, эти реализации (по крайней мере, g++) используют атомарные операции, что делает эти строки свободными от блокировок и безопасными для потоков.
Простой тест показывает семантику копирования при записи:
#include <iostream>
#include <string>
using namespace std;
void foo(string s)
{
cout<<(void*)s.c_str()<<endl;
string ss=s;
cout<<(void*)ss.c_str()<<endl;
char p=ss[0];
cout<<(void*)ss.c_str()<<endl;
}
int main()
{
string s="coocko";
cout<<(void*)s.c_str()<<endl;
foo(s);
cout<<(void*)s.c_str()<<endl;
}
Только два адреса печатаются точно после того, как был использован непостоянный элемент.
Я протестировал этот код с использованием компилятора HP, GCC и Intel и получил аналогичные результаты - строки работают как контейнеры для копирования при записи.
С другой стороны, VC++ 2005 ясно показывает, что каждая строка полностью скопирована.
Зачем?
Я знаю, что в VC++6.0 была ошибка, в которой реализована не-поточная реализация подсчета ссылок, что приводило к случайным программным сбоям. Это причина? Они просто боятся использовать повторный подсчет, даже если это обычная практика? Они предпочитают вообще не использовать реф-счет для решения проблемы?
Спасибо
5 ответов
Я думаю, что все больше и больше std::string
реализации отойдут от пересчета / копирования при записи, поскольку это часто встречная оптимизация в многопоточном коде.
См. Статью Херба Саттера " Оптимизации, которых нет" (в многопоточном мире).
Фактическая STL требует, чтобы при использовании подсчета ссылок семантика была такой же, как и для версии без подсчета ссылок. Это не тривиально для общего случая.(Вот почему вы не должны писать свой класс on string).
Из-за следующей ситуации:
std::string x("This is a string");
char& x5 = x[5];
std::string y(x);
x5 = '*';
Смотрите: http://www.sgi.com/tech/stl/string_discussion.html для получения более подробной информации
Как заявили Martin & Michael, Copy On Write (COW) часто доставляет больше хлопот, чем стоит, для дальнейшего чтения посмотрите эту превосходную статью Келвина Хенни о Mad COW Disease, и я считаю, что именно Андрей Александреску заявил, что Small String Optimization работает лучше во многих приложениях (но я не могу найти статью).
Оптимизация малых строк - это то, где вы увеличиваете строковый объект и избегаете выделения кучи для маленьких строк. Реализация игрушек будет выглядеть примерно так:
class string {
char *begin_, *end_, *capacity_;
char buff_[64]; // pick optimal size (or template argument)
public:
string(const char* str)
{
size_t len = strlen(str);
if (len < sizeof(buff_))
{
strcpy(buff_, str);
begin_ = buff_;
capacity_ = buff_ + sizeof(buff_);
}
else
{
begin_ = strdup(str);
capacity_ = begin_ + len;
}
end_ = begin_+len;
}
~string()
{
if (begin_ != buff_)
free(begin_); // strdup requires free
}
// ...
};
Возможно, Microsoft определила, что копирование строк не было большой проблемой, так как почти весь код C++ использует передачу по ссылке везде, где это возможно. Поддержание счетчика ссылок имеет накладные расходы в пространстве и времени (игнорируя блокировку), которые, возможно, они решили, что не стоит платить.
А может и нет. Если это вас беспокоит, вы должны профилировать свое приложение, чтобы определить, является ли копирование строки серьезными накладными расходами и переключено ли оно на другую реализацию строки.
Это не главная причина, но я видел много неверного кода на платформе win32, который делает что-то вроде const_cast< char* >( str.c_str() )
,
Может быть, Microsoft знает об этом и заботится о разработчиках:)