2013-03-31 33 views
16

Trong C++ Làm thế nào để quyết định hoặc biết nếu một con trỏ đã bị xóa trước khi ??Làm thế nào để phát hiện nếu con trỏ bị xóa và xóa an toàn?

khi tôi cố xóa một con trỏ đã bị xóa trước đó trong một phần khác của mã, nó đã ném một ngoại lệ không thể xử lý được.

Tôi đã tự hỏi liệu có cách nào để kiểm tra hoặc thử xóa con trỏ không? bất kỳ tham chiếu nào về các hoạt động bộ nhớ nâng cao.

tôi cũng muốn nắm vững ngoại lệ chưa xử lý của con trỏ và quyền truy cập vào bảo vệ hoặc quyền truy cập là vi phạm, ... loại lỗi này.

nhờ cho những người đưa ra một số kiến ​​thức của họ và thời gian của mình để giúp đỡ người khác và chia sẻ benfits của họ


Cập nhật

Lời khuyên lớn từ rất nhiều c hiện đại ++ phát triển cộng đồng là - Sử dụng con trỏ thông minh hoặc cố gắng tránh sử dụng con trỏ thô. Nhưng đối với việc ném an toàn và bảo vệ bộ nhớ (ISO_CPP_FAQ) và dĩ nhiên nếu bạn muốn tránh chi phí nhỏ sử dụng con trỏ thông minh [có thể không đáng chú ý nhưng chúng có phí] bạn có thể viết các phương thức tùy chỉnh xử lý con trỏ thô [ loại *] - đây không phải là chung. Luôn ưu tiên con trỏ thông minh đến các con trỏ thô.

Trong 'Going Native 2013', một lời khuyên chung được đưa ra là - Không bao giờ sử dụng con trỏ thô.

+1

Sử dụng con trỏ thông minh ('std :: shared_ptr' và' std :: weak_ptr') –

+0

Cảm ơn rất nhiều vì lời khuyên, nhưng điều này sẽ giải quyết được vấn đề từ gốc, không có vấn đề gì từ phía con trỏ sẽ xuất hiện nếu tôi sử dụng con trỏ thông minh? – ahmedsafan86

+0

a) Bạn không xóa con trỏ, bạn xóa * đối tượng *. b) Sử dụng con trỏ không hợp lệ không ném ngoại lệ; thay vào đó, nó là * hành vi không xác định *. –

Trả lời

24

Có thể có ba giải pháp. Bạn có thể muốn chọn một tùy thuộc vào nỗ lực tỷ lệ chất lượng/bạn muốn đạt được:

Elegant và chính xác nhất giải pháp:

Sử dụng smart pointers và bạn không phải tự gọi delete bao giờ trở lại. Đây là cách tốt nhất có thể để khắc phục vấn đề này. Nó sử dụng nguyên tắc của RAII hoạt động hoàn hảo cho một ngôn ngữ như C++ mà không có bộ thu gom rác bên trong.

Ít thanh lịch nhưng hoàn toàn khả thi giải pháp:

Gán con trỏ để NULL sau khi xóa. Gọi số delete trên con trỏ NULL là số không để nó loại bỏ sự cần thiết phải kiểm tra thêm NULL nhưng điều này có thể ẩn một số vấn đề thay vì hiển thị chúng.

Ít thanh lịch nhưng chính xác hơn giải pháp:

Hunt xuống tất cả các nhiều delete vấn đề bằng cách cho phép sụp đổ chương trình của bạn. Bạn cũng có thể sử dụng các chương trình phân tích bộ nhớ như valgrind và sau đó sửa mã của bạn để tránh tất cả những vấn đề này.

+6

Không có giải pháp nào bạn có thực sự là chung chung. Con trỏ thông minh chỉ hoạt động là các tình huống đặc biệt. Đặt con trỏ bạn xóa thành null không giúp được nhiều, vì nó không ảnh hưởng đến các con trỏ khác, và tìm ra vấn đề sau khi thực tế là rất, rất khó. Giải pháp thực sự duy nhất là thiết kế, lên phía trước, sao cho tuổi thọ của (rất ít) đối tượng được cấp động được xác định. –

+2

@ JamesKanze: Đúng là một ứng dụng được thiết kế tốt sẽ hiếm khi gặp phải các vấn đề như vậy và luôn phải thiết kế để xác định và hạn chế thời gian sống của đối tượng. Thực tế là sự thật cay đắng khác là các ứng dụng được thiết kế đúng chỉ tồn tại trong một thế giới lý tưởng, trong khi hầu hết chúng ta phải làm việc với các ứng dụng đã có những vấn đề này. Bạn không tạo ra chúng mà thừa hưởng chúng. –

+0

Nếu ứng dụng không được thiết kế đúng cách, ứng dụng sẽ không hoạt động, bất kể bạn làm gì sau khi thực tế. –

4

Đây là một câu hỏi hay, nhưng một trong những chân lý cơ bản của làm việc trong một môi trường bằng tay bộ nhớ được quản lý (như C/C++ và người anh em họ của nó) là không có cách nào tốt nhìn vào một con trỏ sau khi thực tế và hỏi liệu nó có hợp lệ không - một khi nó trở thành không hợp lệ, nó biến mất, và nhìn nó dễ bị thổi phồng lên. Công việc của bạn là đảm bảo rằng nó không bao giờ bị xóa hoặc giải phóng nhiều lần, và không bao giờ được truy cập sau thời gian đó.

Chắc chắn xem các con trỏ thông minh, được phát minh để làm cho cuộc sống của người lập trình dễ dàng hơn trong những trường hợp này. (Phương pháp truyền thống hơn là cẩn thận, không vặn nó lên, và sau đó có thể gán NULL cho con trỏ khi bạn biết nó đã bị xóa, như Alok nói.)

+0

Điều này không phải là duy nhất trong C + +. Trên toàn cầu, Java cũng không cung cấp cho bạn giải pháp tự động --- đó là bộ sưu tập rác thải cho phép bạn thực hiện một giải pháp, nhưng bạn vẫn phải tự thực hiện nó. (Và tất nhiên, nếu bạn đang làm bất cứ điều gì quan trọng trong C++, bạn sẽ sử dụng bộ sưu tập rác, chính xác để bạn có thể phát hiện một cách đáng tin cậy khi bạn đang truy cập một đối tượng đã bị xóa.) –

1

sử dụng shared_ptr<>shared_array<>, nhớ shared_ptr<> thể được sử dụng để quản lý bộ nhớ được phân bổ cho một mảng chỉ nếu thích hợp Deleter được cung cấp, nếu không sử dụng shared_array<> để quản lý các mảng của bạn

A* a_tab=new A[100]; 
boost::shared_ptr<A> a_tab_ok(a_tab,ArrayDeleter<A>()); 

// chỉ ok nếu

template <typename T> 
    class ArrayDeleter 
    { 
    public: 
     void operator() (T* d) const 
     { 
      delete [] d; //will delete array! 
     } 
    }; 

được cung cấp

+0

người ta có thể nghĩ rằng shared_ptr <> theo mặc định là hợp lệ cũng cho mảng: hãy cẩn thận mặc dù – 4pie0

1

Con trỏ thông minh là lựa chọn tốt hơn để tránh những vấn đề như vậy (nhưng bạn phải có sự hiểu biết đầy đủ trước khi sử dụng chúng), nhưng tôi muốn đề cập đến các giới hạn hiệu suất liên quan đến con trỏ Thông minh, lý do là chúng thường sử dụng các hoạt động nguyên tử chẳng hạn như InterlockedIncrement trong Win32 API để đếm tham chiếu. Các hàm này chậm hơn đáng kể so với số học số nguyên đơn giản. Tôi không chắc chắn rằng hình phạt hiệu suất nhỏ như vậy chấp nhận được trong trường hợp của bạn hay không.

Những gì tôi thường làm là (vì vậy tôi không phải mất nhiều ngày để gỡ lỗi các lỗi khó chịu), tôi dành nhiều thời gian cho thiết kế và thời gian của đối tượng, trước khi chuyển sang thực tế mã hóa, như tôi xóa bộ nhớ cụ thể thiết lập con trỏ để NULL, nó là thực hành tốt như xa như tôi nghĩ. Một lần nữa có lẽ giải pháp thực sự là dành nhiều thời gian hơn để xác định sự phụ thuộc và thời gian sống của đối tượng trước khi tiếp tục!

+1

Con trỏ thông minh không nhất thiết phải giúp đỡ, và thậm chí chúng có thể làm trầm trọng thêm tình hình. –

+0

@Saqlain: InterlockedIncrement là một hoạt động nguyên tử và nó không có hiệu ứng đáng chú ý về hiệu suất, nếu chỉ sử dụng các con trỏ đơn giản ở mức thấp sẽ làm điều đó, nhưng vấn đề khiến bạn luôn gắn bó với các con trỏ thông minh không có bảo đảm ngoại lệ ném. – ahmedsafan86

3

Trong C++ Cách quyết định hoặc biết con trỏ đã bị xóa trước đó chưa ??

Chuẩn ngôn ngữ không cung cấp bất kỳ cách pháp lý nào để xác định xem con trỏ tùy ý có hợp lệ hay không.

Có một cách, nhưng đó là trình biên dịch cao/hệ điều hành cụ thể. Bạn có thể móc vào trình quản lý bộ nhớ hiện có hoặc thay thế nó bằng chính bộ nhớ của bạn và cung cấp một hàm chuyên dụng để xác nhận con trỏ. Nó có thể không phải là rất dễ dàng để làm, mặc dù. Và bạn không thực sự muốn dựa vào chức năng này nếu hiệu suất là rất quan trọng.

2

Con trỏ sẽ không cho bạn biết bất cứ điều gì. Thiết kế của bạn nên: nếu bạn đang sử dụng phân bổ động, thường là ứng dụng của bạn yêu cầu đối tượng phải có tuổi thọ cụ thể, vì vậy hãy bạn biết khi nào xóa chính xác đối tượng. Nếu đối tượng là có thể sao chép hoặc có tuổi thọ tương ứng với phạm vi, bạn không (thường) phân bổ động đó.

Có, tất nhiên, trường hợp ngoại lệ trong mã mức rất thấp — nếu bạn đang thực hiện một cái gì đó giống như std::vector, bạn sẽ có sử dụng một số loại phân bổ động, vì kích thước không phải là biết đến tại thời gian biên dịch . Nhưng phân bổ như vậy không nên trốn thoát; đó là trách nhiệm của lớp cấp thấp để xử lý bộ nhớ .

Cuối cùng, tràn bộ đệm, truy cập bộ nhớ đã xóa và tương tự là hành vi không xác định. Họ không, nói chung, dẫn đến một ngoại lệ, và không có cách nào chung chung của việc xử lý chúng . (Bạn có thể thường sắp xếp để nhận tín hiệu khi những điều như vậy xảy ra, nhưng có quá ít điều bạn có thể làm từ bộ xử lý tín hiệu , điều này thực sự không giúp được gì nhiều.) Nói chung, những gì bạn muốn chương trình bị lỗi, vì bạn không biết trạng thái của nó. Trong những trường hợp hiếm hoi mà đây không phải là trường hợp , bạn phải quay trở lại thực hiện được xác định tiện ích mở rộng nếu chúng tồn tại. Nếu bạn biên dịch với tùy chọn /EHa với VC++, ví dụ, thông thường thì một sự cố sẽ được chuyển thành ngoại lệ C++. Nhưng đó là phần mở rộng VC++ và bạn vẫn không biết trạng thái tổng thể của chương trình khi điều này xảy ra. Nếu đó là vì bạn đã làm hỏng trường không gian trống , có thể bạn không thể làm gì ngay cả khi bạn bắt được ngoại lệ (và có một cơ hội tốt bạn sẽ nhận được ngoại lệ khác từ trình phá hủy đang cố gắng giải phóng bộ nhớ khi bạn thư giãn ngăn xếp).

0

Tôi biết chủ đề này cũ. Nhưng nếu ai đó đang đọc cái này, anh ta nên biết về unique_ptr. shared_ptr đã thực sự là một chi phí. Bộ đếm được lưu trữ trên heap. Mỗi khi truy cập được truy cập, có một nguy cơ bộ nhớ cache bộ xử lý không khớp. unique_ptr Có giới hạn hơn nhưng không có chi phí so với các con trỏ đơn giản. Đề nghị của tôi là thích unique_ptr hơn shared_ptr khi bạn không cần đếm tham chiếu. Một thông báo quan trọng khác là, unique_ptr hoạt động tốt với các mảng. Nếu tôi nhớ chính xác thì điều này cũng đúng với shared_ptr kể từ C++ 17.

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