เราสามารถคาดหวังประสิทธิภาพอะไรได้บ้างจาก std :: string's c_str () เวลาคงที่เสมอหรือไม่


13

ฉันได้ทำการปรับปรุงบางอย่างที่จำเป็นเมื่อเร็ว ๆ นี้ สิ่งหนึ่งที่ฉันทำคือการเปลี่ยน ostringstreams -> sprintfs ฉันกำลังรีบพวงของ std :: strings ไปที่ array style ac, ala

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

ปรากฎว่าการใช้งาน std :: string :: c_str () ของ Microsoft นั้นทำงานในเวลาคงที่ (มันแค่คืนค่าตัวชี้ภายใน) ปรากฏว่าlibstdc ++ ไม่เหมือนกัน ฉันรู้ว่ามาตรฐานไม่รับประกันสำหรับ c_str แต่มันยากที่จะจินตนาการถึงวิธีการทำเช่นนี้อีก ตัวอย่างเช่นหากพวกเขาคัดลอกไปยังหน่วยความจำพวกเขาอาจต้องจัดสรรหน่วยความจำสำหรับบัฟเฟอร์ (ปล่อยให้มันขึ้นอยู่กับผู้เรียกที่จะทำลายมัน - ไม่ใช่ส่วนหนึ่งของสัญญา STL) หรือพวกเขาจะต้องคัดลอกไปยังคงที่ภายใน บัฟเฟอร์ (อาจไม่ใช่ threadsafe และคุณไม่มีการรับประกันตลอดอายุการใช้งาน) ดังนั้นเพียงแค่คืนค่าพอยน์เตอร์ไปยังสตริงที่สิ้นสุดด้วยค่า null ภายในที่มีการบำรุงรักษาดูเหมือนจะเป็นทางออกเดียวที่แท้จริง

คำตอบ:


9

ถ้าฉันจำได้มาตรฐานอนุญาตให้string::c_str()คืนสิ่งที่ตรงตามความต้องการได้มาก:

  • ที่เก็บข้อมูลซึ่งมีขนาดใหญ่พอสำหรับเนื้อหาของสตริงและการยกเลิก NULL
  • ต้องถูกต้องจนกว่าสมาชิกที่ไม่ใช่สมาชิกของstringอ็อบเจ็กต์ที่กำหนดจะถูกเรียกใช้

ดังนั้นในทางปฏิบัตินี่หมายถึงตัวชี้ไปยังที่จัดเก็บข้อมูลภายใน เนื่องจากไม่มีวิธีในการติดตามอายุของตัวชี้ที่ส่งคืนจากภายนอก ฉันคิดว่าการเพิ่มประสิทธิภาพของคุณปลอดภัยที่จะคิดว่านี่เป็นเวลาคงที่ (เล็ก)

ในบันทึกย่อที่เกี่ยวข้องหากการจัดรูปแบบสตริงเป็นการ จำกัด ประสิทธิภาพ คุณอาจพบว่าโชคดีกว่าการชะลอการประเมินผลจนต้องอย่างกับสิ่งที่ต้องการBoost.Phoenix

Boost.Format ฉันเชื่อว่าการฟอร์แมตภายในจะล้มเหลวจนกว่าคุณจะต้องการผลลัพธ์และคุณสามารถใช้วัตถุรูปแบบเดียวกันซ้ำ ๆ ได้โดยไม่ต้องวิเคราะห์สตริงรูปแบบซ้ำอีกครั้งซึ่งฉันพบว่าสร้างความแตกต่างอย่างมีนัยสำคัญสำหรับการบันทึกความถี่สูง


2
อาจเป็นไปได้สำหรับการใช้งานเพื่อสร้างบัฟเฟอร์ภายในใหม่หรือรอง - มีขนาดใหญ่พอที่จะเพิ่มใน null terminator แม้ว่าจะc_strเป็นวิธี const (หรืออย่างน้อยมีเกิน const - ฉันลืม) mutableนี้จะไม่เปลี่ยนแปลงค่าตรรกะดังนั้นอาจจะเป็นเหตุผลสำหรับ มันจะทำลายพอยน์เตอร์จากการโทรอื่นไปที่c_strยกเว้นพอยน์เตอร์ดังกล่าวจะต้องอ้างถึงสตริงตรรกะเดียวกัน (ดังนั้นจึงไม่มีเหตุผลใหม่ที่จะจัดสรรใหม่ - ต้องมีเทอร์มิเนเตอร์ที่เป็นโมฆะ) มิฉะนั้นจะต้องโทรหา - วิธีการในระหว่าง
Steve314

หากถูกต้องจริงการc_strโทรสามารถใช้เวลา O (n) สำหรับการจัดสรรใหม่และการคัดลอก แต่ก็เป็นไปได้ว่ามีกฎพิเศษในมาตรฐานที่ฉันไม่ทราบว่าจะป้องกันสิ่งนี้ เหตุผลที่ผมบอกว่ามัน - โทรไปยังc_strไม่ได้หมายความว่ามันจะเป็นเรื่องธรรมดา AFAIK ดังนั้นมันอาจจะไม่ได้รับการพิจารณาที่สำคัญเพื่อให้แน่ใจว่าพวกเขากำลังได้อย่างรวดเร็ว - หลีกเลี่ยงที่ไบต์พิเศษของการจัดเก็บข้อมูลสำหรับเทอร์มิ null ปกติที่ไม่จำเป็นในstringกรณีที่ไม่เคยใช้c_strอาจ ได้มาก่อน
Steve314

Boost.Formatภายในต้องผ่านลำธารซึ่งภายในsprintfจบลงด้วยค่าใช้จ่ายที่ค่อนข้างใหญ่ เอกสารบอกว่ามันเป็นประมาณ 8 sprintfครั้งช้ากว่าธรรมดา Boost.Spirit.Karmaหากคุณต้องการประสิทธิภาพการทำงานและประเภทความปลอดภัยให้ลอง
Jan Hudec

Boost.Spirit.Karmaเป็นเคล็ดลับที่ดีสำหรับการทำงาน แต่ระวังว่ามันมีวิธีการที่แตกต่างกันอย่างมากมายซึ่งอาจเป็นเรื่องยุ่งยากในการปรับเปลี่ยนprintfสไตล์โค้ดที่มีอยู่(และโคเดอร์) ฉันติดอยู่กับส่วนใหญ่Boost.Formatเพราะ I / O ของเราไม่ตรงกัน แต่ปัจจัยใหญ่คือฉันสามารถโน้มน้าวให้เพื่อนร่วมงานของฉันใช้มันอย่างสม่ำเสมอ (ยังอนุญาตให้มีประเภทใดostream<<เกินพิกัด - ซึ่งเป็นวิธี.c_str()การถกเถียงกันอย่างเป็นทางการ) The karma peformance number
rvalue

23

ในมาตรฐาน c ++ 11 (ฉันกำลังอ่านเวอร์ชัน N 3290) บทที่ 21.4.7.1 พูดถึงวิธีการ c_str ():

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

คืนค่า: ตัวชี้ p ซึ่ง p + i == & ตัวดำเนินการสำหรับแต่ละ i ใน [0, size ()]
ความซับซ้อน: เวลาคงที่
ต้องการ: โปรแกรมจะต้องไม่เปลี่ยนแปลงค่าใด ๆ ที่เก็บไว้ในอาร์เรย์อักขระ

ดังนั้นใช่: ความซับซ้อนของเวลาคงที่รับประกันโดยมาตรฐาน

ฉันเพิ่งตรวจสอบมาตรฐาน c ++ 03 และไม่มีข้อกำหนดดังกล่าวหรือไม่บอกความซับซ้อน


8

ในทางทฤษฎี C ++ 03 ไม่ต้องการสิ่งนั้นและด้วยเหตุนี้สตริงจึงสามารถเป็นอาร์เรย์ของถ่านโดยที่การมีอยู่ของตัวสิ้นสุดเทอร์มินัลถูกเพิ่มในเวลาที่เรียกว่า c_str () การดำเนินการนี้อาจต้องทำการจัดสรรใหม่ (ไม่เป็นการละเมิด const-ness หากมีการประกาศตัวชี้ส่วนตัวภายในเป็นmutable)

C ++ 11 นั้นเข้มงวดกว่า: มันต้องใช้เวลาที่มีความคุ้มค่าดังนั้นจึงไม่สามารถทำการโยกย้ายได้และอาเรย์จะต้องกว้างพอที่จะเก็บโมฆะไว้ที่ท้าย c_str () ด้วยตัวเองยังคงสามารถทำ " ptr[size()]='\0'" เพื่อให้มั่นใจว่าโมฆะมีอยู่จริง มันไม่ได้ละเมิดค่าคงที่ของอาร์เรย์เนื่องจากช่วง[0..size())จะไม่เปลี่ยนแปลง

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.