2010-06-02 42 views
24
#include <iostream> 
using namespace std; 

class Duck { 
public: 
     virtual void quack() = 0; 
}; 

class BigDuck : public Duck { 
public: 
    // void quack(); (uncommenting will make it compile) 

}; 

void BigDuck::quack(){ cout << "BigDuckDuck::Quack\n"; } 

int main() { 
     BigDuck b; 
     Duck *d = &b; 
     d->quack(); 

} 

Đoạn mã trên không biên dịch. Tuy nhiên, khi tôi khai báo hàm ảo trong lớp con, thì nó biên dịch tốt.Tại sao tôi phải redeclare một chức năng ảo trong khi ghi đè [C++]

Nếu trình biên dịch đã có chữ ký của hàm mà lớp con sẽ ghi đè, thì tại sao cần phải xác nhận lại?

Mọi thông tin chi tiết?

+2

Bạn gặp lỗi trình biên dịch nào? –

+0

Mặc dù trong ví dụ này chúng ta thấy một lớp cơ sở trừu tượng, câu hỏi là hợp lệ nói chung, quá. – xtofl

+0

Tôi có thể, mặc dù không biết bạn đang sử dụng trình biên dịch nào, tôi có thể không nhận được cùng một lỗi (hoặc bất kỳ) nào :) –

Trả lời

21

Các khai báo lại là cần thiết vì:

  • Tiêu chuẩn nói như vậy.
  • Nó làm cho công việc của trình biên dịch dễ dàng hơn bằng cách không leo lên hệ thống phân cấp để kiểm tra xem chức năng đó có tồn tại hay không.
  • Bạn có thể muốn khai báo nó thấp hơn trong cấu trúc phân cấp.
  • Để khởi tạo lớp, trình biên dịch phải biết rằng đối tượng này là cụ thể.
+0

Cảm ơn! Điều đó có ý nghĩa. –

+0

@Matt Ellen: Không có vấn đề –

+1

The = 0; ở cuối khai báo có nghĩa là bạn phải định nghĩa nó trong lớp dẫn xuất. Không phải là bạn có thể muốn. –

2

Định nghĩa của Quack() trong lớp cơ sở của bạn là "trừu tượng" - không có triển khai. Điều này cho trình biên dịch biết rằng lớp dẫn xuất của bạn phải triển khai nó. Không làm như vậy là lỗi biên dịch.

+0

Nhưng chắc chắn trình biên dịch biết rằng nó sẽ được triển khai. Tuyên bố trích xuất phục vụ mục đích gì? –

+0

@Matt Ellen: Xem câu trả lời của tôi –

+0

@Matt Tiêu chuẩn ngôn ngữ nói nó là bắt buộc - điều này không liên quan gì đến các lớp trừu tượng, tất cả các chức năng ảo (và tất cả các chức năng) đều hoạt động theo cách đó. –

2

BigDuck có thể là một lớp trừu tượng khác và bạn có thể không muốn triển khai quack cho đến khi bạn đến lớp cơ sở ReallyBigDuck.

+0

Bây giờ đó là một lý lẽ tốt để có thể _not_ khai báo 'quack'. Nhưng đối số cho _having_ để khai báo nó là gì? – xtofl

1

Cho đến khi bạn cung cấp triển khai, các lớp sẽ kế thừa từ một lớp có chứa PVF là trừu tượng - chúng không thể được khởi tạo. Để cung cấp một triển khai như vậy, bạn phải khai báo hàm trong lớp.

1

Khai báo các phương thức trong mỗi lớp sẽ cho trình biên dịch biết rằng lớp cung cấp cách triển khai khác nhau cho phương thức.

Ngoài ra, trong trường hợp bạn muốn tạo đối tượng BigDuck trên ngăn xếp thì trình biên dịch sẽ biết chữ ký của quack() như thế nào.

BigDuck aDuck; 
aDuck.quack(); 
5

Vì C++ tách 'khai báo' khỏi 'đa hình': bất kỳ hàm nào cần khai báo cho trình biên dịch, bất kể nó có ảo hay không.

Ví dụ của bạn không đi đủ xa, nó có vấn đề 'lớp trừu tượng': một BigDuck không thể được khởi tạo vì nó không thực hiện quack trong giao diện của nó.

Khái quát vấn đề, chúng ta có thể khai báo hàm cơ sở không tinh khiết ảo:

class Duck { public: virtual void quack(){} }; 

class BigDuck : public Duck {}; 
void BigDuck::quack(){ cout << "QUACK!"; }//overrides, but doesn't declare 

Tại đây, trình biên dịch sẽ phàn nàn rằng nó có một biểu tượng BigDuck::quack mà không được công bố. Điều này không liên quan gì đến các lớp trừu tượng hay bất cứ thứ gì.

(Lưu ý: gcc nói: error: no 'void BigDuck::q()' member function declared in class 'BigDuck' )

12

Nếu bạn thay đổi:

virtual void quack() = 0; 

để

virtual void quack(); 

Nó sẽ biên dịch mà không thực hiện quack() trong HugeDuck.

the = 0; ở cuối khai báo chức năng cơ bản nói rằng tất cả BigDucks sẽ quack, nhưng nó phải được thực hiện bởi mỗi vịt có nguồn gốc. Bằng cách loại bỏ = 0; Cướp BigDuck sẽ được gọi trừ khi bạn thực hiện quack trong HugeDuck.

EDIT: Để làm rõ = 0; đang nói rằng lớp dẫn xuất sẽ có định nghĩa cho hàm. Trong ví dụ của bạn, nó được mong đợi HugeDuck để xác định quack(), nhưng như bạn có nó nhận xét nó ra nó không.

Như một lưu ý phụ, vì tất cả vịt có thể quack có lẽ lớp vịt ban đầu của bạn mà chúng ta không thể thấy nên thực hiện quack() thay thế?

+0

Là 'HugeDuck == BigDuck'? Đây có phải là lớp dẫn xuất thứ hai mà bạn đang đề xuất hay một lỗi đánh máy? (hoặc một lớp dẫn xuất của 'BigDuck', hay cái gì khác)? –

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