2009-11-08 39 views
13

Từ những gì tôi hiểu, trong tiêu chuẩn C++ bất cứ khi nào bạn sử dụng toán tử mới, bạn cũng phải sử dụng toán tử xóa tại một số điểm để tránh rò rỉ bộ nhớ. Điều này là do không có bộ sưu tập rác trong C++. Trong bộ sưu tập rác .NET là tự động vì vậy không cần phải lo lắng về việc quản lý bộ nhớ. Tôi hiểu có đúng không? Cảm ơn.Tự động thu gom rác trong tiêu chuẩn C++?

+9

Một trăm phần trăm chính xác! – henle

+10

99,9% chính xác. Có những trường hợp bạn cần phải lo lắng về việc quản lý bộ nhớ trong .NET. – jmucchiello

+1

trong C++/CLI bạn phân bổ bằng cách sử dụng 'gcnew' tuy nhiên bạn vẫn có thể sử dụng 'mới' nhưng sau đó chúng cần phải được ghép nối với 'xóa', giống như trong mã không được quản lý hoàn toàn. –

Trả lời

19

Câu trả lời dài để nó là cho mỗi lần new được gọi là, nơi nào đó, bằng cách nào đó, delete phải được gọi, hoặc một số chức năng deallocation khác (phụ thuộc vào bộ cấp phát bộ nhớ vv)

Nhưng bạn không cần phải là người cung cấp cuộc gọi delete:

  1. Có thu gom rác thải cho C++, dưới dạng Hans-Boehm Garbage Collector. Ngoài ra còn có các thư viện thu gom rác khác.
  2. Bạn có thể sử dụng con trỏ thông minh, sử dụng RAII (và tính tham chiếu nếu con trỏ cho phép truy cập chia sẻ) để xác định thời điểm xóa đối tượng. Một thư viện con trỏ thông minh tốt là số smart pointer của Boost. Con trỏ thông minh trong phần lớn các trường hợp có thể thay thế con trỏ thô.
  3. Một số khung ứng dụng, như Qt, xây dựng cây đối tượng, sao cho có mối quan hệ cha mẹ con cho các đối tượng được cấp phát của khung công tác.Kết quả là, tất cả là cần thiết cho một delete để được gọi trên một đối tượng, và tất cả các con của nó sẽ tự động được delete d là tốt.

Nếu bạn không muốn sử dụng bất kỳ kỹ thuật nào trong số này, để bảo vệ chống lại rò rỉ bộ nhớ, bạn có thể thử sử dụng công cụ kiểm tra bộ nhớ. Valgrind đặc biệt tốt, mặc dù nó chỉ hoạt động trên Linux

Đối với .NET, có, phân bổ bằng cách sử dụng gcnew có nghĩa là bộ nhớ được theo dõi bởi .NET, do đó không có rò rỉ. Tuy nhiên, các tài nguyên khác, như xử lý tệp vv không được quản lý bởi GC.

+6

Một trong những lợi thế (!) Của RAII là bạn có thể quản lý tài nguyên khác với bộ nhớ, ví dụ: xử lý tệp. Xóa đối tượng, và thì cũng là tập tin thuộc sở hữu của đối tượng được đóng lại. – casualcoder

+0

Đừng quên về SBRM trong khi chúng tôi đang ném xung quanh từ viết tắt. ví dụ như fstream sẽ tự động đóng tệp khi nó nằm ngoài phạm vi. Cũng nên nhớ rằng các biến thành viên có một phạm vi của tuổi thọ của đối tượng cha, điều này làm cho SBRM rất thuận tiện ngay cả khi không có một destructor. – joshperry

+1

Tôi nghĩ rằng SBRM bạn đề cập đến cho fstream là RAII. – blwy10

0

Chính xác bạn phải lo lắng về việc thu gom rác thải trên C++.

Và ... có no need to worry about garbage collection on .NET.

Chỉ khi bạn có các tập lệnh dài và chuyên sâu mà bạn cảm thấy cần tối ưu hóa, bạn có cần tập trung vào điều đó không.

Chỉnh sửa: Cả nhận xét của asveikau và Pavel Minaev đều rất tuyệt vời! Tôi đã quá mức để truyền tải thông điệp.

+0

Tôi sẽ không nói hoàn toàn rằng bạn "không phải lo lắng về nó". Đối với một số loại đối tượng, bạn _do_ cần phải cẩn thận để thực hiện dọn dẹp thủ công, ngay cả trong C#. Ví dụ, bạn nên chắc chắn đóng các tập tin, và không dựa vào GC để làm điều đó cho bạn. – asveikau

+1

Và, tất nhiên, không có gì ngăn bạn phân bổ bộ nhớ không được quản lý trong C# (hoặc ngôn ngữ .NET khác), mà sau đó bạn phải tự giải phóng. Xem 'Marshal.AllocHGlobal' vv –

1

"không có bộ sưu tập rác trong C++".

đúng.

+2

Không đúng sự thật. Có sẵn những người thu gom rác. (Và nó không phải là khó để xây dựng của riêng bạn). –

+1

"có sẵn" hoặc "một tính năng tiêu chuẩn của ngôn ngữ?" Câu hỏi đặt ra là về các tính năng tiêu chuẩn của ngôn ngữ. Không có sẵn. Smart Pointers (IMO) là điều cần thiết, nhưng không phải là một tính năng tiêu chuẩn của ngôn ngữ. –

2

Bạn có thể sử dụng C++ với .NET theo hai cách: được quản lý hoặc không được quản lý. Trong chế độ được quản lý, bộ sưu tập rác của .NET sẽ xử lý việc giải phóng bộ nhớ thay cho bạn; trong chế độ không được quản lý, bạn gần gũi với hành vi bình thường/chuẩn của C++, vì vậy bạn phải tự chịu trách nhiệm về bộ nhớ của mình.

+3

Bất kể "chế độ", mỗi 'new' là bộ nhớ không được quản lý và phải được deallocated bằng tay bởi' xóa'. C++/CLI thêm 'gcnew', nhưng đây không phải là C++ như vậy, mà là một phần mở rộng ngôn ngữ. –

+2

Thực sự chúng ta đang nói về hai ngôn ngữ khác nhau. C++ và C++/CLI –

2

Có bạn đúng, theo chuẩn C++ (Trong C++ được quản lý hoặc các biến thể khác tùy thuộc) bạn phải sử dụng xóa sau mỗi lần mới. Trong C#, Java và các ngôn ngữ thu gom rác khác, điều này là không cần thiết (thực tế hầu hết chúng không tương đương với toán tử "delete").

1

thu gom rác thải tự động là hữu ích, nhưng bạn vẫn có thể nhận được rò rỉ bộ nhớ, như câu hỏi này cho thấy:

Memory Leaks in C# WPF

Nó được giảm trong .NET và Java, nhưng điều đó không có nghĩa là nó cho phép xấu mã hóa để được chăm sóc tự động. Vì vậy, trong C++ bạn cần phải giải thích rõ ràng những gì bạn yêu cầu, và tôi nghĩ rằng đôi khi tốt hơn, vì bạn biết những gì đang diễn ra. Tôi muốn trong .NET và Java rằng bộ thu gom rác đã làm ít trong chế độ gỡ lỗi, để giúp đảm bảo mọi người nhận thức được những gì họ đang làm.

8

Tuyên bố của bạn về toán tử mới hoàn toàn chính xác ... nhưng nó quá đơn giản hóa ngữ nghĩa C++ một chút.

Trong C++, các đối tượng có thể được tạo ra trên stack hay trên heap:

class Foo {}; 

int main() { 
    Foo obj1; 
    Foo* obj2 = new Foo(); 
    delete obj2; 
} 

Trong ví dụ trên, obj1 được tạo ra trên stack và obj2 được tạo ra trên heap (với mới). Các đối tượng được tạo trên heap không bị hủy cho đến khi xóa được gọi rõ ràng trên chúng. Tuy nhiên, các đối tượng trên ngăn xếp sẽ tự động bị hủy khi chúng nằm ngoài phạm vi (ví dụ: khi hàm main() trả về trong ví dụ này).

Điều này cho phép thành phần "Khởi tạo tài nguyên đang khởi tạo" (a.k.a. RAII) bằng C++, mạnh hơn nhiều so với thu thập rác cơ bản. Các tài nguyên cần được dọn sạch (bộ nhớ heap, ổ cắm, tệp, kết nối DB, v.v.) thường được đặt trong các đối tượng dựa trên ngăn xếp có các trình phá hủy xử lý việc dọn dẹp.

Ngược lại, Java và C# không cho phép các đối tượng được xây dựng trên ngăn xếp, và không đảm bảo rằng bộ sưu tập sẽ xảy ra cũng như finalizers sẽ chạy (Tôi không phải là một C# guy, vì vậy tôi có thể là một ít sai lầm ở đó). Vì vậy, trong khi bạn nhận được quản lý bộ nhớ heap miễn phí trong Java/C#, bạn sẽ thực sự kết thúc với rất nhiều mã nguồn tài nguyên dọn dẹp trong các ngôn ngữ đó nhiều hơn bạn làm trong C++.

8

Trong idiomatic mức cao C++, bạn không bao giờ gọi xóa.

C++ không có bộ thu gom rác tiêu chuẩn hoạt động giống như trong C#, và do đó, về cơ bản, newdelete cần phải được ghép nối. Tuy nhiên, có các cơ chế trong C++ hoàn toàn loại bỏ việc sử dụng rõ ràng sử dụng delete cho mã được viết theo phong cách hiện đại.

Điều đầu tiên cần lưu ý là trong C++ bạn sử dụng newít thường xuyên hơn hơn bạn sử dụng new trong C#. Điều này là do trong C# bạn sử dụng new bất cứ khi nào bạn tạo một thể hiện của một cấu trúc, lớp hoặc mảng, nhưng trong C++, bạn chỉ sử dụng new khi bạn muốn quản lý một phần tử dữ liệu động. Hầu hết dữ liệu trong C++ không yêu cầu quản lý động và do đó có thể được tạo mà không cần sử dụng new. [Nói cách khác, new có ý nghĩa khác trong C# so với C++. Trong C++ nó chỉ rõ phân bổ động, trong khi trong C# nó được sử dụng cho bất kỳ công trình xây dựng nào.]

Thứ hai, bất cứ lúc nào bạn gọi new trong C++, giá trị trả về phải được chuyển trực tiếp đến smart pointer. Con trỏ thông minh sẽ đảm bảo rằng delete được tự động gọi cho bạn vào thời điểm thích hợp.

Nhân tiện, trừ khi bạn là một guru viết thư viện cấp thấp (hoặc học sinh học cách thực hiện điều này), bạn không bao giờ gọi new để cấp phát một mảng trong C++. Thư viện chuẩn (và cũng là Boost/TR1) cung cấp các lớp mẫu phân bổ và quản lý các mảng cho bạn.

Tóm lại, C++ không sử dụng một nhà sưu tập rác nhưng nó có hình thức riêng của mình quản lý bộ nhớ tự động. Có những khác biệt tinh tế giữa hai cách tiếp cận, nhưng cả hai cách tiếp cận tự động hóa việc giải phóng bộ nhớ, do đó loại bỏ hầu hết các loại rò rỉ bộ nhớ.

Các cuộc biểu tình có thẩm quyền của các khái niệm được đưa ra bởi C++ tạo Bjarne Stroustrup trong câu trả lời cho câu hỏi: How do I deal with memory leaks?

Xem thêm:

+0

Bởi cùng một mã thông báo, nếu bạn sử dụng xóa, nó có thể là một lỗi. Vấn đề là việc sử dụng xóa giả định rằng bạn biết chính xác cách thức luồng điều khiển của chương trình sẽ tiến hành sao cho việc xóa sẽ xảy ra vào thời điểm thích hợp. Đây là một cách dễ dàng, nhưng ngây thơ, giả định để thực hiện. Trước hết, nó đòi hỏi một số biện pháp cẩn thận để đúng vị trí hoạt động xóa. Quan trọng hơn, ngoại lệ thường có thể xảy ra ở bất kỳ điểm trung gian nào trong mã và vi phạm các giả định của bạn về luồng điều khiển. Sửa lỗi này với try/catch là có thể, nhưng nó không phải là "thực hành tốt nhất". – nobar

+1

Trình diễn của Bjarne có phần ngày tháng, đặc biệt là việc anh ta sử dụng auto_ptr <> thường được choáng váng ngày nay (có những lựa chọn thay thế tốt hơn). Nhưng điều này chứng minh sự trưởng thành của cái mà trước đây tôi gọi là "phong cách hiện đại". Bjarne đã dạy cách tiếp cận này trong một thời gian dài. – nobar

+0

Đây là một bài viết nói về cùng một điều nhưng chi tiết hơn về cơ học - bao gồm thảo luận về một số tính năng thư viện mới hơn: http://stackoverflow.com/questions/4963610/visual-c-native-memory-management- thực hành tốt nhất/4963633 # 4963633 – nobar