2011-11-24 30 views
7

Hãy xem xét chương trình bên dưới. Nó đã được đơn giản hóa từ một trường hợp phức tạp. Nó không thành công khi xóa bộ nhớ được cấp phát trước đó, trừ khi tôi loại bỏ trình phá hủy ảo trong lớp Obj. Tôi không hiểu tại sao hai địa chỉ từ đầu ra của chương trình khác nhau, chỉ khi destructor ảo có mặt.Điều gì xảy ra với việc sử dụng vị trí mới này []? làm

// GCC 4.4 
#include <iostream> 

using namespace std; 

class Arena { 
public: 
    void* alloc(size_t s) { 
     char* p = new char[s]; 
     cout << "Allocated memory address starts at: " << (void*)p << '\n'; 
     return p; 
    } 

    void free(void* p) { 
     cout << "The memory to be deallocated starts at: " << p << '\n'; 
     delete [] static_cast<char*> (p); // the program fails here 
    } 
}; 

struct Obj { 
    void* operator new[](size_t s, Arena& a) { 
     return a.alloc(s); 
    } 

    virtual ~Obj() {} // if I remove this everything works as expected 

    void destroy(size_t n, Arena* a) { 
     for (size_t i = 0; i < n; i++) 
      this[n - i - 1].~Obj(); 
     if (a) 
      a->free(this); 
    } 
}; 


int main(int argc, char** argv) { 
    Arena a; 

    Obj* p = new(a) Obj[5](); 
    p->destroy(5, &a); 

    return 0; 
} 

Đây là sản phẩm của chương trình trong việc thực hiện của tôi khi destructor ảo đang hiện diện:

phân bổ địa chỉ bộ nhớ bắt đầu tại địa chỉ: 0x8895008 Bộ nhớ được deallocated bắt đầu tại địa chỉ: 0x889500c

RUN FAILED (giá trị thoát 1)

Vui lòng không hỏi chương trình được yêu cầu o. Như tôi đã nói nó đến từ một trường hợp phức tạp hơn, nơi Arena là một giao diện cho nhiều loại bộ nhớ khác nhau. Trong ví dụ này, bộ nhớ chỉ được phân bổ và deallocated từ heap.

+0

Lưu ý rằng bạn * làm * cần một mảng vị trí phù hợp xóa trong trường hợp một biểu thức mới ném một ngoại lệ. –

Trả lời

5

this không phải là con trỏ trả về bởi các new tại dòng char* p = new char[s]; Bạn có thể thấy rằng kích thước s có lớn hơn 5 Obj trường. Sự khác biệt (phải là sizeof (std::size_t)) là bộ nhớ bổ sung, chứa độ dài của mảng, 5, ngay trước địa chỉ chứa trong this.

OK, spec làm cho nó rõ ràng:

http://sourcery.mentor.com/public/cxx-abi/abi.html#array-cookies

2,7 Mảng điều hành Cookie mới

Khi điều hành mới được sử dụng để tạo ra một mảng mới, một cookie thường được lưu trữ để nhớ chiều dài được phân bổ (số phần tử mảng) sao cho nó có thể được deallocated một cách chính xác.

Cụ thể:

Không cookie được yêu cầu nếu loại phần tử mảng T có một destructor tầm thường (12,4 [class.dtor]) và thông thường (mảng) chức năng deallocation (3.7.3.2 [basic.stc .dynamic.deallocation]) không lấy hai đối số.

Vì vậy, ảo -ness của destructor là không thích hợp, điều quan trọng là các destructor là không tầm thường, mà bạn có thể dễ dàng kiểm tra, bằng cách xóa các từ khóa virtual ở phía trước của destructor và quan sát chương trình gặp sự cố.

+0

Vâng, tôi không thực sự quan tâm đến kích thước. Tôi quan tâm đến lý do tại sao điều này không khớp với con trỏ alloc() đã đưa ra cho việc đặt đối tượng. – Martin

+0

@Martin, vì 'alloc()' đã phân bổ không gian bổ sung cho chiều dài của mảng và các phần tử mảng theo sau. Tôi không thực sự biết ngay tại sao nó không lưu trữ chiều dài mảng nếu không có destructor ảo, phải kiểm tra spec C++ ABI. – chill

+0

Thông số này là gì? Thông số C++ của tôi không đề cập đến cookie. –

0

Dựa trên câu trả lời ớn lạnh, nếu bạn muốn làm cho nó 'an toàn':

#include <type_traits> 

a->free(this - (std::has_trivial_destructor<Obj>::value ? 1 : 0)); 
+1

Không, không thực sự, có lẽ 'a-> miễn phí ((std :: size_t *) này - (std :: has_trivial_destructor :: value? 1: 0));', nhưng nó nói đúng UD và tôi chắc chắn nó sẽ phá vỡ trong một số trường hợp góc tối nghĩa và/hoặc trình biên dịch khác nhau;) – chill

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