it-swarm-vi.tech

Hướng dẫn chung để tránh rò rỉ bộ nhớ trong C ++

Một số mẹo chung để đảm bảo tôi không bị rò rỉ bộ nhớ trong các chương trình C++ là gì? Làm thế nào để tôi tìm ra ai nên giải phóng bộ nhớ đã được phân bổ động?

125
dulipishi

Thay vì quản lý bộ nhớ theo cách thủ công, hãy thử sử dụng con trỏ thông minh nếu có.
[.___.] Hãy xem Boost lib , TR1con trỏ thông minh .
[.___.] Ngoài ra con trỏ thông minh hiện là một phần của tiêu chuẩn C++ có tên C++ 11 .

38
Andri Möll

Tôi hoàn toàn tán thành tất cả các lời khuyên về RAII và các con trỏ thông minh, nhưng tôi cũng muốn thêm một mẹo cấp cao hơn một chút: bộ nhớ dễ quản lý nhất là bộ nhớ bạn không bao giờ phân bổ. Không giống như các ngôn ngữ như C # và Java, nơi mọi thứ đều là tài liệu tham khảo, trong C++, bạn nên đặt các đối tượng lên ngăn xếp bất cứ khi nào bạn có thể. Như tôi đã thấy một số người (bao gồm cả Dr Stroustrup) chỉ ra, lý do chính tại sao bộ sưu tập rác chưa bao giờ phổ biến trong C++ là vì C++ được viết tốt không tạo ra nhiều rác ngay từ đầu.

Đừng viết

Object* x = new Object;

hoặc thậm chí

shared_ptr<Object> x(new Object);

khi nào bạn có thể viết

Object x;
196
Ross Smith

Sử dụng RAII

  • Quên bộ sưu tập rác (Thay vào đó hãy sử dụng RAII). Lưu ý rằng ngay cả Trình thu gom rác cũng có thể bị rò rỉ (nếu bạn quên "null" một số tham chiếu trong Java/C #) và Trình thu gom rác sẽ không giúp bạn loại bỏ tài nguyên (nếu bạn có một đối tượng thu được xử lý một tệp, tệp sẽ không được giải phóng tự động khi đối tượng sẽ ra khỏi phạm vi nếu bạn không thực hiện thủ công trong Java hoặc sử dụng mẫu "vứt bỏ" trong C #).
  • Quên quy tắc "một lần trả cho mỗi hàm" . Đây là một lời khuyên C tốt để tránh rò rỉ, nhưng nó đã lỗi thời trong C++ vì sử dụng ngoại lệ (thay vào đó sử dụng RAII).
  • Và mặc dù "Mô hình bánh sandwich" là một lời khuyên C tốt, nhưng đã lỗi thời trong C++ vì sử dụng ngoại lệ (thay vào đó sử dụng RAII).

Bài đăng này dường như được lặp đi lặp lại, nhưng trong C++, mẫu cơ bản nhất cần biết là RAII .

Tìm hiểu cách sử dụng con trỏ thông minh, cả từ boost, TR1 hoặc thậm chí là thấp (nhưng thường đủ hiệu quả) auto_ptr (nhưng bạn phải biết giới hạn của nó).

RAII là cơ sở của cả an toàn ngoại lệ và xử lý tài nguyên trong C++ và không có mô hình nào khác (sandwich, v.v.) sẽ cung cấp cho bạn cả hai (và hầu hết thời gian, nó sẽ không cung cấp cho bạn).

Xem bên dưới so sánh mã RAII và mã RAII:

void doSandwich()
{
   T * p = new T() ;
   // do something with p
   delete p ; // leak if the p processing throws or return
}

void doRAIIDynamic()
{
   std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

void doRAIIStatic()
{
   T p ;
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

Giới thiệu RAII

Để tóm tắt (sau khi nhận xét từ Ogre Psalm33), RAII dựa trên ba khái niệm:

  • Một khi đối tượng được xây dựng, nó chỉ hoạt động! Không có được tài nguyên trong các nhà xây dựng.
  • Phá hủy đối tượng là đủ! Làm tài nguyên miễn phí trong hàm hủy.
  • Đó là tất cả về phạm vi! Các đối tượng có phạm vi (xem ví dụ doRAIIStatic ở trên) sẽ được xây dựng theo khai báo của chúng và sẽ bị hủy ngay khi thực thi thoát khỏi phạm vi, bất kể lối ra (trả lại, ngắt, ngoại lệ, v.v.).

Điều này có nghĩa là trong mã C++ chính xác, hầu hết các đối tượng sẽ không được xây dựng với new và sẽ được khai báo trên ngăn xếp thay thế. Và đối với những người được xây dựng bằng cách sử dụng new, tất cả sẽ bằng cách nào đó phạm vi (ví dụ: được gắn vào một con trỏ thông minh).

Là một nhà phát triển, điều này thực sự rất mạnh mẽ vì bạn sẽ không cần quan tâm đến việc xử lý tài nguyên thủ công (như được thực hiện trong C hoặc đối với một số đối tượng trong Java, điều này sử dụng nhiều try/finally cho trường hợp đó ) ...

Chỉnh sửa (2012/02/12)

"Các đối tượng trong phạm vi ... sẽ bị phá hủy ... bất kể lối ra" không hoàn toàn đúng. Có nhiều cách để lừa đảo RAII. bất kỳ hương vị chấm dứt () sẽ bỏ qua việc dọn dẹp. exit (EXIT_SUCCESS) là một oxymoron trong vấn đề này.

- wilmustell

wilmustell hoàn toàn đúng về điều đó: Có ngoại lệ để lừa đảo RAII, tất cả đều dẫn đến quá trình dừng đột ngột.

Đó là những cách ngoại lệ vì mã C++ không bị vấy bẩn khi chấm dứt, thoát, v.v. hoặc trong trường hợp có ngoại lệ, chúng tôi muốn có ngoại lệ chưa xử lý để phá vỡ quy trình và lõi kết xuất hình ảnh bộ nhớ của nó, và không phải sau khi làm sạch.

Nhưng chúng ta vẫn phải biết về những trường hợp đó bởi vì, trong khi chúng hiếm khi xảy ra, chúng vẫn có thể xảy ra.

(ai gọi terminate hoặc exit bằng mã C++ thông thường? ... Tôi nhớ phải xử lý vấn đề đó khi chơi với GLUT : Thư viện này rất hướng đến C, thiết kế theo hướng tích cực điều này gây khó khăn cho các nhà phát triển C++ như không quan tâm đến ngăn xếp dữ liệu được phân bổ hoặc có các quyết định "thú vị" về không bao giờ quay lại từ vòng lặp chính của họ ... Tôi sẽ không bình luận về điều đó).

101
paercebal

Bạn sẽ muốn xem các con trỏ thông minh, chẳng hạn như con trỏ thông minh của boost .

Thay vì

int main()
{ 
    Object* obj = new Object();
    //...
    delete obj;
}

boost :: shared_ptr sẽ tự động xóa sau khi số tham chiếu bằng 0:

int main()
{
    boost::shared_ptr<Object> obj(new Object());
    //...
    // destructor destroys when reference count is zero
}

Lưu ý lưu ý cuối cùng của tôi, "khi số tham chiếu bằng 0, đó là phần thú vị nhất. Vì vậy, nếu bạn có nhiều người dùng đối tượng của mình, bạn sẽ không phải theo dõi xem đối tượng có còn được sử dụng hay không. con trỏ chia sẻ, nó bị phá hủy.

Đây không phải là thuốc chữa bách bệnh, tuy nhiên. Mặc dù bạn có thể truy cập con trỏ cơ sở, bạn sẽ không muốn chuyển nó sang API của bên thứ 3 trừ khi bạn tự tin với những gì nó đang làm. Rất nhiều lần, công cụ "đăng" của bạn lên một số chủ đề khác để hoàn thành công việc SAU KHI phạm vi tạo đã kết thúc. Điều này phổ biến với PostThreadMessage trong Win32:

void foo()
{
   boost::shared_ptr<Object> obj(new Object()); 

   // Simplified here
   PostThreadMessage(...., (LPARAM)ob.get());
   // Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}

Như mọi khi, hãy sử dụng nắp suy nghĩ của bạn với bất kỳ công cụ nào ...

25
Doug T.

Đọc tiếp RAII và chắc chắn rằng bạn hiểu nó.

12
Hank

Hầu hết các rò rỉ bộ nhớ là kết quả của việc không rõ ràng về quyền sở hữu đối tượng và tuổi thọ.

Điều đầu tiên cần làm là phân bổ trên Stack bất cứ khi nào bạn có thể. Điều này xử lý hầu hết các trường hợp bạn cần phân bổ một đối tượng cho một số mục đích.

Nếu bạn cần 'mới' một đối tượng thì hầu hết thời gian nó sẽ có một chủ sở hữu rõ ràng duy nhất trong suốt quãng đời còn lại. Trong tình huống này, tôi có xu hướng sử dụng một loạt các mẫu bộ sưu tập được thiết kế để 'sở hữu' các đối tượng được lưu trữ trong chúng bằng con trỏ. Chúng được thực hiện với vectơ STL và các thùng chứa bản đồ nhưng có một số khác biệt:

  • Những bộ sưu tập này không thể được sao chép hoặc gán cho. (một khi chúng chứa các đối tượng.)
  • Con trỏ đến các đối tượng được chèn vào chúng.
  • Khi bộ sưu tập bị xóa, hàm hủy được gọi đầu tiên trên tất cả các đối tượng trong bộ sưu tập. (Tôi có một phiên bản khác, nơi nó xác nhận nếu bị phá hủy và không trống.)
  • Vì họ lưu trữ con trỏ, bạn cũng có thể lưu trữ các đối tượng được kế thừa trong các thùng chứa này.

Điểm yếu của tôi với STL là nó tập trung vào các đối tượng Giá trị trong khi trong hầu hết các đối tượng ứng dụng là các thực thể duy nhất không có ngữ nghĩa sao chép có ý nghĩa cần thiết để sử dụng trong các thùng chứa đó.

11
Jeroen Dirks

Bah, những đứa trẻ và những người thu gom rác mới của bạn ...

Các quy tắc rất mạnh về "quyền sở hữu" - đối tượng hoặc bộ phận nào của phần mềm có quyền xóa đối tượng. Xóa bình luận và tên biến khôn ngoan để làm cho nó rõ ràng nếu một con trỏ "sở hữu" hoặc "chỉ nhìn, không chạm". Để giúp quyết định ai sở hữu cái gì, hãy theo dõi càng nhiều càng tốt mẫu "sandwich" trong mỗi chương trình con hoặc phương pháp.

create a thing
use that thing
destroy that thing

Đôi khi cần phải tạo và phá hủy ở những nơi khác nhau; Tôi nghĩ rằng khó để tránh điều đó.

Trong bất kỳ chương trình nào yêu cầu cấu trúc dữ liệu phức tạp, tôi tạo một cây đối tượng rõ ràng nghiêm ngặt chứa các đối tượng khác - sử dụng con trỏ "chủ sở hữu". Cây này mô hình phân cấp cơ bản của các khái niệm miền ứng dụng. Ví dụ một cảnh 3D sở hữu các vật thể, ánh sáng, kết cấu. Vào cuối kết xuất khi chương trình thoát, có một cách rõ ràng để phá hủy mọi thứ.

Nhiều con trỏ khác được định nghĩa là cần thiết bất cứ khi nào một thực thể cần truy cập vào một thực thể khác, để quét qua các tia hoặc bất cứ thứ gì; đây là "chỉ nhìn". Đối với ví dụ cảnh 3D - một đối tượng sử dụng kết cấu nhưng không sở hữu; các đối tượng khác có thể sử dụng kết cấu tương tự. Sự phá hủy của một đối tượng không không gọi sự phá hủy của bất kỳ kết cấu nào.

Vâng, nó tốn thời gian nhưng đó là những gì tôi làm. Tôi hiếm khi bị rò rỉ bộ nhớ hoặc các vấn đề khác. Nhưng sau đó tôi làm việc trong lĩnh vực giới hạn của phần mềm đồ họa, thu thập dữ liệu và khoa học hiệu năng cao. Tôi thường không xử lý các giao dịch như trong ngân hàng và thương mại điện tử, GUI hướng sự kiện hoặc hỗn loạn không đồng bộ được nối mạng cao. Có lẽ những cách làm mới có lợi thế ở đó!

10
DarenW

Câu hỏi tuyệt vời!

nếu bạn đang sử dụng c ++ và bạn đang phát triển ứng dụng boud CPU và bộ nhớ thời gian thực (như trò chơi), bạn cần phải viết Trình quản lý bộ nhớ của riêng mình.

Tôi nghĩ tốt hơn bạn có thể làm là hợp nhất một số tác phẩm thú vị của các tác giả khác nhau, tôi có thể cung cấp cho bạn một số gợi ý:

  • Phân bổ kích thước cố định được thảo luận nhiều, ở khắp mọi nơi trong mạng

  • Phân bổ đối tượng nhỏ được Alexandrescu giới thiệu vào năm 2001 trong cuốn sách hoàn hảo "Thiết kế c ++ hiện đại"

  • Một tiến bộ tuyệt vời (với mã nguồn được phân phối) có thể được tìm thấy trong một bài viết tuyệt vời trong Lập trình trò chơi Gem 7 (2008) có tên là "Công cụ phân bổ heap hiệu suất cao" được viết bởi Dimitar Lazarov

  • Một danh sách lớn các tài nguyên có thể được tìm thấy trong this bài viết

Đừng tự mình bắt đầu viết một công cụ cấp phát không đáng tin cậy ... TÀI LIỆU CỦA BẠN trước tiên.

8
ugasoft

Một kỹ thuật đã trở nên phổ biến với quản lý bộ nhớ trong C++ là RAII . Về cơ bản, bạn sử dụng các hàm tạo/hàm hủy để xử lý phân bổ tài nguyên. Tất nhiên có một số chi tiết đáng ghét khác trong C++ do an toàn ngoại lệ, nhưng ý tưởng cơ bản là khá đơn giản.

Vấn đề thường đi xuống một trong những quyền sở hữu. Tôi đặc biệt khuyên bạn nên đọc loạt C++ hiệu quả của Scott Meyers và Modern C++ Design của Andrei Alexandrescu.

5
Jason Dagit

Đã có rất nhiều về cách không rò rỉ, nhưng nếu bạn cần một công cụ để giúp bạn theo dõi rò rỉ, hãy xem:

5
fabiopedrosa

Con trỏ thông minh ở mọi nơi bạn có thể! Toàn bộ các lớp rò rỉ bộ nhớ chỉ cần biến mất.

4
DougN

Chia sẻ và biết các quy tắc sở hữu bộ nhớ trong dự án của bạn. Việc sử dụng các quy tắc COM làm cho các tham số nhất quán ([in] được sở hữu bởi người gọi, callee phải sao chép; [out] params được sở hữu bởi người gọi, callee phải tạo một bản sao nếu giữ tham chiếu; v.v.)

4
Seth Morris

valgrind cũng là một công cụ tốt để kiểm tra rò rỉ bộ nhớ chương trình của bạn khi chạy.

Nó có sẵn trên hầu hết các hương vị của Linux (bao gồm cả Android) và trên Darwin.

Nếu bạn sử dụng để viết các bài kiểm tra đơn vị cho các chương trình của mình, bạn sẽ có thói quen hệ thống chạy valgrind trong các bài kiểm tra. Nó có khả năng sẽ tránh được nhiều rò rỉ bộ nhớ ở giai đoạn đầu. Nó cũng thường dễ dàng hơn để xác định chúng trong các thử nghiệm đơn giản trong một phần mềm đầy đủ.

Tất nhiên lời khuyên này vẫn hợp lệ cho bất kỳ công cụ kiểm tra bộ nhớ khác.

4
Joseph

Ngoài ra, không sử dụng bộ nhớ được phân bổ thủ công nếu có lớp thư viện tiêu chuẩn (ví dụ: vectơ). Hãy chắc chắn rằng nếu bạn vi phạm quy tắc đó rằng bạn có một hàm hủy ảo.

3
Joseph

Nếu bạn không thể/không sử dụng một con trỏ thông minh cho một cái gì đó (mặc dù đó phải là một lá cờ đỏ khổng lồ), hãy nhập mã của bạn bằng:

allocate
if allocation succeeded:
{ //scope)
     deallocate()
}

Điều đó là hiển nhiên, nhưng hãy đảm bảo bạn nhập nó trước bạn nhập bất kỳ mã nào trong phạm vi

2
Seth Morris

Mẹo theo thứ tự quan trọng:

-Tip # 1 Luôn nhớ khai báo các hàm hủy của bạn là "ảo".

-Tip # 2 Sử dụng RAII

-Tip # 3 Sử dụng smartpoint boost

-Tip # 4 Đừng viết Smartpointers lỗi của riêng bạn, hãy sử dụng boost (trên một dự án tôi đang thực hiện ngay bây giờ Tôi không thể sử dụng boost và tôi đã phải gỡ lỗi các con trỏ thông minh của riêng mình, tôi chắc chắn sẽ không lấy cùng một lộ trình một lần nữa, nhưng sau đó một lần nữa ngay bây giờ tôi không thể thêm tăng cường cho các phụ thuộc của chúng tôi)

-Tip # 5 Nếu một số yếu tố quan trọng ngẫu nhiên/không hiệu năng (như trong các trò chơi có hàng ngàn đối tượng) hoạt động, hãy nhìn vào hộp chứa con trỏ tăng tốc của Thorsten Ottosen

-Tip # 6 Tìm tiêu đề phát hiện rò rỉ cho nền tảng bạn chọn, chẳng hạn như tiêu đề "vld" của Visual Leak Phát hiện

2
Robert Gould

Một nguồn thường xuyên của các lỗi này là khi bạn có một phương thức chấp nhận một tham chiếu hoặc con trỏ tới một đối tượng nhưng không rõ quyền sở hữu. Phong cách và bình luận quy ước có thể làm cho điều này ít có khả năng.

Đặt trường hợp hàm có quyền sở hữu đối tượng là trường hợp đặc biệt. Trong mọi tình huống xảy ra, hãy nhớ viết bình luận bên cạnh chức năng trong tệp tiêu đề cho biết điều này. Bạn nên cố gắng đảm bảo rằng trong hầu hết các trường hợp, mô-đun hoặc lớp phân bổ một đối tượng cũng chịu trách nhiệm giải quyết nó.

Sử dụng const có thể giúp rất nhiều trong một số trường hợp. Nếu một hàm sẽ không sửa đổi một đối tượng và không lưu trữ một tham chiếu đến nó vẫn tồn tại sau khi nó trả về, hãy chấp nhận một tham chiếu const. Từ việc đọc mã của người gọi, rõ ràng là chức năng của bạn không được chấp nhận quyền sở hữu đối tượng. Bạn có thể có cùng chức năng chấp nhận một con trỏ không phải là const và người gọi có thể hoặc không thể cho rằng quyền sở hữu được chấp nhận, nhưng với một tham chiếu const thì không có câu hỏi nào.

Không sử dụng tài liệu tham khảo không const trong danh sách đối số. Rất không rõ ràng khi đọc mã người gọi rằng callee có thể đã giữ một tham chiếu đến tham số.

Tôi không đồng ý với các ý kiến ​​đề xuất tham chiếu con trỏ đếm. Điều này thường hoạt động tốt, nhưng khi bạn gặp lỗi và nó không hoạt động, đặc biệt là nếu công cụ phá hủy của bạn làm một việc gì đó không tầm thường, chẳng hạn như trong một chương trình đa luồng. Chắc chắn cố gắng điều chỉnh thiết kế của bạn để không cần đếm tham chiếu nếu nó không quá khó.

2
Jonathan

Nếu bạn có thể, hãy sử dụng boost shared_ptr và auto_ptr tiêu chuẩn C++. Những người truyền đạt ngữ nghĩa sở hữu.

Khi bạn trả về auto_ptr, bạn đang nói với người gọi rằng bạn đang trao cho họ quyền sở hữu bộ nhớ.

Khi bạn trả lại shared_ptr, bạn đang nói với người gọi rằng bạn có tham chiếu đến nó và họ tham gia quyền sở hữu, nhưng đó không phải là trách nhiệm của họ.

Những ngữ nghĩa này cũng áp dụng cho các tham số. Nếu người gọi chuyển cho bạn auto_ptr, họ sẽ trao quyền sở hữu cho bạn.

1
Justin Rudd
  • Cố gắng tránh phân bổ các đối tượng một cách linh hoạt. Miễn là các lớp có các hàm tạo và hàm hủy thích hợp, hãy sử dụng một biến của loại lớp, không phải là một con trỏ tới nó và bạn tránh phân bổ động và phân bổ động vì trình biên dịch sẽ làm điều đó cho bạn.
    [.__.] Thật ra đó cũng là cơ chế được sử dụng bởi "con trỏ thông minh" và được một số nhà văn khác gọi là RAII ;-).
  • Khi bạn chuyển các đối tượng cho các chức năng khác, hãy ưu tiên các tham số tham chiếu hơn các con trỏ. Điều này tránh một số lỗi có thể.
  • Khai báo các tham số const, khi có thể, đặc biệt là các con trỏ tới các đối tượng. Bằng cách đó, các đối tượng không thể được giải phóng "một cách tình cờ" (trừ khi bạn bỏ const;;))).
  • Giảm thiểu số lượng địa điểm trong chương trình nơi bạn thực hiện cấp phát và phân bổ bộ nhớ. Ví dụ. nếu bạn phân bổ hoặc miễn phí cùng loại nhiều lần, hãy viết hàm cho nó (hoặc phương thức xuất xưởng ;-)).
    [.___.] Bằng cách này, bạn có thể tạo đầu ra gỡ lỗi (địa chỉ được phân bổ và giải quyết, ...) một cách dễ dàng, nếu cần.
  • Sử dụng hàm nhà máy để phân bổ các đối tượng của một số lớp liên quan từ một hàm duy nhất.
  • Nếu các lớp của bạn có một lớp cơ sở chung với một hàm hủy ảo, bạn có thể giải phóng tất cả chúng bằng cách sử dụng cùng một hàm (hoặc phương thức tĩnh).
  • Kiểm tra chương trình của bạn với các công cụ như thanh lọc (không may là nhiều $/€/...).
1
mh.

Nếu bạn định quản lý bộ nhớ của mình theo cách thủ công, bạn có hai trường hợp:

  1. Tôi đã tạo đối tượng (có lẽ là gián tiếp, bằng cách gọi một hàm phân bổ một đối tượng mới), tôi sử dụng nó (hoặc một hàm tôi gọi sử dụng nó), sau đó tôi giải phóng nó.
  2. Ai đó đã cho tôi tham khảo, vì vậy tôi không nên giải phóng nó.

Nếu bạn cần phá vỡ bất kỳ quy tắc nào trong số này, vui lòng ghi lại nó.

Đó là tất cả về quyền sở hữu con trỏ.

1
Null303

valgrind (chỉ có sẵn cho các nền tảng * nix) là trình kiểm tra bộ nhớ rất Nice

1
Ronny Brendel

Những người khác đã đề cập đến các cách để tránh rò rỉ bộ nhớ ở nơi đầu tiên (như con trỏ thông minh). Nhưng một công cụ phân tích cấu hình và bộ nhớ thường là cách duy nhất để theo dõi các vấn đề về bộ nhớ một khi bạn có chúng.

Valgrind memcheck là một bản miễn phí tuyệt vời.

1
eli

Chỉ dành cho MSVC, thêm phần sau vào đầu mỗi tệp .cpp:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

Sau đó, khi gỡ lỗi với VS2003 trở lên, bạn sẽ được thông báo về bất kỳ rò rỉ nào khi chương trình của bạn thoát (nó theo dõi mới/xóa). Nó cơ bản, nhưng nó đã giúp tôi trong quá khứ.

1
Rob

C++ được thiết kế RAII trong tâm trí. Tôi thực sự không có cách nào tốt hơn để quản lý bộ nhớ trong C++. Nhưng hãy cẩn thận không phân bổ các khối rất lớn (như các đối tượng đệm) trên phạm vi cục bộ. Nó có thể gây ra lỗi tràn ngăn xếp và, nếu có một lỗ hổng trong giới hạn kiểm tra trong khi sử dụng đoạn đó, bạn có thể ghi đè lên các biến khác hoặc trả về địa chỉ, dẫn đến tất cả các loại lỗ hổng bảo mật.

0
artificialidiot

Một trong những ví dụ duy nhất về việc phân bổ và phá hủy ở những nơi khác nhau là tạo luồng (tham số bạn truyền). Nhưng ngay cả trong trường hợp này là dễ dàng. Đây là chức năng/phương thức tạo ra một chủ đề:

struct myparams {
int x;
std::vector<double> z;
}

std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...

Ở đây thay vì chức năng chủ đề

extern "C" void* th_func(void* p) {
   try {
       std::auto_ptr<myparams> param((myparams*)p);
       ...
   } catch(...) {
   }
   return 0;
}

Khá dễ phải không? Trong trường hợp tạo luồng không thành công, tài nguyên sẽ được tự động (xóa) bởi auto_ptr, nếu không quyền sở hữu sẽ được chuyển cho luồng. Điều gì xảy ra nếu luồng nhanh đến mức sau khi tạo, nó giải phóng tài nguyên trước

param.release();

được gọi trong hàm/phương thức chính? Không có gì! Bởi vì chúng tôi sẽ 'nói' auto_ptr bỏ qua giao dịch. Quản lý bộ nhớ C++ có dễ không? Chúc mừng

Ema!

0
Emanuele Oriani

Quản lý bộ nhớ giống như cách bạn quản lý các tài nguyên khác (xử lý, tệp, kết nối db, ổ cắm ...). GC cũng sẽ không giúp bạn với họ.

0
Nemanja Trifunovic

Bạn có thể chặn các chức năng cấp phát bộ nhớ và xem liệu có một số vùng bộ nhớ không được giải phóng khi thoát khỏi chương trình (mặc dù nó không phù hợp với tất cả các ứng dụng).

Nó cũng có thể được thực hiện tại thời điểm biên dịch bằng cách thay thế các toán tử mới và xóa và các chức năng cấp phát bộ nhớ khác.

Ví dụ: kiểm tra cái này trang web [Cấp phát bộ nhớ gỡ lỗi trong C++] Lưu ý: Có một mẹo để xóa toán tử cũng giống như thế này:

#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE

Bạn có thể lưu trữ trong một số biến tên của tệp và khi toán tử xóa bị quá tải sẽ biết đó là nơi mà nó được gọi từ đó. Bằng cách này bạn có thể có dấu vết của mỗi lần xóa và malloc từ chương trình của bạn. Ở cuối chuỗi kiểm tra bộ nhớ, bạn sẽ có thể báo cáo khối bộ nhớ được phân bổ không bị 'xóa' xác định nó bằng tên tệp và số dòng mà tôi đoán bạn muốn gì.

Bạn cũng có thể thử một cái gì đó như BoundChecker trong Visual Studio, điều này khá thú vị và dễ sử dụng.

0
INS

Chúng tôi bọc tất cả các chức năng phân bổ của mình bằng một lớp gắn thêm một chuỗi ngắn ở phía trước và một cờ canh ở cuối. Vì vậy, ví dụ bạn sẽ có một cuộc gọi đến "myalloc (pszSomeString, iSize, iAlocation) hoặc mới (" mô tả ", iSize) MyObject (); trong đó phân bổ nội bộ kích thước đã chỉ định cộng với đủ không gian cho tiêu đề và sentinel của bạn. , đừng quên bình luận điều này cho các bản dựng không gỡ lỗi! Cần thêm một chút bộ nhớ để làm điều này nhưng lợi ích vượt xa chi phí.

Điều này có ba lợi ích - đầu tiên, nó cho phép bạn dễ dàng và nhanh chóng theo dõi mã nào bị rò rỉ, bằng cách thực hiện tìm kiếm nhanh mã được phân bổ trong một số 'vùng' nhất định nhưng không được dọn sạch khi các vùng đó được giải phóng. Nó cũng có thể hữu ích để phát hiện khi một ranh giới đã được ghi đè bằng cách kiểm tra để đảm bảo tất cả các trọng điểm vẫn còn nguyên vẹn. Điều này đã giúp chúng tôi tiết kiệm rất nhiều lần khi cố gắng tìm ra những sự cố được giấu kỹ hoặc những sai lầm mảng. Lợi ích thứ ba là theo dõi việc sử dụng bộ nhớ để xem ai là người chơi lớn - ví dụ, một bản mô tả nhất định trong MemDump cho bạn biết khi nào 'âm thanh' chiếm nhiều không gian hơn bạn dự đoán, chẳng hạn.

0
screenglow