2010-02-03 25 views
7

Tôi đang viết một lớp mẫu lấy làm đầu vào một con trỏ và lưu trữ nó. Con trỏ có nghĩa là trỏ đến một đối tượng được phân bổ bởi một lớp khác, và giao cho lớp có chứa này.Một destructor - tôi nên sử dụng xóa hoặc xóa []?

Bây giờ tôi muốn tạo trình phá hủy cho vùng chứa này. Làm thế nào tôi nên giải phóng bộ nhớ được trỏ tới bởi con trỏ này? Tôi không có cách nào để biết một ưu tiên cho dù đó là một mảng hay một phần tử đơn lẻ.

Tôi sắp xếp mới cho C++, vì vậy hãy chịu đựng với tôi. Tôi đã luôn luôn sử dụng C, và Java là ngôn ngữ OO của tôi lựa chọn, nhưng giữa muốn tìm hiểu C + + và yêu cầu tốc độ của dự án của tôi, tôi đã đi với C + +.

Bạn nên thay đổi vùng chứa từ mẫu thành vùng chứa cho lớp trừu tượng có thể triển khai trình phá hủy của riêng nó?

+3

Câu trả lời của JonH là đúng, vì vậy có lẽ bạn nên cung cấp các mẫu: một cho mảng, một không. Câu trả lời khác là để tránh các mảng và thay vào đó mong đợi một cá thể đơn lẻ có thể hoặc không thể là một bộ sưu tập thích hợp dọn dẹp sau chính nó, chẳng hạn như vectơ <>. –

+1

@Steven Sudit: Tôi nghĩ bạn nên đưa ra nhận xét đó một câu trả lời. –

+0

Bạn cũng có thể có vấn đề về luồng với cách tiếp cận này - bạn không thể xóa nội dung nào đó được phân bổ trong một chuỗi khác. – ChrisF

Trả lời

6

Nếu bạn không biết liệu nó có được phân bổ với new hoặc new[] thì không an toàn để xóa nó.

Mã của bạn có thể hoạt động. Ví dụ, trên một nền tảng tôi làm việc trên, sự khác biệt chỉ quan trọng khi bạn có một mảng các đối tượng có destructors. Vì vậy, bạn làm như sau:

// by luck, this works on my preferred platform 
// don't do this - just an example of why your code seems to work 
int *ints = new int[20]; 
delete ints; 

nhưng sau đó bạn làm như sau:

// crashes on my platform 
std::string *strings = new std::string[10]; 
delete strings; 
+1

Nó không may mắn, đó là 'int' không có destructor. –

+5

@Steven: Thật may mắn. Các kết quả của 'new' và' delete' không khớp với hành vi, khoảng thời gian không xác định. Không được xác định ngay cả khi một loại có một destructor tầm thường. Hoàn toàn hợp lý để tôi thay thế 'toán tử new' và' toán tử delete' thành phù hợp; và tôi có thể mong đợi các con trỏ trong hàm 'delete' của tôi đã được xuất ra bởi' new' tương ứng. – GManNickG

+3

@Steven - thật may mắn vì nền tảng * chọn * để tối ưu hóa phân bổ mảng khi loại không có destructor. Nhưng một nền tảng khác có thể xử lý điều này một cách khác nhau và mã đó sau đó sẽ phá vỡ trên nền tảng khác này. –

5

Câu trả lời ngắn:

Nếu bạn sử dụng [] với mới mà bạn muốn sử dụng [] với xóa.

//allocate some memory 
myObject* m = new myObject[100]; 

//later on...destructor... 
delete m; //wrong 
delete[] m; //correct 

Đó là xương trần, điều khác bạn có thể xem là boost. Cũng khá khó khăn để trả lời xem xét bạn không chắc chắn nếu một mảng của nó hoặc đối tượng duy nhất. Bạn có thể kiểm tra điều này mặc dù thông qua một lá cờ nói cho ứng dụng của bạn có nên sử dụng xóa hoặc xóa [].

+2

Không thực sự trả lời câu hỏi của anh ta. Có lẽ anh ta hiểu rằng điều quan trọng là sử dụng đúng chức năng 'xóa', hoặc anh ta sẽ không hỏi câu hỏi của anh ta. : P – GManNickG

+0

@GMan Tôi sẽ chỉnh sửa để phù hợp với nhu cầu :). – JonH

+0

tăng dường như giải quyết mọi thứ ... đôi khi bạn có suy nghĩ đó? :) – JonH

0

Một con trỏ thông minh như thúc đẩy shared_pointer đã có này được bảo hiểm, bạn có thể sử dụng nó? linky

2

Như một quy luật phát triển chung, bạn nên dính vào một thiết kế nơi lớp trong đó kêu gọi new cũng nên gọi delete

+4

Không thực sự, điều này hoàn toàn trái ngược với hầu hết các thư viện con trỏ thông minh hiện tại: 'std :: auto_ptr ap (new int()); boost :: shared_ptr sp (new int()); 'Cả hai cuộc gọi xóa cho bạn. –

6

Bạn phải lập hồ sơ như thế nào lớp này dự kiến ​​sẽ được sử dụng, và luôn luôn bố trí như mong đợi. Bạn cũng có thể truyền một lá cờ cho đối tượng xác định nó sẽ hủy như thế nào. Ngoài ra, hãy xem số tăng cường của smart pointers, có thể xử lý sự khác biệt này cho bạn.

+0

Điều này về cơ bản là chính xác. Nếu bạn đang chuyển một con trỏ vào, phải có A: chỉ có một cách "đúng" để xóa nó hoặc B: Biến "cờ" khác cho biết cách xóa nó hoặc C: Một deleter tùy chỉnh, là có lẽ cách vượt ra ngoài sự hiểu biết của người mới bắt đầu. Đi với "B" trong trường hợp của bạn IMO. –

+0

@Kevin: Chỉ về cơ bản? Tôi đã bỏ lỡ bất cứ điều gì? Các tùy chọn tôi đi với không phải là một trong những bạn đề cập: có được con trỏ vào một con trỏ thông minh thích hợp càng sớm càng tốt, và để cho con trỏ thông minh xử lý biết làm thế nào/nếu/khi để xóa. Những điều cơ bản của * sử dụng * auto_ptr hoặc bất kỳ con trỏ thông minh nào không phải là rất khó (hiểu tất cả các tùy chỉnh và đặc biệt là các chi tiết triển khai thực hiện). –

+3

Điều gì đã làm cho downvote? –

2

Bạn không nên xóa nó. Nếu lớp của bạn có một con trỏ đã được khởi tạo, nó không an toàn để xóa nó. Nó thậm chí có thể không trỏ đến một đối tượng trên heap; gọi số delete hoặc delete[] có thể là thảm họa.

Việc phân bổ và deallocation bộ nhớ sẽ xảy ra trong cùng một phạm vi. Mã nào sở hữu và khởi tạo phiên bản của lớp của bạn cũng có thể chịu trách nhiệm khởi tạo và chuyển vào con trỏ và rằng là nơi bạn cần delete.

+3

Vì vậy, tôi không thể sử dụng 'auto_ptr' /' unique_ptr' để chuyển quyền sở hữu? – GManNickG

+0

@GMan Có những trường hợp ngoại lệ rõ ràng khi lớp bạn truyền qua con trỏ vào là * được thiết kế * để sở hữu nó ... – meagar

1

Vì con trỏ trong C++ không cho chúng tôi biết cách phân bổ nó, có, không có cách nào để quyết định phương thức phân bổ nào sẽ sử dụng.Giải pháp là đưa ra lựa chọn cho người dùng hy vọng biết được bộ nhớ được phân bổ như thế nào. Hãy xem thư viện Boost smart ptr, đặc biệt là tại shared_ptr hàm tạo với thông số thứ hai, để có ví dụ tuyệt vời.

+0

Đưa ra lựa chọn cho người dùng? Điều đó không có ý nghĩa nhiều? – JonH

+0

@JonH, với * người dùng của vùng chứa *, tức là mã sử dụng nó, không phải là người dùng * ở bàn phím * :) –

+0

ahh Tôi đã gặp bạn, đã bị nhầm lẫn trong một giây. – JonH

2
  • Sử dụng delete nếu bạn đã phân bổ với new.
  • Sử dụng delete[] nếu bạn đã phân bổ với new[].

Sau khi các báo cáo, nếu bạn vẫn còn có một vấn đề (có thể bạn muốn xóa một đối tượng được tạo ra bởi người khác), sau đó bạn đang phá vỡ các quy tắc thứ ba:

  • Luôn xóa những gì bạn tạo. Hệ quả, không bao giờ xóa những gì bạn không tạo ra.
+0

Quy tắc thứ ba đó cần được giải quyết nhân dịp, mặc dù tôi đồng ý với quy tắc này nói chung. –

+0

Hệ quả của quy tắc thứ ba là 'không bao giờ tạo ra con trỏ thông minh' :-) –

+0

Thực tế là không. Nếu bạn tạo một đối tượng, lưu trữ nó trong một con trỏ thông minh và đảm bảo rằng chỉ con trỏ thông minh mới được xuất bản ra thế giới bên ngoài, thì bạn phải chịu trách nhiệm tạo và xóa đối tượng ở cùng một vị trí. việc xóa không được thực hiện bởi chính bạn. –

2

(Moving nhận xét của tôi vào một câu trả lời, theo yêu cầu.)

câu trả lời Jonh là đúng (về việc sử dụng mảng phá hủy chỉ khi bạn sử dụng xây dựng mảng), vì vậy có lẽ bạn nên cung cấp các mẫu: một cho mảng, một không.

Câu trả lời khác là tránh các mảng và thay vào đó mong đợi một cá thể có thể hoặc không thể là một bộ sưu tập thích hợp tự dọn dẹp sau chính nó, chẳng hạn như vector <>.

chỉnh sửa

Trộm cắp trắng trợn từ Roger Pate, tôi sẽ thêm vào đó bạn có thể yêu cầu sử dụng một con trỏ thông minh, mà số tiền để một bộ sưu tập đơn hàng.

0

Đặt một cách đơn giản, chỉ được cung cấp một con trỏ tới bộ nhớ được cấp phát động không có cách nào xác định cách phân bổ nó một cách an toàn. Con trỏ có thể đã được phân bổ trong bất kỳ các cách sau:

  • sử dụng mới
  • sử dụng mới []
  • sử dụng malloc
  • sử dụng người dùng xác định chức năng
  • , vv

Trong mọi trường hợp trước khi bạn có thể giải quyết bộ nhớ, bạn phải biết cách phân bổ bộ nhớ.

+0

Về cơ bản bạn sẽ quyết định xem lớp của bạn có đại diện cho smart_ptr hay smart_array và để nó cho người gọi để cung cấp một con trỏ được phân bổ phù hợp cho lớp của bạn. – UncleBens

+0

@UncleBens Không chắc chắn những gì bình luận của bạn có nghĩa là WRT câu trả lời của tôi - mặc dù tên của nó là một "con trỏ thông minh" không phải là một con trỏ. –

+0

Tôi muốn nói rằng nếu tất cả những gì bạn có là một con trỏ, thì bạn không thể nói nó được phân bổ như thế nào. Tôi không nghĩ nó kết thúc ở đó. Bạn chỉ cần nói, tôi có một lớp ở đây sẽ gọi hàm 'delete' /' delete [] '/' free() '/ user-defined deallocation, và tùy thuộc vào người dùng trong lớp của bạn để cung cấp một cách phù hợp con trỏ được cấp phát. Về cơ bản bạn làm một cái gì đó có ý nghĩa, và phần còn lại không phải là vấn đề của bạn. – UncleBens

2

Nếu bạn có một lớp học có con trỏ nó sẽ giả định quyền sở hữu, thì hợp đồng sử dụng lớp học cần bao gồm một trong hai điều. Hoặc:

  • giao diện cần cho biết cách đối tượng trỏ trỏ được phân bổ để chủ sở hữu mới có thể biết cách xử lý đối tượng một cách an toàn. Tùy chọn này có lợi thế là giữ mọi thứ đơn giản (trên một cấp nào đó), nhưng nó không linh hoạt - lớp không thể xử lý việc sở hữu các đối tượng tĩnh cũng như các đối tượng được phân bổ động.

hoặc

  • giao diện cần bao gồm một cơ chế mà một chính sách deallocation có thể được xác định bởi bất cứ điều gì được cho con trỏ đến lớp. Điều này có thể đơn giản như việc cung cấp một cơ chế truyền vào một hàm con (hoặc thậm chí một con trỏ hàm cũ) sẽ được gọi để deallocate đối tượng (tốt nhất là trong cùng một hàm/hàm tạo đi qua chính con trỏ). Điều này làm cho lớp được cho là phức tạp hơn để sử dụng (nhưng có một chính sách mặc định gọi delete trên con trỏ, ví dụ, có thể làm cho nó dễ sử dụng như tùy chọn 1 cho phần lớn các công dụng). Bây giờ nếu ai đó muốn cung cấp cho lớp một con trỏ tới một đối tượng được phân bổ tĩnh, chúng có thể truyền vào một hàm functor không-op, vì vậy không có gì xảy ra khi lớp muốn deallocates nó, hoặc một functor đến một hoạt động delete[] nếu đối tượng được phân bổ bởi new[] , v.v.
+0

+1 Đây là câu trả lời đúng. Tôi ngạc nhiên là nó không được bình chọn. –

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