2010-02-10 36 views
14

Có vẻ như vấn đề của tôi là lỗi trong MSVC. Tôi đang sử dụng Visual Studio 2008 với Service Pack 1 và mã của tôi hoạt động với GCC (như được kiểm tra trên codepad.org).Lỗi thừa kế ảo trong MSVC

Bất kỳ thông tin chính thức nào về lỗi này? Bất kỳ ý tưởng làm thế nào để làm việc xung quanh nó? Lỗi có được sửa trong VS2010 không? Tất cả thông tin chi tiết sẽ được đánh giá cao.

Mã:

struct Base { 
    Base(int i = 0) : i(i) {} 
    virtual ~Base() {} 
    virtual Base *clone() const = 0; 

protected: 
    int i; 
}; 

struct A : virtual public Base { 
    A() {} 
    virtual A *clone() const = 0; 
}; 

struct B : public A { 
    B() {} 
    B *clone() const { return new B(*this); } 

    /// MSVC debugger shows that 'b' is for some reason missing the Base 
    /// portion of it's object ("Error: expression cannot be evaluated") 
    /// and trying to access 'b.i' causes an unhandled exception. 
    /// 
    /// Note: This only seems to occur with MSVC 
    B(const B &b) : Base(b.i), A() {} 
}; 

void foo(const A &elem) { 
    A *a = elem.clone(); 
    if (a) delete a; 
} 

int main() { 
    A *a = new B; 
    foo(*a); 
    delete a; 
} 
+1

Điều này dường như là một lỗi. – GManNickG

+2

Vẫn làm điều đó trong Visual Studio 2010. – Corey

+0

Tôi lưu ý rằng codepad sử dụng g ++ 4.1.2, vì vậy tôi đã thử với Borland C++ 5.82 và nó hoạt động tốt. – Corey

Trả lời

8

Có vẻ như trình biên dịch không được điều chỉnh một cách chính xác con trỏ this khi gọi qua A::clone. Nếu bạn loại bỏ tuyên bố của A::clone thì mọi thứ hoạt động tốt.

Đào sâu hơn, khi bạn có A::clone, vtable trông như thế này:

[0x0] 0x002f1136 [thunk]:B::`vector deleting destructor'`vtordisp{4294967292,0}' (unsigned int) void * 
    [0x1] 0x002f11e0 [thunk]:B::clone`vtordisp{4294967292,0}' (void) void * 
    [0x2] 0x002f12ad [thunk]:B::clone`vtordisp{4294967292,4}' (void) void * 
    [0x3] 0x002f12a3 B::clone(void) void * 

Và foo gọi elem.__vfptr[2], bù đắp this sai bởi -4 byte. Nếu không có A::clone, vtable trông như sau:

[0x0] 0x00ee1136 [thunk]:B::`vector deleting destructor'`vtordisp{4294967292,0}' (unsigned int) void * 
    [0x1] 0x00ee11e0 [thunk]:B::clone`vtordisp{4294967292,0}' (void) void * 
    [0x2] 0x00ee12a3 B::clone(void) void * 

Và gọi foo elem.__vfptr[1]. Điều đó không điều chỉnh this ở tất cả (và mã giả định rằng this sẽ bằng Base thay vì B).

Vì vậy, nó trông giống như các trình biên dịch giả định rằng A::clone là một phương pháp ảo mới và không ghi đè Base::clone khi xác định liệu A đòi hỏi một bảng ảo mới, nhưng sau đó một số mã khác sau này xác định rằng A không cần một bảng ảo. Bạn có thể xác minh điều này bằng cách so sánh sizeof(B) có hoặc không có chức năng ảo mới:

struct A : virtual public Base { 
    A() {} 
    virtual A *clone() const = 0; 
}; //sizeof(B)==16 

struct A : virtual public Base { 
    A() {} 
    virtual A *clone() const = 0; 
virtual const A *clone2() const { return this; } 
}; //sizeof(B)==20 

Vì vậy, nó là một lỗi biên dịch.

1

Có vẻ như (từ một số thử nghiệm) rằng lỗi được gây ra bởi sự kết hợp của một lớp cơ sở ảo với một phương thức ảo thuần túy sử dụng các kiểu trả về biến đổi.

Kể từ khi thả phương thức ảo thuần túy từ lớp cơ sở hoặc tạo cơ sở cho lớp cơ sở không ảo hoặc làm cho phương thức clone() không biến đổi dường như giải quyết lỗi.

Tôi đoán điều này được giải quyết cho tôi (sau khi tôi gửi báo cáo lỗi cho MS), và tôi thậm chí còn lại với một vài tùy chọn để phá vỡ nó. :)