2013-07-08 32 views
6

Tôi chạy vào một đống tham nhũng ngày hôm nay gây ra bởi các thiết lập CRT khác nhau (MTd MDd) trong dll của tôi và dự án thực tế của tôi. Điều tôi thấy lạ là ứng dụng chỉ gặp sự cố khi tôi đặt trình phá hủy trong dll thành ảo. Có một lời giải thích dễ dàng cho điều đó không? Tôi nhận được rằng tôi không thể giải phóng bộ nhớ mà không phải trên heap của tôi, nhưng nơi chính xác là sự khác biệt khi tôi xác định destructor là phi ảo.CRT destructor ảo

Một số luật chỉ để làm cho nó rõ ràng hơn một chút

Các DLL

#pragma once 
class CTestClass 
{ 
public: 
    _declspec(dllexport) CTestClass() {}; 
    _declspec(dllexport) virtual ~CTestClass() {}; 
}; 

Và dự án của tôi

int main(int argc, char* argv[]) 
{ 
    CTestClass *foo = new CTestClass; 
    delete foo; // Crashes if the destructor is virtual but works if it's not 
} 
+0

Đồng thời, bạn có cùng một vấn đề hay không bằng cách di chuyển declspec sang lớp * * ('lớp _declspec (dllexport) CTestClass {...}') và loại bỏ các declspecs mỗi thành viên? Chỉ tò mò thôi. Và lưu ý, các mã gọi và DLL nên được sử dụng cùng một CRT (gỡ lỗi hoặc phát hành), do đó thats cái gì để xem xét. Tôi thậm chí không chắc chắn chế độ hỗn hợp được hỗ trợ (tôi không nghĩ rằng nó là). – WhozCraig

+6

Bạn có nhiều bản sao của CRT trong quá trình của bạn. Và bạn chỉ xuất các phương thức lớp chứ không phải bảng v. Cố gắng giải thích cách tất cả điều này tương tác với việc đánh bom mã của bạn không hiệu quả như thế nào.Việc xuất một lớp với các phương thức ảo yêu cầu bạn xuất toàn bộ lớp, đặt __declspec (dllexport) bên cạnh từ khóa * class *. Và bạn phải đảm bảo một bộ cấp phát đơn được sử dụng để tạo và phá hủy đối tượng. Rất khó đảm bảo trừ khi bạn xây dựng với/MD một cách nhất quán và sử dụng cùng một phiên bản trình biên dịch chính xác. Việc trưng ra các lớp C++ trên các ranh giới mô-đun chỉ là nguy hiểm. –

+0

Bạn đang có quyền, ngay cả khi tôi tìm ra lý do tại sao nó không hoạt động, nó sẽ không giúp tôi quá nhiều. Cảm ơn anyway cho những suy nghĩ của bạn :) – Poisonbox

Trả lời

2

Có sự khác biệt giữa

class CTestClass 
{ 
public: 
    _declspec(dllexport) CTestClass() {} 
    _declspec(dllexport) virtual ~CTestClass() {} 
}; 

__declspec(dllexport) class CTestClass 
{ 
public: 
    CTestClass() {} 
    virtual ~CTestClass() {} 
}; 

Trong trường hợp trước đây bạn chỉ một trình biên dịch để xuất khẩu chỉ có hai chức năng thành viên: CTestClass :: CTestClass() và CTestClass :: ~ CTestClass(). Nhưng trong trường hợp sau, bạn sẽ hướng dẫn một trình biên dịch xuất khẩu các bảng chức năng ảo. Bảng này là cần thiết khi bạn đã có một destructor ảo. Vì vậy, nó có thể là nguyên nhân của vụ tai nạn. Khi chương trình của bạn cố gắng gọi hàm hủy ảo, nó sẽ tìm kiếm nó trong bảng chức năng ảo được liên kết, nhưng nó không được khởi tạo đúng cách để chúng ta không biết nó thực sự ở đâu. Nếu destructor của bạn không phải là ảo, sau đó bạn không cần bất kỳ bảng chức năng ảo và tất cả mọi thứ hoạt động tốt.

0

Bạn đã không thực sự bài đủ code để đảm bảo. Nhưng ví dụ bạn không nên sụp đổ vì không có bất cứ điều gì sai với nó:

int main(int argc, char* argv[]) 
{ 
    // 1. Allocated an instance of this class in *this/exe* heap, not the DLL's heap 
    // if the constructor allocates memory it will be allocated from the DLL's heap 
    CTestClass *foo = new CTestClass; 

    // 2. Call the destructor, if it calls delete on anything it will be freed from the DLL's heap since thats where the destructor is executing from. Finally we free the foo object from *this/exe* heap - no problems at all. 
    delete foo; 
} 

Tôi nghi ngờ rằng trong mã thực sự của bạn, bạn phải sử dụng toán tử delete trên một đối tượng người là điều hành mới đã được thực hiện trong bối cảnh của dll . Và không có từ khóa ảo, bạn có thể bỏ lỡ cuộc gọi destructor đang thực hiện xóa bối cảnh chéo.

+0

"ví dụ của bạn KHÔNG nên sụp đổ vì không có gì sai với nó", điều này là chính xác những gì tôi nghĩ quá, tuy nhiên tôi đã phá vỡ mã xuống chỉ là những gì đã được viết ở trên và nó trong thực tế Thất bại. – Poisonbox

+0

Bạn có thể tải lên một dự án mẫu không? Phải có cái gì đó khác đi sai – paulm

0

destructor ảo chỉ cần thiết khi bạn có một số cây phân cấp thừa kế. Từ khóa ảo sẽ đảm bảo rằng con trỏ tới đối tượng thực tế (không phải kiểu của đối tượng) bị phá hủy bằng cách tìm ra hàm hủy của nó trong Vtable. Kể từ trong ví dụ này, đi theo mã bạn đã cung cấp, CTestClass không kế thừa từ bất kỳ lớp nào khác, nó là một cách một lớp cơ sở và do đó không cần một destructor ảo. Tôi giả sử có thể có một quy tắc thực thi khác đang được thực hiện nhưng bạn không nên sử dụng ảo với các lớp cơ sở. Bất cứ khi nào bạn tạo ra một đối tượng có nguồn gốc, bạn cũng tạo ra cơ sở của nó (vì lý do đa hình) và cơ sở luôn bị phá hủy (nguồn gốc chỉ bị phá hủy nếu bạn tạo destructor cho nó ảo, do đó đặt nó trong bảng vlookup (ảo) thời gian chạy) .

Cảm ơn

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