2012-11-19 38 views
13

Giả sử tôi có đoạn mã sau:Tại sao tôi phải khai báo lại các hàm bị ghi đè trong các lớp dẫn xuất trong C++?

class Iinterface 
{ 
    virtual void abstractFunction()=0; 
}; 

class Derived : public Iinterface 
{ 
    void abstractFunction(); // Do I need this line? 
}; 

Derived::abstractFunction() 
{ 
    // implementation here 
} 

Nếu tôi không thêm dòng trong câu hỏi, tôi nhận được biên dịch lỗi mà nói abstractFunction không được khai báo trong Derived. Tôi đang sử dụng VS 2008.
Tôi không chắc tại sao tôi cần dòng cụ thể này (không nhầm lẫn điều này với định nghĩa chức năng được cung cấp bên ngoài tuyên bố lớp học), miễn là tôi kế thừa từ Iinterface rõ ràng tôi đã tuyên bố abstractFunction. Đó có phải là một vấn đề với studio trực quan hay nó được thực thi bởi các tiêu chuẩn C++?

Trả lời

8

Nếu khai báo hàm cơ sở thuần ảo được ngụ ý trong tất cả các lớp dẫn xuất, thì bạn không bao giờ có lớp dẫn xuất vẫn trừu tượng đối với hàm cơ sở thuần ảo. Thay vào đó, tất cả các lớp dẫn xuất sẽ tạo ra lỗi liên kết. Điều đó sẽ vô cùng phản trực giác và khó hiểu, và nó sẽ làm cho ngôn ngữ ít diễn cảm hơn.

Hơn nữa, nó thậm chí sẽ không có ý nghĩa: Câu hỏi liệu lớp dẫn xuất có trừu tượng hay không phải được biết ở mọi nơi lúc biên dịch. Việc thực hiện ghi đè thường chỉ được cung cấp trong một đơn vị dịch duy nhất, do đó, sẽ không thể truyền đạt thực tế rằng bạn thực sự muốn cho hàm này bị ghi đè vào phần còn lại của chương trình.

+0

bạn đã tổng hợp nó tốt hơn nhiều :) –

+0

Tôi đồng ý, điều này có lẽ dễ hiểu hơn câu trả lời rambling của tôi, +1. – OmnipotentEntity

+0

Cho dù lớp dẫn xuất là trừu tượng hay không có thể được biết với một từ khóa "trừu tượng" rõ ràng mà C++ thiếu. –

0

Có, toàn bộ điểm của hàm ảo thuần túy là bắt buộc bạn ghi đè lên nó trong lớp dẫn xuất; tuyên bố này phải được rõ ràng trong C++

+0

Tôi biết điều đó, và tôi ** đang ** ghi đè nó, tôi chỉ không hiểu tại sao tôi cần phải viết lại nguyên mẫu của hàm trong khai báo lớp. Tại sao VS sẽ không phỏng đoán chính nó? – atoMerz

+0

VS nghĩ rằng u quên về metod đó :) – whd

+0

Bởi vì định nghĩa lớp ổ đĩa thiết kế phân cấp đối tượng, nếu bạn sẽ; như immillind giải thích ở trên, sự lựa chọn của bạn về việc có nên bao gồm khai báo override hay không cho trình biên dịch xem mã phương thức tương ứng có nên ở đó hay không. –

2
  1. : Nếu bạn muốn tạo một đối tượng của class Derived
  2. các Không: Nếu bạn muốn giữ class Derived cũng trừu tượng
  3. Không: Nếu có một lớp trung gian đã ghi đè chức năng
    ví dụ giữa IinterfaceDerivedclass Intermediate đã ghi đè abstractFunction(); vì vậy bây giờ nó là tùy chọn cho class Derived để ghi đè lên cùng

Sửa: Với tiêu đề câu hỏi thay đổi,

Tại sao tôi phải khai báo lại các chức năng ghi đè trong các lớp thừa trong C++?

Đó là bởi vì trình biên dịch C++ ngữ pháp đòi hỏi rằng tất cả các chức năng thành viên của class (hoặc namespace hoặc file) phải được khai báo bên trong class (hoặc namespace hoặc tập tin) cơ thể. Có thể là virtual hoặc chức năng bình thường.
không lý do chính đáng để vi phạm tính nhất quán đó chỉ dành cho các chức năng virtual.

+1

Tôi nghĩ rằng OP nhận thức được cơ chế ghi đè một hàm cơ sở thuần ảo. Cô ấy muốn biết lý do tại sao * khai báo * là cần thiết trong lớp dẫn xuất. –

+0

@KerrekSB Cảm ơn bạn, đó là những gì tôi có ý nghĩa chính xác. – atoMerz

+0

@KerrekSB, có vẻ như tiêu đề của câu hỏi đã được chỉnh sửa ngay bây giờ. Tôi sẽ sửa đổi câu trả lời. – iammilind

1

Chức năng kết thúc bằng =0 được gọi là deleted function, điều này rất hữu ích khi bạn không muốn đối tượng sử dụng một số nhà xây dựng nhất định (chẳng hạn như unique_ptr có ctor sao chép đã xóa).

Nếu chức năng virtual bị xóa thì tiêu chuẩn lớp sẽ trở thành một loại trừu tượng. Bởi vì trong hầu hết các trường hợp, nguyên mẫu của lớp và các hàm của lớp nằm trong các tệp riêng biệt, điều này có nghĩa là trừ khi bạn phác thảo rõ ràng nguyên mẫu mà bạn đang ghi đè hàm ảo đã xóa thì bạn KHÔNG ghi đè hàm ảo đã xóa. Trình biên dịch không phải là chỉ đơn giản là suy ra rằng bạn có nghĩa là để đặt các chức năng trong đó một khi nó thấy việc thực hiện trong một tập tin hoàn toàn khác nhau.Hãy nhớ rằng nguyên mẫu/ý tưởng triển khai không phải là cách duy nhất để viết mã, bạn cũng có thể đặt quyền thực hiện trong lớp (điều này có thể được thực hiện nếu mã đủ nhỏ và bạn muốn nội tuyến hàm này.).) Và để làm điều đó bạn cần phải một lần nữa, rõ ràng ghi đè lên chức năng ảo đã xóa. Vì vậy, bởi vì bạn cần phải ghi đè lên nó anyway, nó làm cho cảm giác hoàn hảo mà bạn cần phải ghi đè nó một cách rõ ràng trong nguyên mẫu. Chức năng vẫn bị xóa khác.

Đối với một ví dụ cụ thể: giả sử bạn có một List.hpp, List.cpp và main.cpp

Trong List.hpp bạn có một lớp trừu tượng và một lớp học thường xuyên mà kế thừa từ lớp trừu tượng. Trong chính bạn #include "List.hpp" và không phải List.cpp, phải không? Vì vậy, trình biên dịch không có IDEA gì trong tập tin đó (cho đến khi nó cố gắng biên dịch nó.) Nếu bạn không có chức năng ảo bị xóa ghi đè thì trình biên dịch nghĩ rằng bạn chỉ đơn giản là cố gắng để khởi tạo một lớp trừu tượng và ném một lỗi.

Mặt khác, nếu bạn đang biên soạn List.cpp, thì trình biên dịch cũng sẽ ném một lỗi, lần này phàn nàn rằng hàm bạn đang cố viết chưa thực sự được xác định. Bởi vì Base::deletedFunction() khác với Derived::deletedFunction().

+0

Cảm ơn bạn. Đây có phải là giới hạn trình biên dịch hay giới hạn C++ không? – atoMerz

+1

Giới hạn C++. Mặc dù tôi sẽ ngần ngại gọi đó là một hạn chế. Nó chỉ đơn giản là bạn phải rõ ràng về nó. Giúp bạn trong thời gian dài, imo. – OmnipotentEntity

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