Kiedy należy używać, std::string
a kiedy należy używać char*
do zarządzania tablicami char
s w C ++?
Wydaje się, że powinieneś użyć, char*
jeśli wydajność (szybkość) jest kluczowa i jesteś skłonny zaakceptować ryzykowny biznes ze względu na zarządzanie pamięcią.
Czy są inne scenariusze do rozważenia?
char *
prawie nie ma narzutów. Dokładniestd::string
nie wiem, co ma narzut , prawdopodobnie zależy to od implementacji. Prawie nie oczekuję, że narzut będzie znacznie większy niż w przypadku gołego wskaźnika char. Ponieważ nie posiadam kopii standardu, nie mogę tak naprawdę szczegółowo określić żadnych gwarancji udzielonych przez standard. Wszelkie różnice w wydajności prawdopodobnie będą się różnić w zależności od wykonywanych operacji.std::string::size
może przechowywać rozmiar obok danych znakowych i dzięki temu być szybszym niżstrlen
.append(const string&)
Iappend(const char*, size_t)
zamiastoperator+=()
.Mój punkt widzenia jest następujący:
źródło
Użycie nieprzetworzonego ciągu
Tak, czasami naprawdę możesz to zrobić. Używając const char *, tablic char alokowanych na stosie i literałów łańcuchowych możesz to zrobić w taki sposób, że nie ma w ogóle alokacji pamięci.
Pisanie takiego kodu często wymaga więcej przemyślenia i staranności niż użycie łańcucha lub wektora, ale przy użyciu odpowiednich technik można to zrobić. Dzięki odpowiednim technikom kod może być bezpieczny, ale zawsze musisz się upewnić, że kopiując do char [] albo masz jakieś gwarancje co do długości kopiowanego ciągu, albo z wdziękiem sprawdzasz i obsługujesz zbyt duże łańcuchy. Nie robienie tego sprawiło, że ta rodzina funkcji strcpy zyskała reputację niebezpiecznej.
Jak szablony mogą pomóc w pisaniu bezpiecznych buforów znaków
Jeśli chodzi o bezpieczeństwo buforów char [], pomocne mogą być szablony, ponieważ mogą one tworzyć enkapsulację do obsługi rozmiaru bufora za Ciebie. Szablony takie jak ten są wdrażane np. Przez firmę Microsoft w celu zapewnienia bezpiecznych zamienników strcpy. Poniższy przykład pochodzi z mojego własnego kodu, prawdziwy kod ma znacznie więcej metod, ale to powinno wystarczyć, aby przekazać podstawową ideę:
template <int Size> class BString { char _data[Size]; public: BString() { _data[0]=0; // note: last character will always stay zero // if not, overflow occurred // all constructors should contain last element initialization // so that it can be verified during destruction _data[Size-1]=0; } const BString &operator = (const char *src) { strncpy(_data,src,Size-1); return *this; } operator const char *() const {return _data;} }; //! overloads that make conversion of C code easier template <int Size> inline const BString<Size> & strcpy(BString<Size> &dst, const char *src) { return dst = src; }
źródło
char*
stringi nie zawsze znajdują się na stosie.char *str = (char*)malloc(1024); str[1024] = 0;
Jedną z sytuacji, których MUSISZ użyć,
char*
a nie,std::string
jest sytuacja, gdy potrzebujesz statycznych stałych łańcuchowych. Powodem tego jest to, że nie masz żadnej kontroli nad kolejnością modułów, które inicjalizują ich zmienne statyczne, a inny obiekt globalny z innego modułu może odwoływać się do twojego ciągu przed jego zainicjowaniem. http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Static_and_Global_Variablesstd::string
plusy:std::string
minusy: - dwie różne instancje ciągu STL nie mogą współużytkować tego samego podstawowego buforu. Więc jeśli przekażesz wartość, zawsze otrzymasz nową kopię. - istnieje pewna utrata wydajności, ale powiedziałbym, że jeśli nie masz specjalnych wymagań, jest to znikome.źródło
Należy rozważyć użycie
char*
w następujących przypadkach:Właściwie w C ++
char*
są często używane do ustalonych małych słów, jako opcji, nazwy pliku itp.źródło
Kiedy używać std :: string w języku C ++:
Kiedy używać znaku *
źródło
Użyj (const) char * jako parametrów, jeśli piszesz bibliotekę. Implementacje std :: string różnią się między różnymi kompilatorami.
źródło
Jeśli chcesz używać bibliotek C, będziesz musiał radzić sobie z napisami C. To samo dotyczy sytuacji, gdy chcesz udostępnić swój interfejs API C.
źródło
Możesz spodziewać się większości operacji na std :: string (takich jak np
find
) Będzie tak zoptymalizowana, jak to tylko możliwe, więc prawdopodobnie będą wykonywać co najmniej tak dobrze, jak czysty odpowiednik w C.Warto również zauważyć, że iteratory std :: string dość często mapują wskaźniki do podstawowej tablicy char. Zatem każdy algorytm, który opracujesz na podstawie iteratorów, jest zasadniczo identyczny z tym samym algorytmem na szczycie char * pod względem wydajności.
Rzeczy, na które należy zwrócić uwagę, to np.
operator[]
- większość implementacji STL nie wykonuje sprawdzania granic i należy to przetłumaczyć na tę samą operację na bazowej tablicy znaków. AFAIK STLPort może opcjonalnie wykonywać sprawdzanie granic, w którym to momencie ten operator byłby nieco wolniejszy.Więc co daje użycie std :: string? Zwalnia cię z ręcznego zarządzania pamięcią; zmiana rozmiaru tablicy staje się łatwiejsza i generalnie trzeba mniej myśleć o zwalnianiu pamięci.
Jeśli martwisz się o wydajność podczas zmiany rozmiaru ciągu, istnieje
reserve
funkcja, która może Ci się przydać.źródło
jeśli używasz tablicy znaków w podobnym tekście itp. użyj std :: string, bardziej elastycznego i łatwiejszego w użyciu. Jeśli używasz go do czegoś innego, jak przechowywanie danych? używaj tablic (preferuj wektory)
źródło
Nawet jeśli wydajność ma kluczowe znaczenie, lepiej wykorzystujesz
vector<char>
- umożliwia alokację pamięci z wyprzedzeniem (metoda Reserve ()) i pomaga uniknąć wycieków pamięci. Użycie vector :: operator [] prowadzi do narzutu, ale zawsze można wyodrębnić adres bufora i zindeksować go dokładnie tak, jakby był to znak *.źródło
AFAIK wewnętrznie większość std :: string implementuje kopiowanie podczas zapisu, odwołuje się do semantyki liczonej, aby uniknąć narzutu, nawet jeśli ciągi nie są przekazywane przez referencję.
źródło