TẤT CẢ các hàm ảo có cần được triển khai trong các lớp dẫn xuất không?


91

Đây có vẻ là một câu hỏi đơn giản, nhưng tôi không thể tìm thấy câu trả lời ở bất kỳ nơi nào khác.

Giả sử tôi có những thứ sau:

class Abstract {
public:
    virtual void foo() = 0;
    virtual void bar();
}

class Derived : Abstract {
public:
    virtual void foo();
}

Có ổn không khi lớp Derived không triển khai hàm bar ()? Điều gì xảy ra nếu không phải TẤT CẢ các lớp dẫn xuất của tôi đều cần hàm bar (), nhưng một số thì làm. Tất cả các hàm ảo của một lớp cơ sở trừu tượng có cần phải được triển khai trong các lớp dẫn xuất hay chỉ những hàm thuần ảo? Cảm ơn

Câu trả lời:


82

Các lớp dẫn xuất không phải tự thực hiện tất cả các chức năng ảo. Họ chỉ cần thực hiện những cái thuần túy . 1 Điều đó có nghĩa là Derivedlớp trong câu hỏi là đúng. Nó kế thừa việc bartriển khai từ lớp tổ tiên của nó Abstract,. (Điều này giả định rằng nó Abstract::barđược triển khai ở đâu đó. Mã trong câu hỏi khai báo phương thức, nhưng không xác định nó. Bạn có thể xác định nó nội tuyến khi câu trả lời của Trenki hiển thị hoặc bạn có thể định nghĩa nó riêng biệt.)


1 Và thậm chí sau đó, chỉ khi lớp dẫn xuất sẽ được khởi tạo . Nếu một lớp dẫn xuất không được khởi tạo trực tiếp, mà chỉ tồn tại như một lớp cơ sở của nhiều lớp dẫn xuất hơn, thì chính những lớp đó chịu trách nhiệm triển khai tất cả các phương thức ảo thuần túy của chúng. Lớp "giữa" trong hệ thống phân cấp được phép để lại một số phương thức ảo thuần túy không được thực hiện, giống như lớp cơ sở. Nếu lớp "trung lưu" không thực hiện một phương pháp ảo tinh khiết, sau đó hậu duệ của nó sẽ thừa kế thực hiện điều đó, vì vậy họ không cần phải tái thực hiện nó bản thân.


3
Và ngay cả điều này (thực hiện các hàm ảo thuần túy) chỉ khi chúng được dự định là cài đặt (trái ngược với bản thân là một lớp cơ sở trừu tượng).
Christian Rau

1
Đó là những gì tôi nghĩ. Nhưng tôi đang thực hiện việc này trong dự án của mình và tôi nhận được lỗi liên kết nói rằng có một "biểu tượng bên ngoài chưa được giải quyết" cho Derived :: bar (); Nhưng tôi chưa bao giờ khai báo thanh bên trong Derived, vậy tại sao trình liên kết lại tìm kiếm thân hàm?
mikestaub

1
@pixelpusher Tất nhiên Derived::barcó một cơ quan chức năng, của Abstract::bar. Vì vậy, có vẻ như đơn vị dịch nơi được xác định (nó thậm chí được xác định ở bất kỳ đâu?) Không được liên kết với đơn vị dịch nơi nó được gọi.
Christian Rau

2
@Rob: They only need to implement the pure ones.Nó gây hiểu lầm. Các lớp dẫn xuất cũng không nhất thiết phải triển khai các hàm ảo thuần túy .
Nawaz

Tôi đánh giá cao sự giúp đỡ nhưng @trenki đã đánh cây đinh vào đầu. Mặc dù bạn cũng đúng Christian Rau, vì nó KHÔNG được định nghĩa.
mikestaub

47

Chỉ các phương thức ảo thuần túy mới phải được triển khai trong các lớp dẫn xuất, nhưng bạn vẫn cần một định nghĩa (chứ không chỉ là một khai báo) của các phương thức ảo khác. Nếu bạn không cung cấp, trình liên kết rất có thể sẽ phàn nàn.

Vì vậy, chỉ cần đặt {}sau phương thức ảo tùy chọn của bạn sẽ cung cấp cho bạn một triển khai mặc định trống:

class Abstract {
public:
    virtual void foo() = 0; // pure virtual must be overridden
    virtual void bar() {}   // virtual with empty default implementation
};

class Derived : Abstract {
public:
    virtual void foo();
};

Tuy nhiên, một triển khai mặc định có liên quan hơn sẽ đi vào một tệp nguồn riêng biệt.


7

Tiêu chuẩn ISO C ++ quy định rằng tất cả các phương thức ảo của một lớp không thuần ảo phải được xác định.

Nói một cách đơn giản quy tắc là:
Nếu lớp dẫn xuất của bạn vượt quá phương thức ảo của lớp Cơ sở thì nó cũng phải cung cấp một định nghĩa, Nếu không thì lớp Cơ sở sẽ cung cấp định nghĩa của phương thức đó.

Theo quy tắc trên trong ví dụ mã của bạn, virtual void bar();cần một định nghĩa trong lớp Cơ sở.

Tài liệu tham khảo:

C ++ 03 Tiêu chuẩn: 10.3 Hàm ảo [class.virtual]

Một hàm ảo được khai báo trong một lớp sẽ được định nghĩa, hoặc được khai báo thuần túy (10.4) trong lớp đó, hoặc cả hai; nhưng không cần chẩn đoán (3.2).

Vì vậy, bạn nên làm cho hàm thuần ảo hoặc cung cấp một định nghĩa cho nó.

Tài liệu về gcc faq cũng vậy:

Tiêu chuẩn ISO C ++ chỉ định rằng tất cả các phương thức ảo của một lớp không thuần ảo phải được xác định, nhưng không yêu cầu bất kỳ chẩn đoán nào đối với các vi phạm quy tắc này [class.virtual]/8 . Dựa trên giả định này, GCC sẽ chỉ phát ra các hàm tạo được xác định ngầm, toán tử gán, hàm hủy và bảng ảo của một lớp trong đơn vị dịch xác định phương thức phi nội tuyến đầu tiên của nó.

Do đó, nếu bạn không xác định được phương pháp cụ thể này, trình liên kết có thể phàn nàn về việc thiếu định nghĩa cho các ký hiệu dường như không liên quan. Rất tiếc, để cải thiện thông báo lỗi này, có thể cần phải thay đổi trình liên kết và điều này không phải lúc nào cũng thực hiện được.

Giải pháp là đảm bảo rằng tất cả các phương thức ảo không thuần túy đều được xác định. Lưu ý rằng hàm hủy phải được định nghĩa ngay cả khi nó được khai báo là thuần ảo [class.dtor]/7.


3

Vâng, điều đó tốt ... bạn chỉ cần triển khai bất kỳ hàm ảo thuần túy nào để khởi tạo một lớp dẫn xuất từ ​​một lớp cơ sở trừu tượng.


1

Có, Đúng là một lớp Derived phải BÊN NGOÀI chức năng là Pure Virtual trong Lớp cha. Lớp cha có Hàm ảo thuần túy chỉ được gọi là Lớp trừu tượng vì nó là lớp Con phải cung cấp phần thân của Hàm thuần ảo của riêng chúng.

Đối với các Chức năng Ảo Thông thường: - Không cần thiết phải ghi đè chúng thêm, vì một số lớp con có thể có chức năng đó, một số có thể không có.

Mục đích chính của cơ chế Hàm ảo là Đa hình thời gian chạy, cho dù mục đích chính của Hàm ảo thuần túy (Lớp trừu tượng) là bắt buộc phải có cùng tên Hàm với nội dung của chính nó.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.