Std :: string's c_str () 'den ne gibi bir performans bekleyebiliriz? Her zaman sabit zaman?


13

Son zamanlarda bazı gerekli optimizasyonları yapıyorum. Yaptığım bir şey, bazı ostringstream'leri değiştirmek -> sprintfs. Ben ac stil dizisi, ala std :: dizeleri bir demet sprintf'ing

char foo[500];
sprintf(foo, "%s+%s", str1.c_str(), str2.c_str());

Microsoft'un std :: string :: c_str () uygulamasının sabit zamanda çalıştığı ortaya çıkıyor (sadece dahili bir işaretçi döndürüyor). Görünüşe göre libstdc ++ aynı şeyi yapıyor . Std'nin c_str için hiçbir garanti vermediğini, ancak bunu yapmanın başka bir yolunu hayal etmek zor. Örneğin, bir arabellek için bellek ayırmak zorunda kalacakları belleğe kopyalanırlarsa (onu yok etmek için arayana bırakırlar - STL sözleşmesinin bir parçası DEĞİLDİR) VEYA dahili bir statike kopyalamak zorunda kalacaklar tampon (muhtemelen threadsafe değildir ve kullanım ömrü boyunca garantiniz yoktur). Bu nedenle, yalnızca dahili olarak tutulan boş sonlandırılmış dizeye bir işaretçi döndürmek tek gerçekçi çözüm gibi görünüyor.

Yanıtlar:


9

Hatırlıyorsam, standart string::c_str()tatmin edici bir şey döndürmek için izin verir :

  • Dizenin içeriği ve sonlandırma için yeterince büyük depolama alanı NULL
  • Belirtilen stringnesnenin sabit olmayan bir üyesi çağrılıncaya kadar geçerli olmalıdır

Yani pratikte bu, dahili depolamaya bir işaretçi anlamına gelir; çünkü döndürülen işaretçinin ömrünü harici olarak izlemenin bir yolu yoktur. Bence optimizasyonunuzun bu sabit (küçük) zaman olduğunu varsayalım.

İlgili bir notta, dize biçimlendirmesi performans sınırlayıcıysa; Boost.Phoenix gibi bir şeyle kesinlikle gerekli olana kadar değerlendirmeyi ertelemede daha iyi şanslar bulabilirsiniz .

Boost.Format Sonuç gerekli olana kadar biçimlendirmeyi dahili olarak bozduğuna ve yüksek frekanslı günlük kaydı için önemli bir fark yarattığım biçim dizesini yeniden ayrıştırmadan aynı biçim nesnesini tekrar tekrar kullanabileceğinize inanıyorum.


2
Bir uygulamanın, boş sonlandırıcıya eklenecek kadar büyük olan yeni veya ikincil bir dahili tampon oluşturması mümkün olabilir. c_strBir const yöntemi olmasına rağmen (veya en azından bir const aşırı yükü var - hangisini unutuyorum), bu mantıksal değeri değiştirmez, bu nedenle bir neden olabilir mutable. Bu ederim dan işaretçileri kırmak diğer çağrıları c_str, bu tür işaretçileri aynı mantıksal dizeye atıfta gerektiğini hariç (böylece yeniden tahsis için hiçbir yeni nedeni var - zaten boş sonlandırıcı olmalıdır) ya da başka zaten olmayan bir çağrı orada olmalı - arasındaki yöntem.
Steve314

Bu gerçekten geçerliyse, c_straramalar yeniden tahsis ve kopyalama için O (n) zamanı olabilir. Ama aynı zamanda standartta bunu bilmediğim ekstra kurallar olması da mümkündür. Bunu önermek nedeni - aramalar için c_stronların hızlı konum sağlamak önemlidir dikkate alınmayabilir yüzden gerçekten AFAIK ortak olmak anlamına gelmez - bir normalde gereksiz null Terminatör için depolama bu ekstra bayt kaçınarak stringdurumlarda bu asla kullanımı c_strmay öncelik kazanmıştır.
Steve314

Boost.Formatdahili sprintfolarak oldukça büyük bir yük ile sona eren akışlardan geçer . Belgeler, düzden yaklaşık 8 kat daha yavaş olduğunu söylüyor sprintf. Performans ve tip güvenliği istiyorsanız, deneyin Boost.Spirit.Karma.
Jan Hudec

Boost.Spirit.Karmaperformans için iyi bir ipucu, ancak mevcut printfstil kodunu (ve kodlayıcıları) uyarlamakta zorlanabilecek çok farklı bir metodolojiye sahip olduğuna dikkat edin . G Boost.Format/ Ç'miz eşzamansız olduğu için büyük ölçüde takıldım ; ancak büyük bir faktör, meslektaşlarımı sürekli olarak kullanmaya ikna edebilmemdir (yine de ostream<<aşırı yüklenme olan her tür tipe izin verir - bu da .c_str()tartışmayı güzel bir şekilde yönlendirir ) Karma performans numaraları.
rvalue

23

C ++ 11 standardında (N 3290 sürümünü okuyorum), bölüm 21.4.7.1 c_str () yöntemi hakkında konuşuyor:

const charT* c_str() const noexcept; const charT* data() const noexcept;

Döndürür: [0, size ()] içindeki her i için p + i == & operatörü olacak şekilde bir işaretçi p.
Karmaşıklık: sabit zaman.
Gerekenler: Program, karakter dizisinde saklanan değerlerin hiçbirini değiştirmeyecektir.

Yani, evet: sabit zaman karmaşıklığı standart tarafından garanti edilir.

Sadece c ++ 03 standardını kontrol ettim ve böyle bir gereksinimi yok, ne de karmaşıklığı anlatıyor.


8

Teoride C ++ 03 bunu gerektirmez ve bu nedenle dize, boş sonlandırıcının varlığının c_str () çağrıldığı anda eklendiği bir karakter dizisi olabilir. Bu, bir yeniden tahsis gerektirebilir (dahili özel işaretçi olarak bildirilirse sabitliği ihlal etmez mutable).

C ++ 11 daha katıdır: zaman sabitliği gerektirir, bu nedenle yeniden konumlandırma yapılamaz ve dizi her zaman null değerini depolayacak kadar geniş olmalıdır. c_str (), kendi başına, ptr[size()]='\0'null değerinin gerçekten mevcut olmasını sağlamak için " " yapabilir . Aralık [0..size())değişmediği için dizinin sabitliğini ihlal etmez .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.