2012-09-26 76 views
6

Vì vậy, tôi có một lớp cơ sở trừu tượng không có phương thức trừu tượng. Để thực thi tính trừu tượng, tôi đã tuyên bố trình phá hoại (không tầm thường) là thuần túy ảo:Thừa kế ảo thuần túy, đa thừa kế và C4505

class AbstractClass 
{ 
public: 
    AbstractClass() 
    { 
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl; 
    } 
    virtual ~AbstractClass() = 0 
    { 
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl; 
    } 
}; 

class ConcreteClass : public AbstractClass 
{ 
public: 
    ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl; 
    } 
    virtual ~ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl; 
    } 
}; 

Điều này xây dựng và hoạt động như mong đợi; đầu ra cho một khối mã mà chỉ đơn giản định nghĩa một thể hiện của ConcreteClass là

 

    AbstractClass::AbstractClass() 
    ConcreteClass::ConcreteClass() 
    ConcreteClass::~ConcreteClass() 
    AbstractClass::~AbstractClass() 

Bây giờ, khi tôi có lấy được AbstractClass từ một lớp được sử dụng như một lớp giao diện, bản thân có một (tầm thường) destructor ảo (tinh khiết hoặc không) , nó vẫn hoạt động:

class IAlpha 
{ 
public: 
    virtual ~IAlpha() = 0 {} 
}; 

class AbstractClass : public IAlpha 
{ 
public: 
    AbstractClass() 
    { 
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl; 
    } 
    virtual ~AbstractClass() = 0 
    { 
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl; 
    } 
}; 

class ConcreteClass : public AbstractClass 
{ 
public: 
    ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl; 
    } 
    virtual ~ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl; 
    } 
}; 

vấn đề nảy sinh khi tôi cố gắng để thực hiện hai giao diện khác nhau theo cách này:

class IAlpha 
{ 
public: 
    virtual ~IAlpha() = 0 {} 
}; 

class IBeta 
{ 
public: 
    virtual ~IBeta() = 0 {} 
}; 

class AbstractClass : public IAlpha, public IBeta 
{ 
public: 
    AbstractClass() 
    { 
    std::wcout << L"AbstractClass::AbstractClass()" << std::endl; 
    } 
    virtual ~AbstractClass() = 0 
    { 
    std::wcout << L"AbstractClass::~AbstractClass()" << std::endl; 
    } 
}; 

class ConcreteClass : public AbstractClass 
{ 
public: 
    ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::ConcreteClass()" << std::endl; 
    } 
    virtual ~ConcreteClass() 
    { 
    std::wcout << L"ConcreteClass::~ConcreteClass()" << std::endl; 
    } 
}; 

tại thời điểm này, khi bu ilding, tôi nhận được cảnh báo sau:

warning C4505: 'AbstractClass::~AbstractClass' : 
unreferenced local function has been removed

Lạ kỳ, tuy nhiên, đầu ra vẫn hiển thị AbstractClass::~AbstractClass() được gọi.

Đây có phải là lỗi trong MSVC9 (VS 2008) không? Tôi có thể bỏ qua cảnh báo này một cách an toàn không?

Chỉnh sửa: Tôi đã thử tách các định nghĩa phương thức ảo thuần túy khỏi định nghĩa lớp học, vì rõ ràng cú pháp = 0 {} không thực sự hợp lệ. Thật không may, C4505 vẫn hiển thị, cho dù tôi có chỉ định inline hay không.

Vì tôi đã không tìm thấy cách nào để cảnh báo này chỉ cho các phương thức này được bảo vệ. Không phải là một giải pháp lý tưởng, nhưng nó đập rearchitecting phân cấp lớp để có được xung quanh một cảnh báo sai lầm.

Trả lời

0

Bạn đã thử xác định các trình phá hủy theo cách không nội tuyến? Có lẽ cảnh báo có liên quan đến điều đó.

Chẳng hạn như this code.

+0

Tôi đã thử mã trong liên kết của bạn; nó nhận được cùng một cảnh báo mặc dù, như trước đây, nó hoạt động chính xác khi chạy. – somethingdotjunk

0

Mã không được biên dịch. Các hàm ảo thuần túy không thể được định nghĩa bên trong định nghĩa lớp. Di chuyển định nghĩa ra ngoài các lớp học:

struct IAlpha { 
    virtual ~IAlpha() = 0; 
}; 
inline IAlpha::~IAlpha() {} 
// similarly for the rest. 

Bên cạnh đó, mã là chính xác và nên biên dịch.

+0

Thú vị; Tôi không biết rằng bạn không thể kết hợp bộ định danh ảo thuần túy và phần thân phương thức. Thật không may, tôi đã thử nó theo cách này là tốt và cảnh báo vẫn hiển thị (xem câu trả lời từ jeffmagill). – somethingdotjunk

3

Đây là lỗi trong MSVC++ 2010 trở về trước. Mã thực sự được gọi là được gọi là mặc dù trình biên dịch tuyên bố đã xóa mã. Nó có vẻ là cố định trong MSVC++ 2012. Các trình biên dịch khác như gcc hoặc clang không phát ra cảnh báo. Cú pháp "... = 0 {...}" là bất hợp pháp theo tiêu chuẩn C++ 03 phần 10.4.2 (mặc dù MSVC++ không phàn nàn) vì nó đã được chỉ ra:

Lưu ý: phần khai báo hàm không thể cung cấp cả một tinh khiết-specifier và một định nghĩa

Tuy nhiên, việc xác định một tinh khiết destructor ảo nói chung không phải là bất hợp pháp và phần 12.4.7 tiểu bang:

Một destructor có thể được khai báo ảo (10.3) hoặc ảo thuần (10.4); nếu bất kỳ đối tượng nào của lớp đó hoặc bất kỳ lớp dẫn xuất nào được tạo trong chương trình , thì hàm hủy sẽ được xác định. Nếu một lớp có một lớp cơ sở là với một destructor ảo, thì hàm hủy của nó (cho dù người dùng hoặc được khai báo ngầm) là ảo.

cách của tôi về việc vô hiệu hóa các cảnh báo là thêm các dòng sau vào tiêu đề:

#if defined(_MSC_VER) && (_MSC_VER <= 1600) 
# pragma warning(disable:4505) 
#endif 

Nếu bạn muốn vô hiệu hóa các cảnh báo tại địa phương nhiều hơn thì #pragma warning(push)#pragma warning(pop) có thể giúp đỡ. Xem http://msdn.microsoft.com/en-us/library/2c8f766e(v=vs.80).aspx

Vì mã dường như được gọi là bạn có thể bỏ qua các cảnh báo theo ý kiến ​​của tôi.

+0

Tôi muốn được biết nơi mà lỗi này đã được báo cáo để tôi có thể trích dẫn nó. – chappjc

0

Bạn không thể xác định hàm ảo thành nội tuyến.

Vì nội tuyến đang được biên dịch. Ảo trong thời gian chạy.