C++ dize işleme - C++ string handling

C ++ dilini programlama desteği vardır dize işleme çoğunlukla onun uygulanan, standart kütüphanede . Dil standardı, bazıları C'den devralınan , bazıları dilin sınıflar ve RAII gibi özelliklerini kullanmak için tasarlanmış birkaç dize türü belirtir . Bunlardan en çok kullanılanı std::string'dir .

C++'ın ilk sürümleri yalnızca "düşük düzeyli" C dize işleme işlevselliğine ve kurallarına sahip olduğundan, dize işleme sınıfları için birden çok uyumsuz tasarım yıllar içinde tasarlanmıştır ve hala yerine kullanılmaktadır std::stringve C++ programcılarının birden çok kuralı işlemesi gerekebilir. tek bir uygulamada.

Tarih

Std :: string tipi 1998 yılından beri standart C ++ ana dize veri türü olduğunu, ama her zaman C ++ bir parçası değildi. C'den, C++ , bir işaretçi tarafından ilk öğelerine işlenen boş sonlandırılmış dizeleri kullanma kuralını ve bu tür dizeleri işleyen bir işlev kitaplığını devraldı . Modern standart C++'da, "merhaba" gibi bir dize değişmezi hala NUL ile sonlandırılmış bir karakter dizisini belirtir.

Bir dize türünü uygulamak için C++ sınıflarının kullanılması, otomatikleştirilmiş bellek yönetiminin çeşitli yararları, sınır dışı erişim riskinin azalması ve dize karşılaştırma ve birleştirme için daha sezgisel sözdizimi sunar. Bu nedenle, böyle bir sınıf yaratmak çok cazipti. Yıllar içinde, C ++ uygulama, kütüphane ve çerçeve geliştiriciler böyle de biri olarak kendi uyumsuz dize temsillerini, üretilen AT & T 'nin Standart Bileşenler kütüphanede (bu tür ilk uygulama, 1983) ya da CString Microsoft'un tip MFC . İken std :: string standardize dizeleri, eski uygulamalar hala yaygın olarak "neredeyse imkansız" yapma istenen dize karar programcıları C ++ programlarında birden dize türlerini kullanarak ve gerektiren önlemek için, bu tür özel dize türleri ve kütüphaneler C tarzı dizeleri bekleyebiliriz içerirler Bir projeye başlamadan önce temsil.

C++'ın tarihi üzerine 1991 tarihli bir retrospektifinde, mucidi Bjarne Stroustrup , C++ 1.0'da standart bir dizi türünün (ve diğer bazı standart türlerin) olmamasını, geliştirmesinde yaptığı en büyük hata olarak nitelendirdi; "bunların yokluğu, herkesin tekerleği yeniden icat etmesine ve en temel sınıflarda gereksiz bir çeşitliliğe yol açtı".

Uygulama sorunları

Çeşitli satıcıların dize türleri, farklı uygulama stratejilerine ve performans özelliklerine sahiptir. Özellikle, bazı dize türleri , yazma üzerine kopyalama stratejisi kullanır;

string a = "hello!";
string b = a; // Copy constructor

Aslında içeriğini kopyalamaz bir etmek , b ; bunun yerine, her iki dize de içeriklerini paylaşır ve içerikteki referans sayısı artırılır. Gerçek kopyalama, herhangi bir dizeye bir karakter eklemek gibi bir mutasyon işlemi, dizelerin içeriğini farklı hale getirene kadar ertelenir. Yazma üzerine kopyalama, dizeleri kullanarak kodda önemli performans değişiklikleri yapabilir (bazı işlemleri çok daha hızlı ve bazılarını çok daha yavaş hale getirir). Gerçi std :: string artık kullanır, birçok (belki de çoğu) alternatif dize kütüphaneleri hala kopya üzerinde yazma dizeleri uygulamak.

Bazı dize uygulamaları bayt yerine 16 bit veya 32 bit kod noktalarını depolar ; bu, Unicode metninin işlenmesini kolaylaştırmak için tasarlanmıştır . Ancak bu, std::string'den veya bayt dizilerinden bu türlere dönüştürmenin yavaş ve genellikle kayıplı bir işlem olduğu, "yerel ayara" bağlı olduğu ve istisnalar oluşturabileceği anlamına gelir. Değişken genişlikli UTF-16 kodlaması kullanıma sunulduğunda 16 bit kod birimlerinin işleme avantajları ortadan kalktı (ancak Windows gibi 16 bitlik bir API ile iletişim kurmanız gerekiyorsa hala avantajlar var). QT sitesindeki QString bir örnektir.

Üçüncü taraf dize uygulamaları, alt dizeleri çıkarmak veya karşılaştırmak veya metinde arama yapmak için sözdiziminde de önemli ölçüde farklılık gösterdi.

Standart dize türleri

Std :: string sınıfı beri bir metin dizesi için standart temsilidir C ++ 98 . Sınıf, karşılaştırma, birleştirme, bul ve değiştir gibi bazı tipik dize işlemleri ve alt dizeleri elde etmek için bir işlev sağlar . Bir std::string , C stili bir dizeden oluşturulabilir ve birinden C stili bir dize de elde edilebilir.

Dizeyi oluşturan bireysel birimler, her biri en az (ve hemen hemen her zaman) 8 bit olmak üzere char türündedir . Modern kullanımda bunlar genellikle "karakterler" değil, UTF-8 gibi çok baytlı karakter kodlamasının parçalarıdır .

Yazma üzerine kopyalama stratejisine, yararlı bir optimizasyon olarak kabul edildiğinden ve neredeyse tüm uygulamalar tarafından kullanıldığından, std::string için ilk C++ Standardı tarafından kasıtlı olarak izin verildi . Bununla birlikte, hatalar vardı, özellikle [] operatörü , yerinde C dize manipülasyonlarını taşımayı kolaylaştırmak için const olmayan bir referans döndürdü (bu tür kodlar genellikle karakter başına bir bayt varsayılır ve bu nedenle bu iyi olmayabilir. fikir!) Bu, hemen hemen her zaman yalnızca dizeyi incelemek ve onu değiştirmek için kullanılmamasına rağmen bir kopya oluşturması gerektiğini gösteren aşağıdaki kodu sağladı:

  std::string original("aaaaaaa");
  std::string string_copy = original; // make a copy
  char* pointer = &string_copy[3]; // some tried to make operator[] return a "trick" class but this makes it complex
  arbitrary_code_here(); // no optimizations can fix this
  *pointer = 'b'; // if operator[] did not copy, this would change original unexpectedly

Bu, bazı uygulamaların yazma üzerine kopyalamadan vazgeçmesine neden oldu. Ayrıca, çok iş parçacıklı uygulamalarda, referans sayısını incelemek veya değiştirmek için gereken kilitleme nedeniyle ek yükün, modern işlemcilerde küçük dizeleri kopyalama ek yükünden daha büyük olduğu keşfedildi (özellikle bir işaretçi boyutundan daha küçük dizeler için). En sonunda C++11'de optimizasyona izin verilmedi , bunun sonucunda bir fonksiyona argüman olarak bir std::string bile geçirildi , yani.

void print(std::string s) { std::cout << s; }

dizenin tam bir kopyasını yeni ayrılan belleğe gerçekleştirmesi beklenmelidir. Bu tür kopyalamadan kaçınmak için kullanılan yaygın deyim, const referansı olarak geçmektir :

void print(const std::string& s) { std::cout << s; }

Gelen C ++ 17 yeni ilave string_view sadece bir işaretçi ve uzunluk salt okunur veri olduğu sınıf, yukarıdaki örneklerden birine göre çok daha hızlı bir bağımsız değişken geçen yapar:

void print(std::string_view s) { std::cout << s; }
...
  std::string x = ...;
  print(x); // does not copy x.data()
  print("this is a literal string"); // also does not copy the characters!
...


Örnek kullanım

#include <iostream>
#include <iomanip>
#include <string>

int main() {
    std::string foo = "fighters";
    std::string bar = "stool";
    if (foo != bar) std::cout << "The strings are different!\n";
    std::cout << "foo = " << std::quoted(foo)
              << " while bar = " << std::quoted(bar);
}

İlgili sınıflar

std :: string bir olduğunu typedef belirli bir nesnelleştirilmesi için std :: basic_string şablon sınıfına . Tanımı <string> başlığında bulunur:

using string = std::basic_string<char>;

Böylece dize , char türünde öğelere sahip dizeler için basic_string işlevselliği sağlar . wchar_t öğesinden oluşan benzer bir std::wstring sınıfı vardır ve çoğunlukla Windows'ta UTF-16 metnini ve Unix benzeri platformların çoğunda UTF-32'yi depolamak için kullanılır . Ancak C++ standardı, bu türler üzerinde Unicode kod noktaları veya kod birimleri olarak herhangi bir yorum getirmez ve bir wchar_t öğesinin bir char öğesinden daha fazla bit içerdiğini bile garanti etmez . wchar_t özelliklerinden kaynaklanan bazı uyumsuzlukları çözmek için , C++11 iki yeni sınıf ekledi: verilen bit sayısı olan std::u16string ve std::u32string (yeni char16_t ve char32_t türlerinden oluşur ). tüm platformlarda kod birimi başına. C++11 ayrıca 16-bit ve 32-bit "karakterler"den oluşan yeni dize değişmezleri ve Unicode kod noktalarını boş sonlandırılmış (C-stili) dizelere koymak için sözdizimi ekledi .

Bir basic_string'in , kendisine eşlik edecek bir char_traits yapısına sahip herhangi bir tür için özelleştirilebilir olması garanti edilir . C++11'den itibaren, standart kitaplıkta yalnızca char , wchar_t , char16_t ve char32_t uzmanlıklarının uygulanması gerekir; diğer türler uygulama tanımlıdır. Her uzmanlık aynı zamanda bir Standart Kitaplık kabıdır ve bu nedenle Standart Kitaplık algoritmaları , dizelerdeki kod birimlerine uygulanabilir.

Eleştiriler

Tasarımı std :: string ile monolitik bir tasarım örneği olarak askıya alınmıştı Herb Sutter fazla görüyor C ++ 98, 71 sınıf 103 üye fonksiyonlarının olduğunu olabilirdi, üretimden bağımsız uygulama verimlilik kaybı olmadan.

Referanslar