2012-07-13 52 views
12

Ok, vì vậy tôi rất mới trong lập trình C++, và tôi đã tìm kiếm một vài ngày để có câu trả lời quyết định cho điều này. KHI tôi nên khai báo các biến thành viên trên heap so với stack? Hầu hết các câu trả lời mà tôi đã tìm thấy đã giải quyết được các vấn đề khác, nhưng tôi muốn biết khi nào tốt nhất nên sử dụng heap cho các biến thành viên và tại sao tốt hơn là nên gộp các thành viên thay vì xếp chúng.Khi nào và tại sao phải khai báo biến thành viên trên heap C++

+1

* biến thành viên * tham chiếu đến các thành viên * lớp *, và như vậy đi đến nơi * thể hiện * của lớp học. – CapelliC

Trả lời

18

Có hai khái niệm quan trọng để nắm bắt đầu tiên:

  1. Một nên tránh suy nghĩ về "đống" và "chồng". Đó là những chi tiết thực hiện của trình biên dịch/nền tảng của bạn, không phải ngôn ngữ. Thay vào đó, hãy suy nghĩ về thời gian sống của đối tượng đối tượng: tuổi thọ của đối tượng có tương ứng với thời gian của "cha mẹ" hay không? Nếu bạn cần thứ hai, sau đó bạn sẽ cần phải sử dụng new (trực tiếp hoặc gián tiếp) để phân bổ động một đối tượng.

  2. Biến thành viên luôn là có cùng thời gian với cha mẹ. Biến thành viên có thể là một con trỏ, và đối tượng mà nó trỏ tới có thể có một vòng đời độc lập. Nhưng đối tượng được trỏ đến không phải là biến thành viên.

Tuy nhiên, không có câu trả lời chung cho câu hỏi của bạn. Nói một cách thô lỗ, không phân bổ động trừ khi có lý do chính đáng. Như tôi đã nói ở trên, những lý do này thường tương ứng với các tình huống mà tuổi thọ cần khác với "cha mẹ" của nó.


1. Thật vậy, tiêu chuẩn C++ không thực sự nói về "đống" và "ngăn xếp". Chúng quan trọng cần xem xét khi tối ưu hóa hoặc thường suy nghĩ về hiệu suất, nhưng chúng hầu như không liên quan đến quan điểm chức năng chương trình.

+0

"thường tương ứng với các tình huống mà tuổi thọ cần khác với tình huống của cha mẹ": chúng ta có thể nói về điều gì đó với tư cách là thành viên không? –

+0

@ JamesKanze: Thật vậy, tôi nên làm rõ điều đó. –

+1

Các đối tượng cũng yêu cầu tuổi thọ động nếu chúng có kích thước động hoặc để truyền một đối tượng đến một lớp cơ sở ảo. –

1

Ngăn xếp dùng để chỉ call stack. Các cuộc gọi hàm, địa chỉ trả về, các tham số và các biến cục bộ được giữ trong ngăn xếp cuộc gọi. Bạn sử dụng bộ nhớ ngăn xếp bất cứ khi nào bạn vượt qua một tham số hoặc tạo một biến cục bộ. Ngăn xếp chỉ lưu trữ tạm thời. Khi hàm hiện tại vượt quá phạm vi, bạn không còn có quyền truy cập vào bất kỳ biến nào cho tham số.

Heap là một bộ nhớ lớn được sử dụng để phân bổ động. Khi bạn sử dụng toán tử new để cấp phát bộ nhớ, bộ nhớ này được gán từ heap. Bạn muốn cấp phát bộ nhớ heap khi bạn đang tạo các đối tượng mà bạn không muốn mất sau khi hàm hiện tại kết thúc (mất phạm vi). Các đối tượng được lưu trữ trong heap cho đến khi không gian được deallocated với delete hoặc free().

+0

Các đối tượng cũng yêu cầu tuổi thọ động nếu chúng có kích thước động hoặc để truyền một đối tượng đến một lớp cơ sở ảo. –

4

Biến thành viên là thành viên của chính lớp đó. Họ không phải là trên heap cũng không trên stack, hay đúng hơn, họ là nơi mà các lớp học chính nó là.

Có rất ít lý do để thêm mức độ gián đoạn và phân bổ thành viên riêng biệt trên heap: đa hình (nếu loại thành viên không phải lúc nào cũng giống nhau) là phổ biến nhất.

+0

Các đối tượng cũng yêu cầu tuổi thọ động nếu chúng có kích thước động hoặc để đúc một đối tượng vào một lớp cơ sở ảo. –

+0

@MooingDuck Đối tượng có kích thước động phải được quản lý theo lớp của chính chúng (ví dụ: 'std :: vector'): bạn không phân bổ _them_ làm thành viên; bạn làm cho container thành viên. Và đúc thành một lớp cơ sở ảo hoặc bất cứ điều gì ngụ ý rằng thành viên là đa hình, đó là những gì tôi đề cập đến. –

+0

Alright, đủ công bằng –

2

Để nhận một số thuật ngữ thẳng: Điều bạn gọi là heapstack mô tả thời gian tồn tại của các đối tượng.Điều đầu tiên có nghĩa là tuổi thọ là dynamic, số thứ hai automatic và số thứ ba (mà bạn không đề cập đến) là static.

Thông thường, bạn sẽ cần dynamic thời gian tồn tại của một đối tượng khi nó vượt quá phạm vi được tạo. Một trường hợp phổ biến khác là khi bạn muốn chia sẻ nó trên các đối tượng cha mẹ khác nhau. Ngoài ra, tuổi thọ động cũng cần thiết khi bạn làm việc với một thiết kế có hướng đối tượng nặng (sử dụng nhiều tính đa hình, không sử dụng các giá trị), ví dụ: Qt.

Thành ngữ yêu cầu thời gian sống động là thành ngữ pimpl.

Hầu hết các thư viện lập trình chung tập trung hơn vào giá trị và ngữ nghĩa giá trị, vì vậy bạn sẽ không sử dụng ràng buộc động nhiều và thời gian sống tự động trở nên phổ biến hơn nhiều.

Ngoài ra còn có một số ví dụ nơi phân bổ động là cần thiết vì lý do cụ thể thực hiện hơn:

  • đối tượng tự động kích thước (container)
  • xử lý loại không đầy đủ (xem pimpl-ngữ)
  • nullability dễ dàng một loại

Tất cả chỉ là hướng dẫn chung và phải được quyết định theo từng trường hợp. Nói chung, thích các đối tượng tự động trên các đối tượng động.

0

Hãy xem xét ví dụ này:

Bạn triển khai danh sách được liên kết có đầu thành viên trường của nút lớp.
Mỗi nút có một thành viên trường next. Nếu thành viên này thuộc loại Node và không phải Node *, kích thước của mỗi nút sẽ phụ thuộc vào số lượng các nút sau khi nó trong chuỗi.

Ví dụ: nếu bạn có 100 nút trong danh sách, thì thành viên đầu của bạn sẽ rất lớn. Bởi vì nó giữ nút tiếp theo bên trong chính nó vì vậy nó cần phải có đủ kích thước để giữ nó và tiếp theo giữ tiếp theo và như vậy. Vì vậy, đầu phải có đủ không gian để giữ trong nó 99 nút 98 tiếp theo và như vậy ...

Bạn muốn tránh điều đó vì vậy trong trường hợp này, tốt hơn là nên trỏ đến nút tiếp theo trong mỗi Nút thay vì chính nút tiếp theo.

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