Có an toàn để xóa một con trỏ NULL?


299

Có an toàn để xóa một con trỏ NULL?

Và nó có phải là một phong cách mã hóa tốt?


21
Thực hành tốt là viết các chương trình C ++ mà không cần một cuộc gọi nào delete. Sử dụng RAII thay thế. Đó là, sử dụng std::vector<T> v(100);thay vì T* p = new T[100];sử dụng các con trỏ thông minh như unique_ptr<T>shared_ptr<T>chăm sóc xóa thay vì các con trỏ thô, v.v.
fredoverflow

8
nhờ make_shared(c ++ 11) và make_unique(c ++ 14) chương trình của bạn nên chứa zero của newdelete
sp2danny

2
Vẫn có thể có một số trường hợp hiếm hoi yêu cầu mới / xóa, ví dụ: nguyên tử <T *>: nguyên tử <unique_ptr <T >> không được phép và nguyên tử <shared_ptr <T >> có thể không chấp nhận được trong một số trường hợp.
atb

2
Để khai báo một lớp có quản lý tài nguyên bằng RAII, bạn cần gọi mới và xóa phải không?, Hoặc bạn đang nói có một số lớp mẫu để ẩn điều này ngay cả điều này.
VinGarcia

2
@VinGarcia Điểm quan trọng là hầu hết mã người dùng / khách hàng (nghĩa là: không phải thư viện) không bao giờ phải viết newhoặc delete. Các lớp được thiết kế để quản lý tài nguyên, trong đó các thành phần tiêu chuẩn không thể thực hiện công việc, tất nhiên có thể làm những gì chúng cần làm, nhưng vấn đề là chúng làm những thứ xấu xí với bộ nhớ mà chúng quản lý, chứ không phải mã người dùng cuối. Vì vậy, hãy tạo lớp thư viện / trình trợ giúp của riêng bạn để làm new/ deletevà sử dụng lớp đó thay vì chúng.
gạch dưới

Câu trả lời:


265

deletethực hiện kiểm tra bằng mọi cách, vì vậy kiểm tra nó về phía bạn thêm chi phí và trông xấu hơn. Một thực hành rất tốt là đặt con trỏ thành NULL sau delete(giúp tránh xóa hai lần và các vấn đề hỏng bộ nhớ tương tự khác).

Tôi cũng thích nếu deletetheo mặc định là đặt tham số thành NULL như trong

#define my_delete(x) {delete x; x = NULL;}

(Tôi biết về các giá trị R và L, nhưng nó sẽ không tốt sao?)


72
Lưu ý rằng vẫn có thể có một vài con trỏ khác trỏ đến cùng một đối tượng ngay cả khi bạn đặt một thành NULL khi xóa.
sth

13
Trong hầu hết các trường hợp trong mã của tôi, con trỏ đi ra khỏi phạm vi một khi nó bị xóa. An toàn hơn nhiều so với chỉ đơn thuần là đặt nó thành NULL.
jalf

142
Một thực hành rất thần là không đặt con trỏ thành NULL sau khi xóa. Đặt con trỏ thành NULL sau khi xóa nó sẽ khắc phục lỗi cấp phát bộ nhớ, đây là một điều rất tệ. Một chương trình đúng không xóa con trỏ hai lần và chương trình xóa con trỏ hai lần sẽ bị sập.
Damon

15
@ Alice: Không liên quan đến những gì tiêu chuẩn nói về khía cạnh đó. Tiêu chuẩn được xác định xóa một con trỏ null có giá trị vì một số lý do vô lý 30 năm trước, vì vậy nó là hợp pháp (rất có thể là một di sản C). Nhưng việc xóa cùng một con trỏ hai lần (ngay cả sau khi thay đổi mẫu bit của nó) vẫn là một lỗi chương trình nghiêm trọng. Không phải bởi từ ngữ của tiêu chuẩn, mà bởi logic chương trình và quyền sở hữu. Như đang xóa một con trỏ null, vì con trỏ null tương ứng với không có đối tượng , vì vậy không có gì có thể bị xóa. Một chương trình phải biết chính xác nếu một đối tượng là hợp lệ và ai sở hữu nó, và khi nào nó có thể bị xóa.
Damon

27
@Damon Tuy nhiên, mặc dù có sự bãi bỏ các quy tắc sở hữu hà khắc của bạn, các cấu trúc khóa miễn phí có thể mạnh hơn so với các cấu trúc khóa. Và vâng, đồng nghiệp của tôi thực sự yêu tôi vì hồ sơ thực thi nâng cao mà các cấu trúc này cung cấp và sự an toàn của luồng nghiêm ngặt mà họ duy trì, cho phép dễ dàng lý luận về mã (rất tốt cho bảo trì). Tuy nhiên, không có cuộc tấn công cá nhân hay ngụ ý nào của bạn liên quan đến bất kỳ định nghĩa nào về tính đúng đắn, hợp lệ hoặc quyền sở hữu. Những gì bạn đề xuất là một quy tắc tốt, nhưng nó không phải là một quy luật phổ quát và cũng không được quy định trong tiêu chuẩn.
Alice

77

Từ tiêu chuẩn dự thảo C ++ 0x.

$ 5,3,5 / 2 - "[...] Trong cả hai cách khác, giá trị của toán hạng xóa có thể là giá trị con trỏ null. [... '"

Tất nhiên, không ai sẽ thực hiện 'xóa' một con trỏ có giá trị NULL, nhưng nó an toàn để làm. Lý tưởng nhất là không nên có mã xóa con trỏ NULL. Nhưng đôi khi nó rất hữu ích khi xóa các con trỏ (ví dụ trong một thùng chứa) xảy ra trong một vòng lặp. Vì việc xóa giá trị con trỏ NULL là an toàn, người ta thực sự có thể viết logic xóa mà không cần kiểm tra rõ ràng cho toán hạng NULL để xóa.

Bên cạnh đó, Tiêu chuẩn C $ 7,20.3.2 cũng nói rằng 'miễn phí' trên con trỏ NULL không có hành động.

Hàm miễn phí làm cho không gian được trỏ bởi ptr bị phân bổ, nghĩa là, được tạo sẵn để phân bổ thêm. Nếu ptr là một con trỏ null, không có hành động xảy ra.


2
Tôi thực sự muốn câu trả lời này cho các trích dẫn của nó nếu nó không cố ý đưa ra sự không hiệu quả trong mã không được tối ưu hóa. Như câu trả lời được chấp nhận, việc xóa một con trỏ null là không có. Do đó, kiểm tra nếu một con trỏ là null trước khi xóa nó là hoàn toàn không liên quan.
codetaku

47

Có nó là an toàn.

Không có hại trong việc xóa một con trỏ null; nó thường làm giảm số lượng kiểm tra ở phần đuôi của hàm nếu các con trỏ chưa được phân bổ được khởi tạo về 0 và sau đó chỉ cần xóa.


Vì câu trước đã gây nhầm lẫn, một ví dụ - không phải là ngoại lệ an toàn - về những gì đang được mô tả:

void somefunc(void)
{
    SomeType *pst = 0;
    AnotherType *pat = 0;

    
    pst = new SomeType;
    
    if (…)
    {
        pat = new AnotherType[10];
        
    }
    if (…)
    {
        code using pat sometimes
    }

    delete[] pat;
    delete pst;
}

Có tất cả các loại nits có thể được chọn với mã mẫu, nhưng khái niệm này (tôi hy vọng) rõ ràng. Các biến con trỏ được khởi tạo về 0 để các deletehoạt động ở cuối hàm không cần kiểm tra xem chúng có phải là null trong mã nguồn hay không; mã thư viện thực hiện kiểm tra nào.


Tôi đã phải đọc nó một vài lần để có ý nghĩa của nó. Bạn phải có nghĩa là khởi tạo chúng về 0 ở đầu phương thức, hoặc trong khi đó, không phải ở đuôi, chắc chắn? Nếu không, bạn sẽ chỉ cần loại bỏ cả zeroing và xóa.
Hầu tước Lorne

1
@EJP: Một phác thảo không hoàn toàn hợp lý của một chức năng có thể là : void func(void) { X *x1 = 0; Y *y1 = 0; … x1 = new[10] X; … y1 = new[10] Y; … delete[] y1; delete[] x1; }. Tôi đã không hiển thị bất kỳ cấu trúc khối hoặc nhảy, nhưng các delete[]hoạt động ở cuối là an toàn vì các khởi tạo khi bắt đầu. Nếu một cái gì đó nhảy đến cuối cùng sau khi x1được phân bổ và trước khi y1được phân bổ và không có khởi tạo y1, thì sẽ có hành vi không xác định - và trong khi mã có thể kiểm tra tính vô hiệu (của x1y1) trước khi xóa, không cần phải làm gì vì thế.
Jonathan Leffler

22

Xóa một con trỏ null không có hiệu lực. Đó không phải là phong cách mã hóa tốt bởi vì nó không cần thiết, nhưng nó cũng không tệ.

Nếu bạn đang tìm kiếm các thực tiễn mã hóa tốt, hãy cân nhắc sử dụng các con trỏ thông minh thay vào đó, vậy thì bạn không cần phải làm gì deletecả.


20
thời gian mọi người muốn xóa một con trỏ NULL là khi họ không chắc nó có chứa NULL không ... nếu họ biết đó là NULL thì họ sẽ không xem xét xóa và từ đó hỏi ;-).
Tony Delroy

@Tony: Quan điểm của tôi chỉ là nó sẽ không có tác dụng và sự hiện diện của mã đó sẽ xóa một con trỏ đôi khi chứa NULL không hẳn là xấu.
Brian R. Bondy

3
Kiểm tra dự phòng IMO chắc chắn là xấu, về hiệu suất, khả năng đọc và khả năng bảo trì.
paulm

@paulm OP chắc chắn không nói về loại xấu đó, hơn nữa là loại Seg Fault / UB tệ.
Mùa đông


3

Nó là an toàn trừ khi bạn quá tải toán tử xóa. nếu bạn quá tải toán tử xóa và không xử lý điều kiện null thì nó không an toàn chút nào.


Bạn có thể thêm bất kỳ lời giải thích cho câu trả lời của bạn?
Marcin Nabiałek

-2

Tôi đã trải nghiệm rằng không an toàn (VS2010) để xóa [] NULL (tức là cú pháp mảng). Tôi không chắc liệu đây có phải là theo tiêu chuẩn C ++ hay không.

an toàn để xóa NULL (cú pháp vô hướng).


6
Điều này là bất hợp pháp và tôi không tin điều đó.
Konrad Rudolph

5
Bạn sẽ có thể xóa bất kỳ con trỏ null. Vì vậy, nếu nó vi phạm cho bạn, thì có lẽ bạn có một lỗi trong mã hiển thị nó.
Bí ẩn

3
§5.3.2 Trong phương án thứ hai (xóa mảng), giá trị của toán hạng xóa có thể là giá trị con trỏ null hoặc giá trị con trỏ xuất phát từ biểu thức mới của mảng trước đó.
sp2danny

1
@Opux Hành vi của VS2010 bị cáo buộc trong câu trả lời. Như đã giải thích trong các bình luận khác, nó an toàn delete[] NULL.
Konrad Rudolph

1
@Opux Đó là lý do tại sao tôi đã viết ra Tôi không tin đó là Thay vì đó là sai. Nhưng tôi vẫn không làm, và nó sẽ là một sự vi phạm tiêu chuẩn khá kỳ quặc, ngu ngốc. VC ++ thực sự nhìn chung khá tốt trong việc tuân theo các hạn chế của tiêu chuẩn và những nơi vi phạm chúng có ý nghĩa về mặt lịch sử.
Konrad Rudolph
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.