2008-09-19 22 views

Trả lời

53

Sử dụng ngăn xếp khi biến của bạn sẽ không được sử dụng sau khi hàm hiện tại trả về. Sử dụng vùng heap khi dữ liệu trong biến là cần thiết vượt quá tuổi thọ của hàm hiện tại.

+3

Có nhiều cách để giải quyết vấn đề đó. Việc chuyển một bộ đệm tới một hàm mà sau đó ghi dữ liệu vào nó là một cách hay để có một hàm "trả về" dữ liệu động đang sống trong khung ngăn xếp thấp hơn. Nó ít giống OO hơn, nhưng nó hiệu quả hơn nhiều. –

+6

Kích thước cũng là một xem xét: bất cứ điều gì trên 1K trên ngăn xếp nên được xem xét cẩn thận. Đôi khi tốt hơn để có một con trỏ ngăn xếp để bộ nhớ heap (cùng với 'Resource Acquisition là Initialization' thành ngữ) – Arkadiy

+1

Nhưng những gì về khi bộ nhớ là một tài sản của một lớp học, làm thế nào để bạn quyết định khi một tài sản lớp phải là một con trỏ hay cách khác? Ngoài ra khi nào bạn có thể sử dụng một con trỏ thông minh? – JagWire

3

Sử dụng vùng đệm để chỉ phân bổ không gian cho các đối tượng trong thời gian chạy. Nếu bạn biết kích thước tại thời gian biên dịch, hãy sử dụng ngăn xếp. Thay vì trả về các đối tượng phân bổ heap từ một hàm, chuyển một bộ đệm vào hàm để nó ghi vào. Bằng cách đó, bộ đệm có thể được cấp phát nơi mà hàm được gọi là một mảng hoặc cấu trúc dựa trên ngăn xếp khác.

Ít báo cáo malloc() bạn có, ít cơ hội bị rò rỉ bộ nhớ hơn.

0

Câu hỏi bị hình thành. Có những tình huống bạn cần ngăn xếp, những nơi khác bạn cần đống, những nơi khác bạn cần bộ nhớ tĩnh, những nơi khác bạn cần dữ liệu bộ nhớ const, những nơi khác bạn cần lưu trữ miễn phí.

Ngăn xếp nhanh, vì phân bổ chỉ là "gia tăng" trên SP và tất cả "phân bổ" được thực hiện tại thời điểm yêu cầu của hàm bạn đang ở. Phân bổ lưu trữ/phân bổ Heap (hoặc lưu trữ miễn phí) là nhiều thời gian hơn đắt tiền và dễ bị lỗi.

28

Theo quy tắc chung, tránh tạo các đối tượng lớn trên ngăn xếp.

  • Tạo đối tượng trên ngăn xếp giải phóng bạn khỏi gánh nặng ghi nhớ để dọn dẹp (đọc xóa) đối tượng. Nhưng việc tạo quá nhiều đối tượng trên ngăn xếp sẽ làm tăng nguy cơ tràn ngăn xếp.
  • Nếu bạn sử dụng heap cho đối tượng, bạn nhận được bộ nhớ nhiều mà hệ điều hành có thể cung cấp, lớn hơn nhiều so với chồng, nhưng sau đó lại phải đảm bảo giải phóng bộ nhớ khi bạn hoàn thành. Ngoài ra, việc tạo quá nhiều đối tượng quá thường xuyên trong vùng heap sẽ có xu hướng phân mảnh bộ nhớ, do đó sẽ ảnh hưởng đến hiệu suất của ứng dụng của bạn.
12

Sử dụng ngăn xếp khi bộ nhớ đang được sử dụng được giới hạn nghiêm ngặt đối với phạm vi bạn đang tạo. Điều này rất hữu ích để tránh rò rỉ bộ nhớ vì bạn biết chính xác nơi bạn muốn sử dụng bộ nhớ, và bạn biết khi nào bạn không còn cần đến bộ nhớ nữa, do đó bộ nhớ sẽ được dọn dẹp cho bạn.

int main() 
{ 
    if (...) 
    { 
     int i = 0; 
    } 
    // I know that i is no longer needed here, so declaring i in the above block 
    // limits the scope appropriately 
} 

Vùng heap hữu ích khi bộ nhớ của bạn có thể được truy cập ngoài phạm vi tạo và bạn không muốn sao chép biến ngăn xếp. Điều này có thể cung cấp cho bạn kiểm soát rõ ràng về cách bộ nhớ được phân bổ và deallocated.

Object* CreateObject(); 

int main() 
{ 
    Object* obj = CreateObject(); 
    // I can continue to manipulate object and I decide when I'm done with it 

    // .. 
    // I'm done 
    delete obj; 
    // .. keep going if you wish 
    return 0; 
} 

Object* CreateObject() 
{ 
    Object* returnValue = new Object(); 
    // ... do a bunch of stuff to returnValue 
    return returnValue; 
    // Note the object created via new here doesn't go away, its passed back using 
    // a pointer 
} 

Rõ ràng là một vấn đề phổ biến ở đây là bạn có thể quên xóa đối tượng của mình. Điều này được gọi là rò rỉ bộ nhớ. Những vấn đề này phổ biến hơn khi chương trình của bạn trở nên ít hơn và ít tầm thường hơn khi "quyền sở hữu" (hoặc người chính xác chịu trách nhiệm xóa mọi thứ) trở nên khó xác định hơn.

Các giải pháp phổ biến trong nhiều ngôn ngữ được quản lý hơn (C#, Java) là triển khai bộ sưu tập rác để bạn không phải suy nghĩ về việc xóa mọi thứ.Tuy nhiên, điều này có nghĩa là có gì đó trong nền chạy theo định kỳ để kiểm tra dữ liệu heap của bạn. Trong một chương trình không tầm thường, điều này có thể trở nên khá kém hiệu quả khi luồng "thu gom rác" bật lên và chuồn đi, tìm kiếm dữ liệu sẽ bị xóa, trong khi phần còn lại của chương trình của bạn bị chặn thực thi.

Trong C++, giải pháp phổ biến nhất và tốt nhất (theo ý kiến ​​của tôi) để xử lý rò rỉ bộ nhớ là sử dụng con trỏ thông minh. Phổ biến nhất trong số này là boost::shared_ptr là (reference counted)

Vì vậy, để tạo lại ví dụ trên tăng :: shared_ptr CreateObject();

int main() 
{ 
    boost::shared_ptr<Object> obj = CreateObject(); 
    // I can continue to manipulate object and I decide when I'm done with it 

    // .. 
    // I'm done, manually delete 
    obj.reset(NULL); 
    // .. keep going if you wish 
    // here, if you forget to delete obj, the shared_ptr's destructor will note 
    // that if no other shared_ptr's point to this memory 
    // it will automatically get deleted. 
    return 0; 
} 

boost::shared_ptr<Object> CreateObject() 
{ 
    boost::shared_ptr<Object> returnValue(new Object()); 
    // ... do a bunch of stuff to returnValue 
    return returnValue; 
    // Note the object created via new here doesn't go away, its passed back to 
    // the receiving shared_ptr, shared_ptr knows that another reference exists 
    // to this memory, so it shouldn't delete the memory 
} 
+0

Và sau đó có di chuyển-ngữ nghĩa –

4

làm quy tắc sử dụng ngón tay cái bất cứ khi nào bạn có thể. tức là khi biến không bao giờ cần ngoài phạm vi đó.

nhanh hơn, ít bị phân mảnh hơn và sẽ tránh các chi phí khác liên quan đến việc gọi điện thoại malloc hoặc mới. phân bổ tắt của ngăn xếp là một vài hoạt động lắp ráp, malloc hoặc mới là hàng trăm dòng mã trong một thực hiện hiệu quả.

không bao giờ tốt nhất để sử dụng heap ... không thể tránh khỏi. :)

+0

Nó tốt hơn so với một vài hoạt động lắp ráp - nó chỉ là một cộng hoặc trừ (tùy thuộc vào hướng chồng của bạn phát triển). – Eclipse

+0

cộng và trừ không phải lúc nào cũng là đơn lẻ ... mà còn xem xét việc dọn dẹp ở đầu bên kia. tùy thuộc vào quy ước cuộc gọi sẽ có một phụ/thêm để phù hợp với add/sub mặc dù tất cả những điều này có thể được kết hợp tùy thuộc vào cách bạn sử dụng stack và những gì tối ưu hóa trình biên dịch thực hiện (nó thực sự có thể đun sôi xuống để hướng dẫn bằng không. .. hoặc trong trường hợp rất đặc biệt, chỉ dẫn trừ đi) – jheriko

8

Một ngoại lệ cho quy tắc nêu trên mà bạn thường nên sử dụng ngăn xếp cho các biến địa phương mà không cần thiết bên ngoài phạm vi của hàm:

chức năng đệ quy có thể làm cạn kiệt không gian ngăn xếp nếu họ phân bổ lớn địa phương biến hoặc nếu chúng được gọi đệ quy nhiều lần. Nếu bạn có một hàm đệ quy sử dụng bộ nhớ, có thể là một ý tưởng tốt để sử dụng bộ nhớ dựa trên heap thay vì bộ nhớ dựa trên ngăn xếp.

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