2012-08-25 35 views
5

Tôi tự hỏi, nếu có bất kỳ nhà kinh doanh nào trong C++ có thể làm sáng tỏ tình huống kỳ lạ này. Một trong những ví dụ đi kèm với động cơ vật lý Box2D đang gặp sự cố với thông điệp "phương pháp ảo thuần túy được gọi", nhưng chỉ với một trình biên dịch nhất định (và chỉ trong bản phát hành bản phát hành).constructor nội tuyến lặp lại trong khung ngăn xếp gây ra "phương pháp ảo thuần túy được gọi là"?

Box2D như bạn có thể biết là một đoạn mã khá vững chắc vì vậy tôi nghĩ đây có thể là một vấn đề với trình biên dịch, đặc biệt là nó chỉ xảy ra với trình biên dịch cụ thể này. Tôi đang sử dụng mingw32 trên Windows7:

> gcc.exe --version 
gcc version 4.4.0 (GCC) 

Dưới đây là trích đoạn trích đoạn của các phần liên quan của Box2D. Bạn có thể kiểm tra nguồn gốc đầy đủ tại địa chỉ:

b2Shape.h
b2CircleShape.h
b2CircleShape.cpp
SensorTest.h

//base class 
class b2Shape 
{ 
public: 
    virtual ~b2Shape() {} 
    virtual b2Shape* Clone(b2BlockAllocator* allocator) const = 0; 
}; 


//sub class 
class b2CircleShape : public b2Shape 
{ 
public: 
    b2CircleShape(); 
    b2Shape* Clone(b2BlockAllocator* allocator) const; 
}; 

inline b2CircleShape::b2CircleShape() {} 

b2Shape* b2CircleShape::Clone(b2BlockAllocator* allocator) const 
{ 
    void* mem = allocator->Allocate(sizeof(b2CircleShape)); 
    b2CircleShape* clone = new (mem) b2CircleShape; 
    *clone = *this; 
    return clone; 
} 

Lưu ý vị trí mới trong hàm Clone.

Bây giờ thực hiện điều đó gây ra vấn đề nắm này:

{ 
    b2CircleShape shape; 
    shape.Clone(allocator); //ok 
} 
{ 
    b2CircleShape shape; 
    shape.Clone(allocator); //"pure virtual method called" 
} 

Sau khi giáo dục bản thân mình về cách một phương pháp ảo bao giờ có thể được gọi ở nơi đầu tiên, tôi đã cố gắng để tìm ra lý do tại sao nó đang xảy ra ở đây , vì nó không phù hợp với trường hợp cổ điển gọi một hàm ảo trong hàm tạo lớp cơ sở. Sau một phiên kéo dài một cách mù quáng xung quanh một cách mù quáng, tôi đã đưa ra trường hợp tối thiểu ở trên.

Đoán ngẫu nhiên của tôi là trình biên dịch đủ thông minh để thấy rằng hai phiên bản b2CircleShape này không được sử dụng trong cùng phạm vi, vì vậy nó chỉ phân bổ không gian cho một và tái sử dụng nó. Sau khi trường hợp đầu tiên bị hủy, vtable như mong đợi, được hosed. Sau đó, đối với một số lý do khi dụ thứ hai được xây dựng, vtable không được xây dựng lại ...?

Tôi đã đưa ra hai điều để tránh sự cố, nhưng giống như tôi nói có vẻ như vấn đề trình biên dịch hơn nên tôi không đề xuất mã này cần được thay đổi.

Sửa chữa đáng ngờ số 1 là để nhận xét định nghĩa phá hủy ảo trong lớp cơ sở. Tất cả các thông tin tôi đã đọc về chủ đề này cho thấy đây không phải là câu trả lời. Tôi thích rằng trình biên dịch sẽ cung cấp một destructor mặc định ~ b2Shape() {} nếu không có gì được chỉ định, vậy tại sao kết quả lại khác nhau nếu tôi thực sự chỉ định những gì mặc định sẽ được anyway? Vâng, điều này là bên cạnh điểm thực sự ...)

Không đáng ngờ sửa chữa số 2 Tôi phát hiện ra là để loại bỏ 'nội tuyến' từ constructor sub-class . Có lẽ có một cái gì đó về vị trí mới, xây dựng nội tuyến và các trường hợp tái sử dụng trong cùng một khung ngăn xếp mà không chơi độc đáo với nhau. Một số nghiên cứu khác cho tôi biết rằng trình biên dịch tự do làm bất cứ điều gì nó thích về đề xuất 'nội tuyến', vì vậy có lẽ các trình biên dịch khác không có vấn đề này bởi vì họ đang bỏ qua 'nội tuyến'?

+0

Bạn có thể cho chúng tôi biết người phân bổ thực sự là gì không? Bạn có cơ hội phân bổ theo giá trị cho hàm nhân bản không? – Arunmu

+0

Nguồn có thể tìm thấy tại đây. http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Common/ Có vẻ như trình phân bổ không liên quan mặc dù vì tôi có thể sử dụng tính năng 'mới' điển hình hơn trong hàm Clone với cùng một kết quả. – iforce2d

+2

Chết tiệt, mã C trong C++ ngụy trang: x Tôi cho rằng bạn biết bạn đang rò rỉ bộ nhớ? (Có nó không liên quan, nhưng tôi không thể nghĩ ra bất cứ điều gì trong đoạn mã được trình bày để tạo ra hành vi như vậy). Và đối với một bình luận có liên quan: lắp ráp được tạo ra cho phương pháp được xem xét là gì? –

Trả lời

1

Tôi đã thử mã của bạn và nhận error: no matching function for call to ‘operator new(long unsigned int, void*&)‘ với phiên bản g ++ 4.5.2 ... Tôi không chắc nhưng cú pháp mới bạn sử dụng phải là nội dung ...(new (mem) b2CircleShape)

Tuy nhiên, như Matthieu đã chỉ ra, đó có lẽ không phải là những gì bạn muốn làm trong C++. Tạo một bản sao giả sử bạn có thể sao chép các đối tượng của bạn (và bạn thực hiện một bản sao trong mã của bạn) chỉ đơn giản là:

clone = new b2CircleShape(original); 
+3

'new (mem) b2CircleShape' không phải là" nội bộ ", nó là [vị trí' mới'] (http://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new) . – DCoder

0

Điều này là hiển nhiên.

A) Nếu nó hoạt động một lần như mong đợi thì tốt. B) Thực tế là lỗi xảy ra lần nữa chỉ có thể có nghĩa là lỗi trong gcc của bạn. Vâng, đúng là những thứ này có thể có lỗi. Tôi đã thấy lỗi trong tất cả các trình biên dịch ngoại trừ trình biên dịch MSVC.

Lỗi đó nếu bạn nhận được mã cho GCC hoặc Clang hoặc bất cứ điều gì và tìm nơi xảy ra lỗi sẽ do một số cờ mà nó đọc. Nếu nó hoạt động một lần và sau đó thất bại một lần nữa thì các cờ hoặc bit đã thay đổi trong dữ liệu trình biên dịch và điều đó có nghĩa là một lỗi tràn bộ nhớ hoặc lỗi đánh máy khác trong trình biên dịch.

Rất tiếc.

+1

Hài hước - Tôi chỉ thấy các lỗi trong 'cl' của Microsoft;) –

+1

Điều đó nói rằng, tôi không sử dụng nó nhiều như GCC, BCC và Delphi nhưng có lỗi ở khắp mọi nơi và đôi khi lỗi trình biên dịch đơn giản! –