Tại sao có quá nhiều lớp chuỗi khi đối mặt với std :: string?


56

Dường như với tôi rằng nhiều thư viện C ++ lớn hơn cuối cùng tạo ra kiểu chuỗi của riêng họ. Trong mã khách hàng bạn có phải sử dụng một trong các từ thư viện ( QString, CString, fbstringvv, tôi chắc chắn rằng bất cứ ai có thể một vài tên) hoặc giữ chuyển đổi giữa các loại tiêu chuẩn và một trong những những ứng dụng thư viện (mà hầu hết thời gian liên quan đến ít nhất một bản sao).

Vì vậy, có một sai lầm cụ thể hoặc một cái gì đó sai về std::string(giống như auto_ptrngữ nghĩa là xấu)? Nó đã thay đổi trong C ++ 11?


32
Nó được gọi là "Hội chứng không được phát minh ở đây".
Cat Plus Plus

10
@CatPlusPlus QString và CString cả std :: chuỗi có trước.
Gort Robot

8
@Cat Plus Plus: Hội chứng này dường như không ảnh hưởng đến lớp Chuỗi Java.
Giorgio

20
@Giorgio: Nhân tiện, các lập trình viên Java quá bận rộn để phát minh ra cách giải quyết cho sự thiếu hụt ngôn ngữ để lo lắng về các lớp chuỗi (Nhân tiện, Android đã phát minh lại Chuỗi).
Cat Plus Plus

9
@Giorgio: Điều đó có lẽ là do hỗ trợ cú pháp được mã hóa cứng của Java cho java.lang.String(thiếu quá tải toán tử, v.v.) sẽ khiến cho việc sử dụng bất cứ thứ gì khác trở nên khó khăn.
Ốc cơ khí

Câu trả lời:


57

Hầu hết các thư viện C ++ lớn hơn đã được bắt đầu trước khi std::stringđược chuẩn hóa. Các tính năng khác bao gồm các tính năng bổ sung được chuẩn hóa muộn hoặc vẫn chưa được tiêu chuẩn hóa, chẳng hạn như hỗ trợ UTF-8 và chuyển đổi giữa các bảng mã.

Nếu những thư viện đó được triển khai ngày hôm nay, có lẽ họ sẽ chọn viết các hàm và các trình vòng lặp hoạt động trên các std::stringthể hiện.


5
Hỗ trợ cho UTF-8 được chuẩn hóa kể từ C ++ 98. Theo cách thực hiện bất tiện và một phần được xác định theo cách mà dường như không ai có thể sử dụng nó
AProgrammer

9
@AProgrammer: charđược đảm bảo đủ lớn để chứa bất kỳ loại tiền mã hóa UTF-8 nào. AFAIK, đó là "hỗ trợ" duy nhất mà C ++ 98 cung cấp.
Ben Voigt

4
@AProgrammer: Sự hỗ trợ đó thực sự khá vô dụng.
DeadMG

4
@AProgrammer locale Đó được cho là bị phá vỡ kể từ khi wchar_tkhông đủ lớn để đại diện cho tất cả các điểm mã Unicode. Hơn nữa, đã có cuộc thảo luận toàn bộ này về UTF-16 được coi là có hại mà lập luận rất thuyết phục đã được thực hiện mà UTF-8 nên được sử dụng độc quyền ...
Konrad Rudolph

6
@KonradRudolph, nó không phải là hệ thống miền địa phương bị phá vỡ ở đó (định nghĩa của wchar_t là "đủ rộng cho bất kỳ bộ ký tự được hỗ trợ nào"); các hệ thống đã cam kết với 16 bit mà wchar_t đã làm đồng thời cam kết không hỗ trợ Unicode. Chà, thủ phạm là Unicode, lần đầu tiên đảm bảo rằng nó sẽ không bao giờ sử dụng các điểm mã cần nhiều hơn 16 bit, sau đó các hệ thống cam kết với 16 bit wchar_t, sau đó chuyển đổi unicode cần nhiều hơn 16 bit.
AProgrammer

39

Chuỗi là sự bối rối lớn của C ++.

Trong 15 năm đầu tiên, bạn hoàn toàn không cung cấp một lớp chuỗi - buộc mọi trình biên dịch trên mọi nền tảng và mọi người dùng phải tạo riêng cho họ.

Sau đó, bạn làm cho một cái gì đó bối rối về việc nó được coi là API thao tác chuỗi đầy đủ hay chỉ là một thùng chứa char STL, với một số thuật toán sao chép các thuật toán trên std :: Vector hoặc khác nhau.

Khi một hoạt động chuỗi rõ ràng như thay thế () hoặc mid () liên quan đến một trình lặp lặp như vậy mà bạn cần phải giới thiệu một từ khóa 'tự động' mới để giữ cho câu lệnh phù hợp trên một trang và khiến hầu hết mọi người từ bỏ toàn bộ ngôn ngữ .

Và sau đó, bạn có unicode 'hỗ trợ' và std :: wopes chỉ là arghh .....

<rant off> cảm ơn bạn - Bây giờ tôi cảm thấy tốt hơn nhiều.


12
@DeadMG - có và nó đã được chuẩn hóa vào năm 1998, 15 năm sau khi nó được phát minh và 6 năm sau ngay cả MSFT cũng đang sử dụng nó. Có các trình vòng lặp là một cách hữu ích để làm cho một mảng và danh sách trông giống nhau, bạn có nghĩ rằng chúng là một cách rõ ràng để thực hiện thao tác chuỗi không?
Martin Beckett

3
C with Classes được phát minh vào năm 1983. Không phải C ++. Các thư viện Tiêu chuẩn duy nhất là những thư viện được xác định bởi Standard - điều này, thật kỳ lạ, chỉ có thể xảy ra khi bạn có một Tiêu chuẩn, vì vậy ngày sớm nhất có thể cho bất kỳ thư viện Tiêu chuẩn nào là năm 1998. Và các trình lặp có thể được coi là chính xác bằng các chỉ mục, nhưng được gõ mạnh. Tôi là tất cả cho thực tế rằng các trình vòng lặp hút so với các phạm vi, nhưng điều đó không thực sự cụ thể std::string. Việc thiếu một lớp String vào năm 1983 không chứng minh rằng có nhiều người trong số họ bây giờ.
DeadMG

8
Tôi nghĩ rằng iostreams là sự bối rối lớn của C ++ ...
Doug T.

18
@DeadMG Mọi người đã sử dụng một thứ gọi là "C ++" trong nhiều năm trước năm 1998. Tôi đã viết chương trình đầu tiên của mình bằng cách sử dụng một thứ gọi là "C ++" vào năm 1985. Nếu bạn muốn nói rằng đây không phải là "C ++" thực sự, thì tốt thôi, nhưng trước đó, chúng tôi đã viết mã và phải lấy một lớp chuỗi từ đâu đó. Khi chúng tôi có các cơ sở mã di sản này, chúng tôi không thể chính xác ném chúng ra hoặc viết lại từ đầu khi chúng tôi có một tiêu chuẩn. Bây giờ điều đáng lẽ đã xảy ra là đáng lẽ phải có một lớp chuỗi đi kèm với cfront.
Gort Robot

8
@DeadMG - Nếu không ai sử dụng ngôn ngữ cho đến khi có chứng chỉ ISO thì sẽ không có ngôn ngữ nào được sử dụng vì nó sẽ không bao giờ đạt được ISO. Không có tiêu chuẩn ISO cho trình biên dịch x86 nhưng tôi rất vui khi sử dụng nền tảng này
Martin Beckett

32

Trên thực tế ... có một số vấn đề với std::string, và vâng, nó sẽ tốt hơn một chút trong C ++ 11, nhưng chúng ta đừng vượt lên chính mình.

QStringCStringlà một phần của các thư viện , do đó chúng tồn tại trước khi C ++ được chuẩn hóa (giống như SGI STL). Do đó, họ phải tạo ra một lớp.

fbstringgiải quyết các mối quan tâm hiệu suất rất cụ thể. Tiêu chuẩn quy định độ phức tạp của giao diện và thuật toán đảm bảo tối thiểu, tuy nhiên, đó là Chất lượng thực hiện chi tiết cho dù kết thúc này có nhanh hay không. fbstringcó tối ưu hóa cụ thể ( findví dụ liên quan đến lưu trữ hoặc nhanh hơn ).

Những mối quan tâm khác không được gợi lên ở đây (en vrac):

  • trong C ++ 03, không bắt buộc việc lưu trữ phải liền kề nhau, khiến khả năng tương tác với C có thể trở nên khó khăn. C ++ 11 sửa lỗi này.
  • std::string đang mã hóa mà không biết và không có mã đặc biệt nào cho UTF-8, thật dễ dàng để lưu trữ chuỗi UTF-8 trong đó và vô tình làm hỏng nó
  • std::stringgiao diện cồng kềnh , nhiều phương thức có thể đã được triển khai dưới dạng các hàm tự do và nhiều phương thức được sao chép để phù hợp với cả giao diện dựa trên chỉ mục và giao diện dựa trên iterator.

5
Quan tâm lại # 1 - C ++ 03 21.3.6 / 1 đảm bảo c_str()trả về một con trỏ tới bộ lưu trữ liền kề, cung cấp cho một số khả năng tương tác C. Tuy nhiên, bạn không thể sửa đổi dữ liệu trỏ vào. Cách giải quyết điển hình bao gồm sử dụng a vector<char>.
John Dibling

@ JohnDibling: Có, và có một hạn chế khác: nó có thể phải chịu một bản sao trong bộ lưu trữ mới được phân bổ (Tiêu chuẩn không nói là không). Tất nhiên C ++ 11 cũng không ngăn chặn việc sao chép, nhưng vì đơn giản là bạn có thể làm điều &s[0]đó không còn quan trọng nữa :)
Matthieu M.

1
@MatthieuM.: Con trỏ thu được qua &s[0]có thể không trỏ đến chuỗi kết thúc NUL (trừ khi c_str()được gọi kể từ lần sửa đổi cuối cùng).
Ben Voigt

2
@Matthieu: Một bộ đệm khác không được phép. " c_str()Trả về: Một con trỏ psao p + i == &operator[](i)cho mỗi itrong [0,size()]".
Ben Voigt

3
Điều đáng chú ý là không ai trong tâm trí của họ sử dụng MFC nữa, vì vậy thật khó để tranh luận rằng CString là một lớp chuỗi trong C ++ hiện đại.
DeadMG

7

Ngoài những lý do được đăng ở đây còn có một lý do khác - khả năng tương thích nhị phân . Các nhà văn của thư viện không có quyền kiểm soát std::stringviệc bạn đang sử dụng triển khai nào và liệu nó có bố cục bộ nhớ giống như của họ không.

std::stringlà một mẫu, vì vậy việc triển khai nó được lấy từ các tiêu đề STL cục bộ của bạn. Bây giờ hãy tưởng tượng rằng bạn đang sử dụng một số phiên bản STL được tối ưu hóa hiệu suất, hoàn toàn tương thích với tiêu chuẩn. Ví dụ, bạn có thể đã chọn xâm nhập bộ đệm tĩnh trong mỗi bộ đệm std::stringđể giảm số lần phân bổ động và bỏ lỡ bộ đệm. Do đó, bố cục bộ nhớ và / hoặc kích thước triển khai của bạn khác với thư viện.

Nếu chỉ có bố cục khác nhau, một số std::stringchức năng thành viên gọi các trường hợp được truyền từ thư viện đến máy khách hoặc cách khác có thể không thành công, phụ thuộc vào thành viên nào được thay đổi.

Nếu kích thước cũng khác nhau, tất cả các loại thư viện có std::stringthành viên sẽ xuất hiện có kích thước khác nhau khi được kiểm tra trong thư viện và trong mã máy khách. Các thành viên dữ liệu theo sau std::stringthành viên cũng sẽ được thay đổi và bất kỳ trình truy cập trực tiếp / truy cập nội tuyến nào được gọi từ máy khách sẽ trả về rác, mặc dù "trông ổn" khi gỡ lỗi chính thư viện.

Tóm lại - nếu thư viện và mã máy khách được biên dịch lại các std::stringphiên bản khác nhau , chúng sẽ liên kết tốt, nhưng nó có thể dẫn đến một số lỗi khó hiểu, khó hiểu. Nếu bạn thay đổi std::stringtriển khai, tất cả các thư viện cho thấy các thành viên từ STL phải được biên dịch lại để phù hợp với std::stringbố cục của khách hàng . Và bởi vì các lập trình viên muốn thư viện của họ mạnh mẽ, bạn sẽ hiếm khi thấy được std::stringtiếp xúc ở bất cứ đâu.

Để công bằng, điều này áp dụng cho tất cả các loại STL. IIRC họ không có bố cục bộ nhớ nổi bật.


2
Bạn phải là một lập trình viên * nix. Khả năng tương thích nhị phân C ++ không bằng nhau trên tất cả các nền tảng và đặc biệt trên các lớp Windows KHÔNG chứa các thành viên dữ liệu có thể di chuyển giữa các trình biên dịch.
Ben Voigt

(Ý tôi là ngoại trừ các loại POD, và thậm chí sau đó yêu cầu đóng gói rõ ràng là cần thiết)
Ben Voigt

1
Cảm ơn cho đầu vào, mặc dù tôi không nói về trình biên dịch khác nhau, tôi đang nói về STL khác nhau.
gwiazdorrr

1
+1: ABI là một lý do rất lớn để tạo ra phiên bản lớp biên dịch được cung cấp của riêng bạn. Đối với điều đó một mình, tôi muốn đây là câu trả lời được chấp nhận.
Thomas Eding

6

Có nhiều câu trả lời cho câu hỏi nhưng đây là một số:

  1. Di sản. Nhiều thư viện và lớp chuỗi đã được viết PRIOR cho sự tồn tại của std :: string.

  2. Để tương thích với mã trong C. Thư viện std :: string là C ++ trong đó có các thư viện chuỗi khác hoạt động với C và C ++.

  3. Để tránh phân bổ động. Thư viện std :: string sử dụng phân bổ động và có thể không phù hợp với các hệ thống nhúng, mã gián đoạn hoặc mã liên quan đến thời gian thực hoặc cho chức năng cấp thấp.

  4. Mẫu. Thư viện std :: string dựa trên các mẫu. Cho đến gần đây, một số trình biên dịch C ++ có hiệu năng kém hoặc thậm chí hỗ trợ mẫu lỗi. Thật không may, tôi làm việc trong một ngành sử dụng nhiều công cụ tùy chỉnh và một trong những công cụ của chúng tôi từ một người chơi chính trong ngành không "chính thức" hỗ trợ 100% C ++ (với công cụ lỗi là các mẫu et al).

Có lẽ có nhiều lý do hợp lệ hơn là tốt.


2
"Khá gần đây" có nghĩa là "Đã một thập kỷ kể từ khi Visual Studio có hỗ trợ khá hợp lý cho họ"?
DeadMG

@DeadMG - Visual Studio không phải là trình biên dịch không tuân thủ duy nhất trên thế giới. Tôi làm việc trong các trò chơi video và chúng tôi thường làm việc trên các trình biên dịch tùy chỉnh cho các nền tảng phần cứng chưa được phát hành (xảy ra vài năm một lần trong chu kỳ bàn điều khiển hoặc khi phần cứng mới xuất hiện). "Khá gần đây" có nghĩa là ngày hôm nay - Hiện tại một số trình biên dịch nhất định không hỗ trợ tốt các mẫu. Tôi không thể cụ thể mà không vi phạm NDA nhưng tôi hiện đang làm việc trên nền tảng với các công cụ tùy chỉnh trong đó hỗ trợ C ++ - đặc biệt là tuân thủ mẫu - được coi là "thử nghiệm".
Adisak

4

Chủ yếu là về Unicode. Hỗ trợ tiêu chuẩn cho Unicode là tốt nhất và mọi người đều có nhu cầu Unicode riêng. Ví dụ, ICU hỗ trợ mọi chức năng Unicode mà bạn có thể muốn, đằng sau giao diện Java được tạo tự động kinh tởm nhất mà bạn có thể tưởng tượng và nếu bạn trên Unix bị mắc kẹt với UTF-16 có thể không phải là ý tưởng của bạn về khoảng thời gian tốt đẹp.

Ngoài ra, nhiều người cần các mức hỗ trợ Unicode khác nhau - không phải ai cũng cần các API bố cục văn bản phức tạp và những thứ như vậy. Vì vậy, thật dễ dàng để biết lý do tại sao có nhiều lớp chuỗi tồn tại - Tiêu chuẩn khá hấp dẫn và mọi người đều có nhu cầu khác với những người mới, không ai quản lý để tạo một lớp duy nhất có thể thực hiện nhiều nền tảng hỗ trợ Unicode với giao diện dễ chịu.

Theo tôi, đây hầu hết là lỗi của Ủy ban C ++ vì đã không cung cấp hỗ trợ chính xác cho Unicode- vào năm 1998 hoặc 2003, có thể đó là điều dễ hiểu, nhưng không phải trong C ++ 11. Hy vọng trong C ++ 17 họ sẽ làm tốt hơn.


Xin chào, C ++ 20 ở đây, đoán xem điều gì đã xảy ra với hỗ trợ Unicode?
qua đường

-4

Đó là bởi vì mọi lập trình viên đều có thứ gì đó để chứng minh và cảm thấy cần phải tạo lớp chuỗi nhanh hơn, tuyệt vời của riêng họ cho chức năng tuyệt vời của họ. Nó thường hơi thừa và dẫn đến tất cả các loại chuyển đổi chuỗi bổ sung theo kinh nghiệm của tôi.


7
Điều này có đúng không, tôi hy vọng sẽ thấy một số lượng triển khai Chuỗi tương tự trong các ngôn ngữ như Java nơi có triển khai tốt.
Bill K

@BillK Chuỗi Java là cuối cùng, vì vậy bạn phải đặt chức năng mới ở nơi khác.

Và quan điểm của tôi là, thậm chí là cuối cùng, trong 20 năm tôi chưa từng thấy ai viết một chuỗi giả định chuỗi tùy chỉnh (Chà, tôi đã cố gắng cải thiện hiệu suất nối chuỗi nhưng hóa ra java thông minh hơn ở chuỗi + chuỗi hơn bạn ' d tưởng tượng)
Bill K

2
@Bill: Điều đó có thể phải làm với một nền văn hóa khác. C ++ thu hút những người muốn hiểu các chi tiết cấp thấp. Java thu hút những người chỉ muốn hoàn thành công việc bằng cách sử dụng các khối xây dựng của người khác. (Lưu ý rằng đây không phải là tuyên bố về bất kỳ cá nhân cụ thể nào chọn sử dụng ngôn ngữ này, nhưng về mục tiêu và văn hóa thiết kế tương ứng của ngôn ngữ)
Ben Voigt
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.