2010-03-05 34 views
9

Trong khi đọc ngay hôm nay, tôi đã nhận được nhận xét này trong một số post về cách các địa điểm hàng đầu trong thử thách Google Ai được C++ thực hiện. Tài reventlov tuyên bốCâu hỏi cơ bản về RAII, STL pop và PIMPL

Vấn đề lớn nhất tôi có với C++ là nó waaay quá dễ dàng để nghĩ rằng bạn là một "C++ lập trình viên" mà không thực sự hiểu tất cả những điều bạn cần phải hiểu để sử dụng C++ chấp nhận được tốt.

Bạn phải biết RAII và biết sử dụng không gian tên và hiểu xử lý ngoại lệ phù hợp (ví dụ, bạn có thể giải thích lý do tại sao các phương thức pop() trong STL không trả lại giá trị mà chúng loại bỏ) . Bạn phải biết được ba thế hệ chức năng nào trong thư viện chuẩn là thư viện đúng. Bạn nên làm quen với các khái niệm như PIMPL. Bạn cần hiểu cách thiết kế thư viện chuẩn (đặc biệt là STL) hoạt động như thế nào. Bạn cần phải hiểu cách macro tương tác với các không gian tên và tại sao bạn thường không nên sử dụng các macro trong C++ và những gì bạn nên sử dụng thay vào đó (thường là các mẫu hoặc các dòng nội tuyến, hiếm khi là một lớp). Bạn cần biết về tăng cường.

Tôi nghĩ rằng tôi là một trong những người lập trình C++ không biết gì mà anh ta đề cập đến. Để giữ ngắn gọn, câu hỏi của tôi là

  1. Bạn có thể đưa ra ví dụ về lỗi giám sát RAII điển hình, ví dụ: nơi thực hành tốt nhất quyết định việc sử dụng RAII nhưng các lập trình viên đã thực hiện bằng cách nào khác?
  2. Tại sao không phải phương thức pop() trong STL trả về giá trị mà chúng loại bỏ?
  3. Tôi đã đọc mục nhập của Wikipedia cho PIMPL, không hiểu bất kỳ mục nào trong số đó. Bạn có thể đưa ra một ví dụ về cách sử dụng điển hình của thành ngữ PIMPL.
+0

Tôi không hiểu câu hỏi 1, bạn có thể diễn đạt lại câu hỏi này không? –

+21

Có lẽ bạn sẽ hạnh phúc hơn khi ở trên reddit? Quy tắc ở đây là một câu hỏi tại một thời điểm. –

+2

@Neil, lol điều này sẽ trở thành 10+ nhận xét tiếp theo của bạn xD –

Trả lời

9
  1. Một ví dụ điển hình cho thấy RAII rất quan trọng nhưng đôi khi bị lãng quên khi khóa một mutex. Nếu bạn có một phần của mã khóa một mutex, thực hiện các hoạt động, sau đó mở khóa nó, nếu các hoạt động ném một ngoại lệ hoặc nếu không gây ra thread chết, mutex vẫn bị khóa. Đây là lý do tại sao có một số lớp khóa có phạm vi (như QMutexLocker) kể từ khi được nêu là here, bạn được đảm bảo rằng trình phá hủy sẽ chạy. Vì vậy, nếu bạn sử dụng một khóa scoped nó sẽ luôn luôn mở khóa trên phá hủy ngăn chặn một khóa chết.

  2. Trả về cửa sổ void vì lợi ích của tốc độ: SGI FAQ và để ngăn chặn các ngoại lệ có thể được tạo bởi trình tạo bản sao đối tượng.

  3. PIMPL được sử dụng nặng nề bởi khung Qt để cung cấp khả năng tương thích nhị phân. Nó cho phép bạn ẩn tất cả các bên trong của một cấu trúc dữ liệu từ API công cộng. Điều này có nghĩa, nếu bạn muốn thêm thành viên riêng vào một lớp, bạn thêm nó vào con trỏ d. Điều này duy trì Binary Code Compatibility vì thành viên dữ liệu duy nhất tiếp xúc là một con trỏ.

+2

+1 cho điểm 2: Quyết định của SGI trong thiết kế STL không được giải thích về ngoại lệ an toàn, nhưng hiệu quả. Tất nhiên điều đó không cho thấy liệu quá trình tiêu chuẩn hóa C++ có giữ nguyên lý do tương tự hay không, hoặc đi đến cùng một kết luận theo một cách khác. –

3

Đọc "Đặc điểm C++" của Herb Sutter và "Kiểu C++ vượt trội".

+0

Tôi vừa định nói điều đó. "Ngoài ra C++" bao gồm ba chủ đề (và những người khác) rất kỹ lưỡng. –

9

Tôi sẽ trả lời điểm 2 và để phần còn lại cho người khác. Lý do chính tại sao pop không trả lại giá trị đã xóa là do sự an toàn ngoại lệ.

Đầu tiên, hãy hiểu rằng các vùng chứa C++ (không giống như, ví dụ, Java) chứa đối tượng của chúng theo giá trị. Điều đó có nghĩa rằng nếu bạn muốn container trả về đối tượng tại thời điểm popping, nó phải trả về đối tượng của bạn theo giá trị, bằng cách sao chép đối tượng đang bị xóa. Ngược lại, khi bạn truy cập top trước pop, nó có thể đơn giản trả về tham chiếu đến phần tử trên cùng và bạn có thể sao chép nó vào nội dung trái tim của bạn trước khi bật nó. (Trong khi đó, nếu pop trả lại phần tử theo tham chiếu, nó sẽ là tham chiếu treo lơ lửng, vì đối tượng không còn trong vùng chứa nữa.)

Hậu quả của việc làm cho pop trả về giá trị (ngoài việc không hiệu quả liên quan đến sao chép đối tượng bị xóa) là nó gây nguy hiểm cho sự an toàn ngoại lệ. Lý tưởng nhất, nếu một hoạt động ném một ngoại lệ, trạng thái của các đối tượng liên quan là không thay đổi. Nhưng nếu pop đã trả về đối tượng đã xóa theo giá trị, điều gì xảy ra nếu hàm tạo bản sao cho đối tượng đó không thành công? Đối tượng đã bị xóa khỏi vùng chứa và do đó trạng thái đã được thay đổi.

Đây là từ ngữ hơn tôi muốn làm, nhưng hy vọng cung cấp cho bạn một ý tưởng tại sao pop trả lại giá trị là một ý tưởng tồi.

+2

Điều này không giải thích tại sao các hàm mutator khác trong thư viện chuẩn trả về các kiểu không được xây dựng theo giá trị (thường là các trình lặp, có các hàm tạo là * không * để có khả năng ném). Nhưng có, thực tế là họ không cung cấp một bảo đảm ngoại lệ mạnh mẽ không có nghĩa là top/pop không nên. –

+0

'mẫu tên tệp C :: value_type pop (C & c) {tên tệp C :: value_type copy = c.top(); c.pop(); trả lại bản sao; } 'Vấn đề an toàn ngoại lệ duy nhất là nếu dtor của vật phẩm có thể ném, nhưng ném dtors nên * cực kỳ * hiếm, và một dtor không ném có thể dễ dàng được yêu cầu gọi pop này(), tương tự như các yêu cầu container khác. –

+0

@Roger: Đó là tốt, nhưng (trên ngoại lệ an toàn) trình biên dịch không cần thiết để thực hiện RVO, trong trường hợp đó, 'return copy' sẽ gây ra các bản sao constructor được gọi (một lần nữa). (PS ném dtors là rất xấu Tin tức (tm), bởi vì nếu nó xảy ra trong stack unwinding, 'std :: terminate' được gọi, hoặc một cái gì đó để có hiệu lực. Vì vậy, một trong những có thể/nên an toàn giả định rằng điều này không nên xảy ra.) –