2012-01-23 35 views
16

xem xét ví dụ dưới đây:Override một hàm thành viên với nhau trở lại loại

#include <iostream> 

using namespace std; 

class base 
{ 
    public: 
     virtual int func() 
     { 
     cout << "vfunc in base class\n"; 
     return 0; 
     } 
}; 

class derived: public base 
{ 
    public: 
     double func() 
     { 
     cout << "vfunc in derived class\n"; 
     return 0; 
     } 
}; 

int main() 
{ 
    base *bptr = new derived; 
    bptr->func(); 

    return 0; 
} 

Trình biên dịch đưa ra một lỗi cho các mã trên rằng có loại mâu thuẫn với chức năng ghi đè. Tại sao không thể ghi đè lên một hàm trong lớp dẫn xuất với một kiểu trả về khác?

Tôi tin rằng, theo thứ tự để ghi đè lên một hàm, phương thức ảo lớp cơ sở cần phải được định nghĩa lại trong lớp dẫn xuất. Để xác định lại một phương thức, chữ ký của các phương thức phải giống nhau. Vì kiểu trả về không phải là một phần của chữ ký, tôi tin rằng ngay cả khi có sự khác biệt về kiểu trả về, phương thức vẫn sẽ được định nghĩa lại? Trong trường hợp đó đối với mã ở trên, hàm ảo func được định nghĩa lại trong lớp dẫn xuất có kiểu trả về khác. Nhưng trình biên dịch ném một lỗi. Tôi hiểu có đúng không?

+0

Vì làm rõ, những gì trình biên dịch được tạo cho bạn lỗi gì? –

+0

@SionSheevok, GCC có ít nhất: http://codepad.org/z7rXpCeK – bdonlan

+0

@SionSheevok: Tôi đang sử dụng gcc 3.4.6 –

Trả lời

17

Ghi đè về cơ bản có nghĩa là phương pháp lớp cơ sở hoặc phương thức lớp gốc sẽ được gọi vào thời gian chạy tùy thuộc vào đối tượng thực được trỏ bởi con trỏ .
Nó ngụ ý rằng:
tức là: Mỗi nơi mà phương thức lớp cơ sở có thể được gọi có thể được thay thế bằng cách gọi phương thức lớp gốc mà không có bất kỳ thay đổi nào đối với mã gọi. Để đạt được điều này, cách duy nhất có thể là hạn chế các kiểu trả về của các phương thức ảo quan trọng để trả về cùng kiểu với lớp cơ sở hoặc kiểu bắt nguồn từ đó (các kiểu trả về cùng biến thể) và các tiêu chuẩn thực thi. tình trạng này.

Nếu điều kiện trên không đúng chỗ, nó sẽ để lại một cửa sổ để ngắt mã hiện tại bằng cách thêm chức năng mới.

+2

Als: 'type bắt nguồn từ đó (các kiểu trả về cùng biến thể)'. Điều đó rất quan trọng đối với sự hiểu biết của tôi. Nice lời giải thích. :) –

7

Để ghi đè hàm ảo, giá trị trả về phải giống hệt *. C++ sẽ không phải tự động chuyển đổi giữa doubleint ở đây - sau khi tất cả, làm thế nào nó sẽ biết loại trả về bạn muốn khi gọi từ một con trỏ lớp dẫn xuất? Lưu ý rằng nếu bạn thay đổi một phần của chữ ký (tham số, const-ness, vv), thì bạn cũng có thể thay đổi giá trị trả về.

* - nói đúng, nó phải là 'biến thể'. Điều này có nghĩa là loại bạn trả về phải là một tập con của kiểu trả về của hàm cha. Ví dụ: nếu lớp cha trả về một số base *, bạn có thể trả lại derived *. Vì derived s đồng thời cũng là base s, trình biên dịch cho phép bạn ghi đè theo cách này. Nhưng bạn không thể trả lại các loại hoàn toàn không liên quan như intdouble; chỉ vì có một chuyển đổi ngầm không có nghĩa là trình biên dịch sẽ cho phép bạn thực hiện loại ghi đè này.

+0

'Lưu ý rằng nếu bạn thay đổi một phần của chữ ký (tham số, const-ness, vv), thì bạn cũng có thể thay đổi giá trị trả về'. Nếu tôi làm điều đó thì chức năng không được ghi đè đúng không? –

+3

Chính xác như vậy hoặc một phân lớp, tôi nghĩ bạn có ý nghĩa. – Nemo

+1

loại trả về 'co-variant' được phép cho các chức năng ảo. –

1

Xem this question. Để tóm tắt, bạn chỉ có thể ghi đè lên một hàm ảo bằng cách sử dụng một kiểu trả về khác nếu các kiểu là covariant.

+0

* Các loại * không phải là biến thể, * ghi đè * là. –

+0

Làm cách nào tôi liên kết có được ngữ nghĩa sai? – ezod

+0

Không, chỉ giải thích của bạn về nó là một chút phong cách riêng. –

0

Ghi đè không thể sở hữu, vì chữ ký khác nhau. Mục đích cơ bản của ghi đè là đa hình nhưng không thể trong ví dụ trên

+1

Chữ ký giống nhau ở đây. Các hàm chỉ khác nhau về kiểu trả về và kiểu trả về không phải là một phần của chữ ký. –

+0

@LinuxPenseur cảm ơn vì đã nói, đó là những gì tôi dự định có nghĩa là – kvk

1

Nếu bạn muốn ghi đè, bạn nên cố gắng sử dụng mẫu.

Xem sau:

#include <iostream> 

using namespace std; 

class base 
{ 
    public: 
     template<typename X> X func() 
     { 
     cout << "vfunc in base class\n"; 
     return static_cast<X>(0); 
     } 
};  

class derived: public base 
{ 
    public: 
     template<typename X> X func() 
     { 
     cout << "vfunc in derived class\n"; 
     return static_cast<X>(2); 
     } 
};  

int main() 
{ 
    derived *bptr = new derived; 
    cout << bptr->func<int>() << endl; 
    cout << dynamic_cast<base*>(bptr)->func<int>() << endl; 

    derived *bptr2 = new derived; 
    cout << bptr->func<double>() << endl; 
    cout << dynamic_cast<base*>(bptr)->func<int>() << endl; 


    return 0; 
} 

Tất nhiên, bạn không cần phải khai báo nó trên hai lớp khác nhau theo cách đó, bạn có thể làm:

class base 
{ 
    public: 
     int func() 
     { 
     cout << "vfunc in base class\n"; 
     return 0; 
     } 

     double func(){ 
     cout << "vfunc for double class\n"; 
     return 2.; 

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