Là quản lý tài nguyên phi quyết định có phải là một sự trừu tượng hóa không?


9

Từ những gì tôi có thể thấy, có hai hình thức quản lý tài nguyên phổ biến: phá hủy xác định và rõ ràng. Các ví dụ trước đây sẽ là các hàm hủy của C ++ và các con trỏ thông minh hoặc phụ DESTROY của Perl, trong khi một ví dụ về cái sau sẽ là mô hình khối tài nguyên quản lý khối của Ruby hoặc giao diện IDispose của .NET.

Các ngôn ngữ mới hơn dường như chọn cách thứ hai, có lẽ là tác dụng phụ của việc sử dụng các hệ thống thu gom rác thuộc loại không đếm tham chiếu.

Câu hỏi của tôi là: cho rằng các công cụ phá hủy cho con trỏ thông minh hoặc hệ thống thu gom rác đếm tham chiếu - gần như giống nhau - cho phép phá hủy tài nguyên ngầm và minh bạch, có phải là một sự trừu tượng ít rò rỉ hơn các loại không xác định dựa trên tường minh ký hiệu?

Tôi sẽ đưa ra một ví dụ cụ thể. Nếu bạn có ba lớp con C ++ của một siêu lớp đơn, một lớp có thể có một triển khai không cần bất kỳ sự phá hủy cụ thể nào. Có lẽ nó làm phép thuật của nó theo một cách khác. Thực tế là nó không cần bất kỳ sự phá hủy đặc biệt nào là không liên quan - tất cả các lớp con vẫn được sử dụng theo cùng một cách.

Một ví dụ khác sử dụng các khối Ruby. Hai lớp con cần giải phóng tài nguyên, vì vậy siêu lớp chọn một giao diện sử dụng một khối trong hàm tạo, mặc dù các lớp con cụ thể khác có thể không cần nó vì chúng không yêu cầu phá hủy đặc biệt.

Đây có phải là trường hợp mà sau này rò rỉ chi tiết thực hiện của việc phá hủy tài nguyên, trong khi cái trước thì không?

EDIT: So sánh, giả sử, Ruby với Perl có thể công bằng hơn vì một người có sự phá hủy mang tính quyết định và người kia thì không, nhưng cả hai đều được thu gom rác.


5
Tôi muốn nói "có", nhưng tôi thích nghe những gì người khác nói về điều này.
Bart van Ingen Schenau

Phá hủy tài nguyên minh bạch? Ngoài thực tế là bạn phải sử dụng con trỏ thông minh thay vì con trỏ thông thường? Tôi không nghĩ rằng điều này minh bạch hơn là chỉ có một cơ chế (tham chiếu) để truy cập các đối tượng (trong C ++, bạn có ít nhất bốn hoặc năm).
Giorgio

@Giorgio: "Cách truy cập một đối tượng" khá mơ hồ. Bạn có nghĩa là đọc hoặc viết? Const / Trình độ dễ bay hơi? Con trỏ không thực sự là "một cách để truy cập một đối tượng"; hầu như bất kỳ biểu thức nào cũng dẫn đến một đối tượng và bỏ qua một con trỏ không phải là đặc biệt.
MSalters

1
@Giorgio: Theo nghĩa OOP đó, bạn không thể gửi tin nhắn đến con trỏ C ++. Bạn cần phải hủy đăng ký con trỏ để gửi tin nhắn (*ptr).Message()hoặc tương đương ptr->Message(). Có một tập hợp vô hạn các biểu thức được phép, ((*ptr))->Messagecũng tương đương. Nhưng tất cả đều sôi sục xuốngexpressionIdentifyingAnObject.Message()
MSalters

1
Với việc đếm tiền bạn cần cẩn thận về việc tránh vòng tròn. Vì vậy, sự trừu tượng cũng bị rò rỉ, theo một cách khác.
CodeInChaos

Câu trả lời:


2

Ví dụ của riêng bạn trả lời câu hỏi. Sự phá hủy trong suốt rõ ràng là ít rò rỉ hơn sự phá hủy rõ ràng. Nó có thể bị rò rỉ, nhưng ít rò rỉ hơn.

Phá hủy hoàn toàn tương tự như malloc / miễn phí trong C với tất cả các cạm bẫy. Có thể với một số đường cú pháp để làm cho nó xuất hiện dựa trên phạm vi.

Một số lợi ích của việc hủy trong suốt so với rõ ràng: -
mô hình sử dụng -
bạn không thể quên giải phóng tài nguyên.
- làm sạch chi tiết không xả rác cảnh quan tại điểm sử dụng.


2

Sự thất bại trong sự trừu tượng thực ra không phải là thực tế rằng việc thu gom rác là không xác định, mà là ở ý tưởng rằng các đối tượng "quan tâm" đến những thứ mà họ giữ tài liệu tham khảo và không quan tâm đến những thứ mà họ không nắm giữ người giới thiệu. Để xem lý do tại sao, hãy xem xét kịch bản của một đối tượng duy trì bộ đếm về tần suất một điều khiển cụ thể được vẽ. Khi tạo, nó đăng ký vào sự kiện "vẽ" của điều khiển và xử lý nó hủy đăng ký. Sự kiện nhấp chỉ đơn giản là tăng một trường và một phương thức getTotalClicks()trả về giá trị của trường đó.

Khi đối tượng truy cập được tạo, nó phải khiến một tham chiếu đến chính nó được lưu trữ trong phạm vi kiểm soát mà nó giám sát. Kiểm soát thực sự không quan tâm đến đối tượng truy cập, và sẽ rất vui nếu đối tượng truy cập và tham chiếu đến nó, không còn tồn tại, nhưng miễn là tham chiếu tồn tại, nó sẽ gọi trình xử lý sự kiện của đối tượng đó mỗi lần nó tự sơn. Hành động này hoàn toàn vô dụng đối với điều khiển, nhưng sẽ hữu ích cho bất kỳ ai từng gọi getTotalClicks()đối tượng.

Nếu ví dụ: một phương pháp là tạo ra một đối tượng "bộ đếm sơn" mới, thực hiện một số hành động trên điều khiển, quan sát số lần điều khiển được sơn lại, và sau đó từ bỏ đối tượng bộ đếm sơn, đối tượng sẽ vẫn đăng ký vào sự kiện mặc dù không ai quan tâm nếu đối tượng và tất cả các tham chiếu đến nó chỉ đơn giản biến mất. Các đối tượng sẽ không đủ điều kiện để thu thập, tuy nhiên, cho đến khi chính điều khiển là. Nếu phương thức đó là một phương thức sẽ được gọi nhiều nghìn lần trong vòng đời của điều khiển [một kịch bản hợp lý], thì nó có thể gây ra tràn bộ nhớ nhưng thực tế là chi phí của các lệnh gọi N có thể sẽ là O (N ^ 2) hoặc O (N ^ 3) trừ khi xử lý đăng ký rất hiệu quả và hầu hết các hoạt động không thực sự liên quan đến bất kỳ bức tranh nào.

Kịch bản cụ thể này có thể được xử lý bằng cách cung cấp cho điều khiển giữ tham chiếu yếu đến đối tượng truy cập thay vì mạnh. Một mô hình đăng ký yếu là hữu ích, nhưng không hoạt động trong trường hợp chung. Giả sử thay vì muốn có một đối tượng theo dõi một loại sự kiện từ một điều khiển duy nhất, thì người ta muốn có một đối tượng ghi nhật ký sự kiện theo dõi một số điều khiển và cơ chế xử lý sự kiện của hệ thống là mỗi điều khiển cần một tham chiếu đến một đối tượng logger sự kiện khác nhau. Trong trường hợp đó, đối tượng liên kết điều khiển với bộ ghi sự kiện sẽ chỉ tồn tại miễn là cả haikiểm soát được theo dõi và bộ ghi sự kiện vẫn hữu ích. Nếu cả trình điều khiển và trình ghi sự kiện không tham chiếu mạnh đến sự kiện liên kết, nó sẽ không còn tồn tại mặc dù nó vẫn "hữu ích". Nếu một trong hai sự kiện mạnh, thời gian tồn tại của đối tượng liên kết có thể được kéo dài vô ích ngay cả khi đối tượng kia chết.

Nếu không có tham chiếu đến một vật thể tồn tại ở bất cứ đâu trong vũ trụ, vật thể đó có thể được coi là vô dụng và bị loại khỏi sự tồn tại một cách an toàn. Tuy nhiên, thực tế là một tham chiếu tồn tại cho một đối tượng, tuy nhiên, không ngụ ý rằng đối tượng đó là "hữu ích". Trong nhiều trường hợp, tính hữu dụng thực tế của các đối tượng sẽ phụ thuộc vào sự tồn tại của các tham chiếu đến các đối tượng khác - từ quan điểm của GC - hoàn toàn không liên quan đến chúng.

Nếu các đối tượng được thông báo một cách xác định khi không ai quan tâm đến chúng, họ sẽ có thể sử dụng thông tin đó để đảm bảo rằng bất kỳ ai sẽ được hưởng lợi từ kiến ​​thức đó đều được thông báo. Tuy nhiên, trong trường hợp không có thông báo như vậy, không có cách chung nào để xác định đối tượng nào được coi là "hữu ích" nếu người ta chỉ biết tập hợp các tham chiếu tồn tại và không có ý nghĩa ngữ nghĩa được đính kèm với các tham chiếu đó. Do đó, bất kỳ mô hình nào giả định rằng sự tồn tại hoặc không tồn tại của các tham chiếu là đủ để quản lý tài nguyên tự động sẽ bị tiêu diệt ngay cả khi GC có thể phát hiện ngay việc từ bỏ đối tượng.


0

Không có "kẻ hủy diệt, hoặc giao diện khác nói rằng" lớp này phải bị hủy "là một hợp đồng của giao diện đó. Nếu bạn tạo một kiểu con không yêu cầu phá hủy đặc biệt, tôi sẽ có xu hướng xem xét việc vi phạm Nguyên tắc thay thế Liskov .

Đối với C ++ so với những người khác, không có nhiều khác biệt. C ++ buộc giao diện đó trên tất cả các đối tượng của nó. Trừu tượng không thể rò rỉ khi ngôn ngữ yêu cầu.


4
"Nếu bạn tạo một kiểu con không yêu cầu phá hủy đặc biệt" Đó không phải là vi phạm LSP, vì no-op là trường hợp hủy diệt đặc biệt hợp lệ. Vấn đề là khi bạn thêm yêu cầu hủy vào lớp dẫn xuất.
CodeInChaos

Tôi đang bối rối ở đây. Nếu một người cần thêm mã hủy đặc biệt vào lớp con C ++, thì nó hoàn toàn không thay đổi kiểu sử dụng của nó, bởi vì nó tự động. Điều đó có nghĩa là siêu lớp và lớp con vẫn có thể được sử dụng thay thế cho nhau. Nhưng với ký hiệu rõ ràng để quản lý tài nguyên, một lớp con cần phá hủy rõ ràng sẽ khiến việc sử dụng nó không tương thích với siêu lớp, phải không? (Giả sử siêu lớp không cần phá hủy rõ ràng.)
Louis Jackman

@CodesInChaos - à vâng, tôi cho rằng điều đó đúng.
Telastyn

@ljackman: Một lớp đòi hỏi sự phá hủy đặc biệt đặt ra gánh nặng cho bất cứ ai gọi người xây dựng của nó để đảm bảo rằng nó được thực hiện. Điều này không tạo ra vi phạm LSP vì DerivedFooThatRequiresSpecialDestructionchỉ có thể được tạo bằng mã mà gọi new DerivedFooThatRequiresSpecialDestruction(). Mặt khác, một phương thức xuất xưởng trả về DerivedFooThatRequiresSpecialDestructionmã không mong đợi thứ gì đó cần hủy, sẽ là vi phạm LSP.
supercat

0

Câu hỏi của tôi là: cho rằng các công cụ phá hủy cho con trỏ thông minh hoặc hệ thống thu gom rác đếm tham chiếu - gần như giống nhau - cho phép phá hủy tài nguyên ngầm và minh bạch, có phải là một sự trừu tượng ít rò rỉ hơn các loại không xác định dựa trên tường minh ký hiệu?

Phải theo dõi chu kỳ bằng tay không phải là ngầm hay minh bạch. Ngoại lệ duy nhất là một hệ thống đếm tham chiếu với ngôn ngữ cấm các chu kỳ theo thiết kế. Erlang có thể là một ví dụ về một hệ thống như vậy.

Vì vậy, cả hai cách tiếp cận rò rỉ. Sự khác biệt chính là các hàm hủy bị rò rỉ ở mọi nơi trong C ++ nhưng IDisposerất hiếm trên .NET.


1
Ngoại trừ các chu kỳ là cực kỳ hiếm và thực tế không bao giờ xảy ra ngoại trừ trong các cấu trúc dữ liệu rõ ràng là theo chu kỳ. Sự khác biệt chính là các hàm hủy trong C ++ được xử lý đúng cách ở mọi nơi nhưng IDispose hiếm khi xử lý sự cố trong .NET.
DeadMG

"Ngoại trừ chu kỳ là cực kỳ hiếm". Trong ngôn ngữ hiện đại? Tôi sẽ thách thức giả thuyết đó.
Jon Harrop
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.