2010-01-06 46 views
6

Tôi có lớp học abstractBase và lớp Derived.Xóa toán tử và mảng?

int main() 
{ 
    Base *arrayPtr[3]; 

    for (int i = 0; i < 3; i++) 
    { 
    arrayPtr[i] = new Derived(); 
    } 

    //some functions here 

    delete[] arrayPtr; 

    return 0; 
} 

Tôi không biết cách sử dụng toán tử xóa. Nếu tôi xóa các mảng của các con trỏ lớp cơ sở như được hiển thị ở trên, thì cuộc gọi này có dẫn xuất các đối tượng lớp hủy và làm sạch bộ nhớ không?

Trả lời

10

Bạn có để lặp qua các phần tử của mảng của bạn, delete mỗi trong số họ. Sau đó, gọi delete [] trên mảng nếu nó đã được phân bổ động bằng cách sử dụng new[].

Trong mã mẫu của bạn, mảng được cấp phát trên ngăn xếp để bạn không được gọi delete [] trên đó.

Ngoài ra, hãy đảm bảo lớp học Base của bạn có hàm hủy là virtual.

Tham chiếu: When should my destructor be virtual.

+0

+1 đối với điểm phá hủy 'ảo' –

+7

Không cần gọi xóa [] trên mảng, là nó chưa được' làm mới '. – fretje

+0

vâng tôi đã chỉnh sửa câu trả lời của tôi khi bạn viết bình luận của bạn, thx –

10

Không, bạn phải xóa một cách rõ ràng từng hạng mục trong mảng:

for (int i = 0; i < 3; ++i) 
{ 
    delete arrayPtr[i]; 
} 
+0

@Roger Tôi thấy không xóa [] –

+0

@Roger: nhận ra rằng sau bài đăng đầu tiên của tôi ... đã được chỉnh sửa, nhưng dù sao đi nữa;) – fretje

+0

James: Nó đã được chỉnh sửa trong vòng 5 phút đầu tiên. –

2

Bạn thay vào đó nên làm:

for (int = i; i < 3; i++) 
{ 
    delete arrayPtr[i]; 
} 

Và bạn không nên làm delete[] arrayPtr; như bạn đang cố gắng để miễn phí/xóa một stack giao arrayPtr.

Một điều cần xem xét là sử dụng một số std::vector của con trỏ thay vì mảng. Và nếu bạn đang sử dụng trình biên dịch thực hiện TR1, bạn cũng có thể sử dụng một số std::vector của std::tr1::shared_ptr thay cho con trỏ thô và bạn sẽ không phải lo lắng về việc xóa các đối tượng đó.

Ví dụ:

{ 
    std::vector< std::tr1::shared_ptr<Base> > objects; 
    for (int i=0; i < 3; ++i) 
    { 
     objects.push_back(std::tr1::shared_ptr<Base>(new Derived())); 
    } 
} // here, once "objects" exit scope, all of your Derived objects are nicely deleted 
+0

Shared_ptr <> không phải là câu trả lời cho mọi thứ !!! Con trỏ không được chia sẻ để bắt đầu. boost :: ptr_vector <> có thể là một lựa chọn tốt hơn. –

+0

Đồng ý, chỉ là gợi ý "ít nhất của tệ nạn", theo nghĩa nào đó, tôi đã có shared_ptr trên macbook của tôi với gcc, và sử dụng boost :: ptr_vector Tôi cần phải kéo tăng trong dự án, có thể không cần thiết . – Dmitry

+0

@Martin York: 'shared_ptr <>' không phải là * câu trả lời * cho mọi thứ, nhưng đó là câu trả lời * an toàn * cho hầu hết các câu hỏi về con trỏ. Tôi thường sử dụng nó trừ khi tôi đã có một lý do để sử dụng một cái gì đó khác nhau, và hợp lý thoải mái cho thấy nó trong tình huống chung. –

0

Hãy chắc chắn rằng Base có một destructor ảo. Sau đó, như fretje outlined, hãy xóa từng phần tử trong mảng, sau đó xóa mảng đó.

Bạn nên sử dụng std::vector cho mảng đó. Điều đó nói rằng, bạn nên thực sự đang sử dụng một hộp chứa được làm cho loại điều này. (Vì vậy, bạn không vô tình không xóa tất cả các yếu tố, mà chắc chắn sẽ là trường hợp nếu một ngoại lệ được ném!) Tăng có such a library.

+0

Mặc dù "bạn nên sử dụng các thùng chứa" chắc chắn là một lời khuyên hàng đầu, bằng cách nào đó nó không nên được ném vào quá nhanh. Tôi có nghĩa là 1) hiểu những gì đang xảy ra bằng cách làm nó bằng tay, sau đó 2) trong mã sản xuất đòn bẩy các thư viện đá rắn như STL và Boost. Nếu không, không phải là có xu hướng xem xét các container và các công cụ khác được cung cấp bởi các thư viện như hộp đen chỉ? Bạn nghĩ sao? –

+0

Gregory: Không đồng ý. Tại một số điểm bạn nên hiểu những gì đang xảy ra dưới mui xe (có thể), nhưng nó không còn rõ ràng với tôi theo thứ tự nên được (tôi sử dụng để đồng ý với bạn). Bạn có nên hiểu làm thế nào malloc hoạt động trước khi bạn có thể gọi mới? Bạn có nên hiểu cách vtables làm việc trước khi sử dụng dynamic_cast (và * sau đó * tìm ra rằng không phải là một phần được đảm bảo của việc triển khai)? Cả hai không. Điều đó nói rằng, bạn nên luôn luôn biết nơi để đi tìm kiếm thông tin bạn cần, hoặc người để nói chuyện, vv –

+0

@Roger tôi nhận được quan điểm của bạn. Lấy ví dụ của bạn, bạn không cần phải biết chi tiết thực hiện phân bổ bộ nhớ heap vẫn còn tốt để biết sự khác biệt giữa heap và stack. Bạn không cần phải biết vtable được thực hiện như thế nào và như vậy, bạn vẫn phải biết việc gọi một phương thức ảo làm cho một sự gián tiếp. Và cuối cùng các thuật toán và cấu trúc dữ liệu là vàng: bạn chắc chắn phải biết sự khác biệt giữa một mảng liền kề và một danh sách các khái niệm ngay cả khi bạn đang sử dụng std :: vector hoặc std :: list implementations; nếu không trong thời gian dài bạn chỉ thiếu manh mối :) –

1

Bạn phải xóa từng thành viên của mảng. Bạn cũng phải chắc chắn rằng lớp cơ sở của bạn có một destructor ảo. Bạn cũng có thể muốn xem xét làm cho nó trở thành một mảng (hoặc tốt hơn vẫn là một std :: vector) của các con trỏ thông minh, chẳng hạn như boost :: shared_ptr.

1

Không, bạn không thể làm điều đó. Như những người khác đề nghị bạn phải đi qua từng mục và xóa nó. Đó là một quy tắc rất đơn giản cần nhớ. Nếu bạn đã phân bổ bằng cách sử dụng new thì hãy sử dụng delete và nếu bạn đã sử dụng new[] thì sử dụng delete[]

0

Không, điều này không thực sự làm những gì bạn muốn.

Có hai điểm cần xem ra cho ở đây:

  1. Cú pháp delete[] arrayPtr được sử dụng nếu bạn tự động phân bổ mảng, như thế này:

    arrayPtr = new (Base *)[mylength]; 
    

    Trong trường hợp của bạn, tuy nhiên, bạn có một mảng được phân bổ tĩnh, vì vậy không cần phải xóa nó. Bạn làm gì, tuy nhiên, cần phải xoá bỏ các yếu tố cá nhân trong mảng:

    for (int = i; i < 3; i++) 
        delete arrayPtr[i]; 
    
  2. Điểm thứ hai bạn cần phải chăm sóc là làm cho destructor của lớp Base ảo:

    class Base 
    { 
        virtual ~Base(); 
        /* ... */ 
    }; 
    

    Điều này đảm bảo rằng khi bạn gọi xóa trên Base * thực sự trỏ đến một số Derived, thì hàm hủy của Derived được gọi, không chỉ là hàm hủy của Base.

1

Chú ý gì vắng mặt:

int main() { 
    boost::ptr_vector<Base> v; 
    for (int i = 0; i < 3; i++) v.push_back(new Derived()); 
    // some functions here, using v[0] through v[2] 
} 

Kiểm tra Boost's pointer containers ra.

1

Toán tử xóa phải khớp với toán tử mới trên con trỏ đó, nếu nó được cấp phát với new[], bạn phải gọi delete[] và ngược lại;

int* pInt = new int; 
delete pInt; OK 
delete [] pInt; WRONG 

int[] pIntArr = new int[3]; 
delete [] pIntArr; OK 
delete pIntArr; WRONG 

Trong trường hợp của bạn có điều gì khác không đúng - bạn đang cố gắng phân bổ delete trên ngăn xếp. Điều đó sẽ không hiệu quả.

Bạn phải xóa riêng từng con trỏ trong trường hợp cụ thể này.

1

Những gì bạn có ở đó là hành vi không xác định - một lỗi. Mỗi cuộc gọi đến new cần phải khớp với delete; mỗi cuộc gọi đến new[] cần phải được đối sánh với một delete[]. Cả hai là riêng biệt và không thể được trộn lẫn.

Trong mã bạn đã đăng, bạn có một chuỗi con trỏ tới Căn cứ được cấp phát trên ngăn xếp. Sau đó, bạn đang gọi delete[] trên một mảng được cấp phát trên ngăn xếp - bạn không thể làm điều đó. Bạn chỉ có thể delete[] một mảng được phân bổ trên heap với new[].

Bạn cần có cuộc gọi đến delete cho mỗi phần tử được phân bổ với new - hoặc tốt hơn là hãy xem xét sử dụng lớp chứa, chẳng hạn như std::vector, thay vì sử dụng mảng.

0

Bạn đang trộn mô hình - Toán tử xóa mảng được thiết kế để giải phóng bộ nhớ được cấp bởi toán tử mới mảng, nhưng bạn phân bổ mảng của bạn trên ngăn xếp dưới dạng mảng con trỏ và sau đó phân bổ đối tượng cho mỗi mảng hội viên. Trong mã của bạn, bạn cần phải lặp qua mảng.

Để sử dụng các nhà điều hành mảng mới, bạn muốn tuyên bố như thế này:

Base *array; 
array = new Base[3]; 
/* do stuff */ 
delete[] array; 

này phân bổ một khu vực bộ nhớ tiếp giáp với ba đối tượng - lưu ý rằng bạn đã có một mảng các đối tượng cơ sở, không một mảng con trỏ tới các đối tượng Base.

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