2010-04-30 24 views
8

Như bạn đã biết có các thư viện sử dụng các luồng như iostreamfstream.Tại sao các luồng trong C++?

Câu hỏi của tôi là:

  • Tại sao suối? Tại sao họ không gắn bó với các chức năng tương tự như print, fgets và như vậy (ví dụ)?

Họ đòi hỏi các nhà khai thác của mình <<>> nhưng tất cả họ làm có thể được thực hiện trong các chức năng đơn giản như trên, cũng là chức năng

printf("Hello World!"); 

là rất nhiều dễ đọc hơn và hợp lý với tôi hơn

cout << "Hello World"; 

Tôi cũng nghĩ rằng tất cả các chuỗi trừu tượng trong C++ đều biên dịch xuống các hàm gọi hàm chuẩn (kém hiệu quả) trong nhị phân.

+2

Hiệu quả không thực sự là một mối quan tâm, vì 99% thời gian sẽ được chi tiêu trong cùng một cuộc gọi hệ điều hành để in ra bàn điều khiển –

+2

Tóm tắt chuỗi nào? –

+2

-1 hey boy, tôi cho rằng bạn là người mới bắt đầu trong thế giới C++. Làm sao bạn có thể nói rằng nó kém hiệu quả hơn, nếu bạn không biết? Trước khi được phê bình, xin vui lòng, tìm hiểu, và được khiêm tốn. –

Trả lời

24

Các luồng có độ an toàn cao hơn.

Ví dụ printf("%s", a); có thể bị lỗi nghiêm trọng nếu a là số nguyên. cout << a; không có vấn đề này.

Một vấn đề khác là luồng phù hợp hơn với phương pháp thiết kế hướng đối tượng.

Ví dụ bạn có một ứng dụng đơn giản viết một số đầu ra và sau đó bạn muốn đầu ra để chuyển đến một tệp thay vì giao diện điều khiển. Với các cuộc gọi C, bạn sẽ phải thay thế tất cả các cuộc gọi thành printf để gọi tới số fprintf và cẩn thận duy trì FILE* trên đường đi. Với suối bạn chỉ cần thay đổi lớp bê tông của dòng bạn đang sử dụng và đó là nó, hầu hết các mã vẫn giữ nguyên như vậy:

void doSomething(ostream& output) 
{ 
    output << "this and that" << a; 
} 

doSomething(cout); 
doSomething(ofstream("c:\file.txt")); 
+1

Ha hah" khủng khiếp sai ", thật vậy, đúng vậy. –

+0

Tất nhiên, các trình biên dịch hiện đại đã được mở rộng (không theo cách tuân thủ tiêu chuẩn) để hỗ trợ kiểm tra kiểu cho * chuỗi định dạng printf. –

+0

"Với C cuộc gọi, bạn sẽ phải thay thế tất cả các cuộc gọi đến printf thành cuộc gọi đến fprintf" - tình huống không quá tệ, ví dụ: bạn có thể làm chuyển hướng của freopen dòng ("output.txt", "w", stdout); và tránh thay thế – Tebe

7

Dòng C++ được nhập an toàn. Trong C, nếu bạn nói:

double d = 1.23; 
printf("%d", d); 

bạn không được đảm bảo nhận được thông báo lỗi, mặc dù chuyển đổi không chính xác.

Dường như bạn đang đặt câu hỏi C++ cơ bản. Bạn đang sử dụng sách văn bản C++ nào không bao gồm chúng?

2

Streams có thể được xích lại với nhau

cout << "hello" << " " << "world" 
+1

Vì vậy, có thể là phương pháp: out.print ("hello"). Print ("") .print ("thế giới") –

+2

'std :: cout.operator << (" hello "). Operator << (" ") .operator << ("thế giới"); ' –

5

printf không phải là loại an toàn cho một. Giao diện cout cũng phổ biến hơn, cho phép rất nhiều thứ không có sẵn với phiên bản printf. Một ví dụ tuyệt vời là nhà điều hành luồng của bạn cho các loại của bạn là, nếu bạn làm đúng, hãy tham khảo std :: ostream dưới dạng luồng, chứ không phải cout. Vì vậy, bạn có thể thay đổi đích của đầu ra chỉ đơn giản bằng cách sử dụng một luồng khác. Để làm điều đó với printf, bạn phải thực hiện toàn bộ quá trình ghi đè phụ thuộc vào nền tảng của các chốt xử lý đầu ra.

Tôi cũng nghĩ rằng tất cả các chuỗi trừu tượng trong C++ đều biên dịch xuống (ít hiệu quả) các hàm gọi hàm chuẩn trong nhị phân.

Hãy suy nghĩ tất cả những gì bạn muốn. Cho đến khi bạn thực sự kiểm tra giả định của bạn, ý kiến ​​của bạn sẽ không giữ được nhiều trọng lượng. Hơn nữa, bạn cần phải xem xét việc mất trừu tượng, cái gì đó thường là toàn bộ hơn rất nhiều quan trọng trong phát triển phần mềm hiện đại.

1

Cú pháp giống như luồng với operator<<operator>> cho phép nối tốt đẹp.

printf("%d%d%d%d%d", a, b, c, d, e); 

vs

cout << a << b << c << d << e; 

Hơn nữa trong cách tiếp cận đầu tiên người ta luôn luôn phải suy nghĩ về các loại, trong khi ở cách tiếp cận dòng, loại không được xác định.

1

Trên thực tế

cout << "Test: " << test << endl; 

vẻ như rất nhiều trực quan hơn để tôi hơn

printf("Test: %d\n", test); 

Nếu bạn chưa bao giờ thấy bản in trước đó, bạn sẽ không biết cách làm gì.

Trong cả hai trường hợp,

print "Test: " + test 

(trong một số ngôn ngữ, bao gồm cả Python :() làm rất nhiều ý nghĩa hơn :)

+0

Python có 'in 'Kiểm tra:% d"% test' quá, và nó là rất cần thiết cho định dạng phức tạp hơn. – UncleBens

+0

@UncleBens: Đúng, và rất tiếc là họ đã chọn sử dụng định dạng kiểu C-string, * đặc biệt * yêu cầu nhập cho ngôn ngữ động ... –

+0

Nếu bạn chưa bao giờ thấy luồng C++ trước đây, thậm chí còn tệ hơn: bạn nghĩ bạn đang thực hiện một số thay đổi chút điên! –

9

Thứ nhất, nó cho phép bạn tận dụng lợi thế của mô hình đối tượng C++ để tạo các hàm không quan tâm liệu chúng có ghi vào đầu ra tiêu chuẩn, tệp hoặc ổ cắm mạng hay không (nếu bạn có một ổ cắm mạng có nguồn gốc từ ostream). Ví dụ.

void outputFoo(std::ostream& os) 
{ 
    os << "Foo!"; 
} 

int main() 
{ 
    outputFoo(std::cout); 

    std::ofstream outputFile("foo.txt"); 
    outputFoo(outputFile); 

    MyNetworkStream outputSocket; 
    outputFoo(outputSocket); 
} 

Và tương tự cho luồng đầu vào.

Luồng cũng có lợi thế khi nhập vào và xuất các đối tượng. Điều gì sẽ xảy ra nếu bạn muốn đọc trong một đối tượng với scanf?

MyObject obj; 
scanf("??", &obj); // What specifier to use? 

Ngay cả khi có thông số kỹ thuật thích hợp, cách scanf biết cách điền vào các thành viên của đối tượng? Với luồng C++, bạn có thể quá tải operator<< và viết

MyObject obj; 
std::cin >> obj; 

Và nó sẽ hoạt động. Tương tự như vậy đối với std::cout << obj, vì vậy bạn có thể viết mã tuần tự hóa đối tượng ở một nơi và không phải lo lắng về nó ở bất kỳ nơi nào khác.

+0

Một ổ cắm mạng có nguồn gốc từ 'ostream' thực sự là điều tôi muốn thấy :) – shoosh

+5

@shoosh 'boost :: asio :: ip :: tcp :: iostream'? – Cubbi

1

C++ cho phép bạn sử dụng printf, nhân tiện. Vì vậy, nếu bạn thích điều đó, hãy tiếp tục và sử dụng nó.

Luồng được sử dụng nhiều hơn viết đầu ra cho bảng điều khiển. Chúng là một giải pháp đệm dữ liệu có mục đích chung có thể được áp dụng cho mọi thứ từ đầu ra màn hình đến xử lý tệp đến giao diện mạng và giao diện thiết bị đầu vào. Nội dung của bạn có thể ghi vào (hoặc đọc từ) mà không cần quan tâm đến dữ liệu thực sự sẽ đi đến đâu.

Giả sử bạn có một đối tượng phức tạp mà bạn muốn có thể ghi vào bàn điều khiển đầu ra, vào tệp nhật ký và vào cửa sổ bật lên gỡ lỗi. Bạn sẽ viết ba hàm thực hiện gần như chính xác điều đó, hay bạn sẽ viết một hàm mà bạn truyền một luồng đầu ra (y)?

2

Lợi ích khác của luồng là chúng được tạo ra để có thể mở rộng. Với suối, bạn có thể có người dùng định nghĩa các lớp học của bạn làm việc giống như được xây dựng trong các loại:

class foo { ... }; 

ostream &operator<<(ostream &ostr, const foo &f) 
{ 
    ostr << ... how you want to print a foo ...; 
    return ostr; 
} 

Và bây giờ bạn có thể in một foo giống như bất cứ điều gì khác:

int n = ... 
foo f = ... 

cout << n << ": " << f << endl; 
0

tôi vẫn sử dụng printf, chủ yếu là do thật dễ dàng để kiểm soát định dạng đầu ra. Với tôi, gõ an toàn không phải là một lợi ích chính ở đây bởi vì nó có thể dễ dàng bị bắt bởi kiểm tra, với thực tế là một chương trình dựa trên bàn điều khiển dễ dàng hơn nhiều để kiểm tra với các ứng dụng dựa trên giao diện người dùng hoặc dựa trên web. Nếu bạn không thử nghiệm, các lỗi nghiêm trọng hơn có thể vượt qua kiểm tra thời gian biên dịch theo bất kỳ cách nào.

Tôi cũng không đồng ý với một lý do khác khiến luồng xác nhận quyền sở hữu linh hoạt hơn do khả năng thay thế. Nó tương đương với đề nghị sử dụng fprintf (fout, ...) cho khả năng thay thế. Nếu bạn cần chuyển hướng đầu ra, hãy sử dụng đường ống. Nếu bạn đang ở trong một thư viện, tại sao bạn không chỉ trả lại một chuỗi và để cho người gọi quyết định nơi nó đi?

+0

Sử dụng đường ống - không phải mọi thứ đều là một lệnh như ứng dụng. Trả về một chuỗi - không hiệu quả lắm. – shoosh

+1

shoosh, chỉ từ tò mò, làm thế nào bạn có thể trả về một chuỗi trong C từ từ chức năng? Tôi có nghĩa là nếu nó được phân bổ ở đó như (static char * mystring), bạn sẽ chỉ trả lại một con trỏ. nếu bạn có nghĩa là chuỗi từ STL thì bạn chỉ trả về đối tượng đại diện cho một chuỗi, chuỗi được giữ ở một nơi khác - không bị mất hiệu lực – Tebe

3

Bên cạnh việc là loại an toàn và đa hình, luồng có thể di động hơn. Trên một số hệ thống printf với một yêu cầu dài "% d" và trên một số hệ thống nó đòi hỏi "% ld".

+0

Tuân thủ luôn yêu cầu '% ld', nhưng' l' không làm gì trên một số nền tảng. – Potatoswatter

+0

Tôi đã thay đổi rất nhiều mã phức tạp, điên rồ để làm việc xung quanh việc này để sử dụng luồng mà không bao giờ nhận ra điều đó! –

+0

+1 một ngày tôi đã mất một thời gian dài cho đến khi tôi sử dụng trình gỡ lỗi để xem – Tebe

0

stringstream an toàn hơn snprintf/sscanf vì nó hoàn toàn tránh khả năng tràn bộ đệm (ngay cả "lỗi thất bại").

0

Luồng hoạt động với mẫu, trong khi printf/scanf thì không.

2

C++ IOStreams vô lý không hiệu quả (trong hầu hết các triển khai mà tôi biết). Thông thường, đó không phải là một mối quan tâm, nhưng khi, thư viện cơ bản là vô ích. Nó cũng là một điểm tốt mà cú pháp là unintuitive (và rất, rất tiết). Thư viện phức tạp và không cần thiết để mở rộng. Nó cũng không linh hoạt lắm. Khi đối chiếu với một thứ gì đó giống như STL, IOStreams thực sự trông giống như một giấc mơ xấu. Nhưng nó ở đây, và chúng tôi đang mắc kẹt với nó.

Lý do ở đây và lý do có vẻ như nó đã được phát triển trước đó, trước khi C++ là ngôn ngữ trưởng thành. Trước khi chúng tôi có nhiều thập kỷ kinh nghiệm cho chúng tôi biết thiết kế thư viện tốt là gì. Trước khi bất cứ ai thực sự biết những gì các tùy chọn được.

C++ cần thư viện I/O là tốt hơn so với C. Và trong một số cách quan trọng, C++ IOStreams là tốt hơn là. Họ là loại an toàn và mở rộng như những người khác đã đề cập. Bằng cách thực hiện một toán tử đơn, tôi có thể in ra một lớp do người dùng định nghĩa. Điều đó không thể thực hiện được với printf. Tôi cũng không phải lo lắng về việc nhận định dạng sai và in ra rác vì thiếu an toàn kiểu.

Những thứ đó cần để được sửa. Và hey, trong những ngày đầu, chức năng ảo và quá tải của nhà điều hành là shit. Trông nó thật tuyệt.Tất nhiên các thư viện muốn sử dụng các tính năng đó.

Thư viện IOStream là một thỏa hiệp giữa:

  • một cái gì đó an toàn hơn và mở rộng hơn C stdio.h
  • cái gì hiệu quả
  • một cái gì đó được thiết kế tốt và trực quan
  • một thư viện mà thực sự tồn tại thời điểm C++ đã được chuẩn hóa. (Họ đã có thêm một cái gì đó , vì vậy họ đã phải lựa chọn giữa các ứng cử viên thực sự tồn tại vào thời điểm đó.)

Thư viện không đạt được tất cả trong số này, và tôi tin rằng ngày hôm nay, với nhiều thập kỷ của chúng tôi kinh nghiệm với ngôn ngữ, chúng tôi có thể thiết kế một thư viện tốt hơn nhiều. Nhưng trở lại vào giữa những năm 90, khi họ đang tìm kiếm một thư viện I/O để thêm vào, đây là điều tốt nhất mà họ có thể tìm thấy.

Các vấn đề liên quan