2010-01-13 31 views
6

Có vấn đề nào với việc ghi đè một phần các hàm ảo được xác định bởi một lớp cơ sở không?Các vấn đề với phần ghi đè chức năng một phần trong C++

trình biên dịch của tôi cung cấp các cảnh báo sau đây:

overloaded virtual function "MyBaseClass::setValue" is only partially overridden in class "MyDerivedClass". 

Các lớp trông như thế này:

class MyBaseClass 
{ 
public: 
    virtual void setValue(int); 
    virtual void setValue(SpecialType*); 
} 

class MyDerivedClass : public MyBaseClass 
{ 
public: 
    virtual void setValue(int); 
} 

Cách đơn giản để thoát khỏi cảnh báo này là sử dụng tên gọi khác nhau cho các chức năng cơ bản, nhưng tôi muốn biết nếu có bất kỳ lý do thuyết phục nào để khắc phục cảnh báo cụ thể này. Tôi không tin điều này vi phạm tiêu chuẩn C++. Tôi đoán là để cảnh báo một lập trình viên rằng họ có thể đã quên thực hiện hành vi cho tất cả các kiểu đầu vào có thể. Trong trường hợp của chúng tôi, nó là cố ý loại trừ một số loại cụ thể.

Bạn có ngăn chặn hoàn toàn cảnh báo này không?

+0

Nếu bạn đang cân nhắc sử dụng tên gọi khác nhau cho các chức năng cơ bản, bằng mọi cách làm như vậy. Điều đó gợi ý với tôi rằng MyBaseClass :: setValue (int) thực hiện một điều khái niệm khác với MyDerivedClass :: setValue (int), và điều đó không tốt. Các chức năng ảo phải làm điều tương tự trong cơ sở và có nguồn gốc, hoặc bạn có thể nhận được các lỗi khó tìm thấy từ những thay đổi nhỏ. –

+0

Trong trường hợp cụ thể của tôi, họ đang làm khái niệm điều tương tự. Việc đổi tên có thể là một cái gì đó như setValueFromInt() setValueFromSpecialType() mà sẽ không trông sạch sẽ. Tôi muốn sử dụng các câu lệnh được mô tả trong một trong các câu trả lời. Hiện đang điều tra triển khai phương pháp tiếp cận đó. –

Trả lời

13

Các ghi đè cho setValue(int) ẩn setValue(SpecialType*) của lớp cơ sở (xem C++ FAQ Lite), vì vậy nếu bạn cố gắng gọi setValue(new SpecialType()) bạn sẽ nhận được một lỗi.

Bạn có thể tránh điều này bằng cách thêm một chỉ thị using đến lớp được thừa kế đó "nhập khẩu" quá tải từ lớp cơ sở:

class MyDerivedClass : public MyBaseClass 
{ 
public: 
    using MyBaseClass::setValue; 
    virtual void setValue(int); 
}; 
+0

+1 để thực sự hiển thị cách KHÔNG viết lại các phiên bản bạn không quan tâm đến chuyên môn. –

+0

Tuyên bố sử dụng hoạt động cho tôi. Tôi đang phát triển cho nhiều nền tảng. Tôi gặp sự cố khi tải Microsoft Visual Studio 6.0 để khiếu nại về vấn đề mã cụ thể này. Ngay cả với mức cảnh báo nghiêm ngặt nhất, tôi cũng không thể khiếu nại được. Bất cứ ai với một ý tưởng làm thế nào để có được trình biên dịch này để nhổ ra một cảnh báo? –

6

Cảnh báo là chính xác, được gọi là "ẩn tên". Biến loại MyDerivedClass không thể gọi số setValue(SpecialType*).


Bây giờ tôi sẽ ngang nhiên xé toạc someone else's blog:

quá tải và tên trốn trong C++

Trong một cuộc trò chuyện điện thoại với Brad đêm qua, ông nói với tôi về một kỳ lạ vấn đề anh gặp phải trong công việc C++ mới của mình. Cấp, nó có lẽ không có vấn đề lớn đối với những người có trải nghiệm C++ rộng lớn, nhưng đối với những người trong chúng ta sống trong thế giới mã được quản lý, điều này có vẻ lạ.

Trong C++, khi bạn có một lớp với phương thức quá tải (hàm thành viên, bất cứ điều gì bạn muốn gọi), sau đó bạn mở rộng và ghi đè phương thức đó, bạn phải ghi đè lên tất cả các phương thức quá tải.

Tôi hiểu trường hợp bạn đã thay đổi chữ ký phương thức trong lớp con, do đó làm mất hiệu lực giao diện đã thiết lập. Trong trường hợp này, mặc dù, nó có vẻ phản trực giác, vì bạn không thay đổi giao diện, nhưng có chọn lọc ghi đè. Đó là khác nhau.

Ví dụ:

class FirstClass 
{ 
public: 
     virtual void MethodA (int); 
     virtual void MethodA (int, int); 
}; 

void FirstClass::MethodA (int i) 
{ 
    std::cout << "ONE!!\n"; 
} 

void FirstClass::MethodA (int i, int j) 
{ 
    std::cout << "TWO!!\n"; 
} 

Simple lớp ở đây với hai phương pháp (hoặc một phương pháp quá tải). Bạn muốn ghi đè lên phiên bản hai tham số, vì vậy bạn tiếp tục với những điều sau:

class SecondClass : public FirstClass 
{ 
public: 
    void MethodA (int); 
}; 

void SecondClass::MethodA (int i) 
{ 
    std::cout << "THREE!!\n"; 
} 

Bây giờ, khi bạn sử dụng một thể hiện của SecondClass, hầu hết Java hoặc C# lập trình viên có thể giả định bạn có thể gọi:

int main() 
{ 
    SecondClass a; 
    a.MethodA (1); 
    a.MethodA (1, 1); 
} 

Tuy nhiên, cuộc gọi thứ hai sẽ không làm việc, kể từ khi hai tham số MethodA là không nhìn thấy được. Bạn có thể nhận được một con trỏ và up-cast để FirstClass, nhưng trường hợp SecondClass của bạn không kế thừa các phương thức không ghi đè trực tiếp.

0

Rõ ràng là trình biên dịch muốn cảnh báo bạn: bạn đã tạo một lớp con hoạt động khác khi đưa ra một số int, nhưng bạn đã không thay đổi hành vi của nó khi tặng nó SpecialType*.

Mặc dù điều này có thể là là ý định, rất có thể hành vi đã thay đổi cũng cần thiết cho các chức năng ảo bị quá tải khác.

Tôi muốn trình biên dịch đã cảnh báo cho tôi khó khăn hơn, thời gian tôi bỏ qua nó! Phương pháp ghi đè của tôi hóa ra và biên dịch tốt trong kịch bản của tôi, nhưng một số kịch bản khác đã thực sự sai do quá tải không bị ghi đè.

Suy nghĩ kỹ trước khi bạn tắt cảnh báo đó!

Nếu bạn muốn hành vi ban đầu giữ, thật dễ dàng để chỉ gọi hàm cha mẹ:

class MyDerivedClass : public MyBaseClass { 
    virtual void setValue(int); 
    // explicit: keep original behavior for SpecialType 
    virtual void setValue(SpecialType* p) { MyBaseClass::setValue(p); } 
}; 
+0

Tôi hiểu rằng cảnh báo rất quan trọng và đang cố gắng cảnh báo cho tôi về điều gì đó có thể xảy ra sai. Mục đích của câu hỏi của tôi là tìm hiểu tại sao cảnh báo lại quan trọng đối với nhà thiết kế để bắt đầu và những gì cạm bẫy có thể xảy ra bằng cách bỏ qua/im lặng nó. –

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