Nó thường hữu ích từ góc độ thiết kế để có thể đánh dấu mọi thứ là không thay đổi. Theo cách tương tự, bộ const
cung cấp trình bảo vệ trình biên dịch và chỉ ra rằng trạng thái không nên thay đổi, final
có thể được sử dụng để chỉ ra rằng hành vi không nên thay đổi bất kỳ phân cấp kế thừa nào nữa.
Thí dụ
Hãy xem xét một trò chơi video trong đó các phương tiện đưa người chơi từ vị trí này sang vị trí khác. Tất cả các phương tiện nên kiểm tra để đảm bảo rằng họ đang di chuyển đến một địa điểm hợp lệ trước khi khởi hành (đảm bảo căn cứ tại địa điểm đó không bị phá hủy, ví dụ). Chúng ta có thể bắt đầu sử dụng thành ngữ giao diện không ảo (NVI) để đảm bảo rằng kiểm tra này được thực hiện bất kể phương tiện.
class Vehicle
{
public:
virtual ~Vehicle {}
bool transport(const Location& location)
{
// Mandatory check performed for all vehicle types. We could potentially
// throw or assert here instead of returning true/false depending on the
// exceptional level of the behavior (whether it is a truly exceptional
// control flow resulting from external input errors or whether it's
// simply a bug for the assert approach).
if (valid_location(location))
return travel_to(location);
// If the location is not valid, no vehicle type can go there.
return false;
}
private:
// Overridden by vehicle types. Note that private access here
// does not prevent derived, nonfriends from being able to override
// this function.
virtual bool travel_to(const Location& location) = 0;
};
Bây giờ hãy nói rằng chúng ta có phương tiện bay trong trò chơi của chúng ta, và một điều mà tất cả các phương tiện bay yêu cầu và có điểm chung là chúng phải trải qua kiểm tra an toàn bên trong nhà chứa máy bay trước khi cất cánh.
Ở đây chúng ta có thể sử dụng final
để đảm bảo rằng tất cả các phương tiện bay sẽ trải qua quá trình kiểm tra như vậy và cũng truyền đạt yêu cầu thiết kế này của các phương tiện bay.
class FlyingVehicle: public Vehicle
{
private:
bool travel_to(const Location& location) final
{
// Mandatory check performed for all flying vehicle types.
if (safety_inspection())
return fly_to(location);
// If the safety inspection fails for a flying vehicle,
// it will not be allowed to fly to the location.
return false;
}
// Overridden by flying vehicle types.
virtual void safety_inspection() const = 0;
virtual void fly_to(const Location& location) = 0;
};
Bằng cách sử dụng final
theo cách này, chúng tôi sắp xếp một cách hiệu quả tính linh hoạt của thành ngữ giao diện không ảo để cung cấp hành vi thống nhất xuống hệ thống phân cấp thừa kế (ngay cả khi suy nghĩ lại, chống lại vấn đề lớp cơ sở mong manh) cho chính các hàm ảo. Hơn nữa, chúng tôi tự mua phòng ngọ nguậy để thực hiện các thay đổi trung tâm ảnh hưởng đến tất cả các loại phương tiện bay như một cách suy nghĩ mà không sửa đổi từng triển khai phương tiện bay đang tồn tại.
Đây là một ví dụ như vậy của việc sử dụng final
. Có những bối cảnh bạn sẽ gặp phải khi đơn giản là nó không có ý nghĩa gì khi chức năng thành viên ảo bị ghi đè thêm nữa - làm như vậy có thể dẫn đến một thiết kế dễ vỡ và vi phạm các yêu cầu thiết kế của bạn.
Đó là nơi final
hữu ích từ quan điểm thiết kế / kiến trúc.
Nó cũng hữu ích từ quan điểm của trình tối ưu hóa vì nó cung cấp cho trình tối ưu hóa thông tin thiết kế này cho phép nó làm mờ các cuộc gọi chức năng ảo (loại bỏ chi phí gửi động, và thường là đáng kể hơn, loại bỏ rào cản tối ưu hóa giữa người gọi và callee).
Câu hỏi
Từ các ý kiến:
Tại sao cuối cùng và ảo sẽ được sử dụng cùng một lúc?
Nó không có nghĩa đối với một lớp cơ sở ở gốc của một hệ thống phân cấp để khai báo một hàm là cả hai virtual
và final
. Điều đó có vẻ khá ngớ ngẩn đối với tôi, vì nó sẽ khiến cả trình biên dịch và người đọc phải nhảy qua các vòng không cần thiết có thể tránh được bằng cách đơn giản là tránh virtual
hoàn toàn trong trường hợp như vậy. Tuy nhiên, các lớp con kế thừa các hàm thành viên ảo như vậy:
struct Foo
{
virtual ~Foo() {}
virtual void f() = 0;
};
struct Bar: Foo
{
/*implicitly virtual*/ void f() final {...}
};
Trong trường hợp này, có hay không Bar::f
sử dụng từ khóa ảo một cách rõ ràng, Bar::f
là một chức năng ảo. Các virtual
từ khóa sau đó trở thành tùy chọn trong trường hợp này. Vì vậy, nó có thể có ý nghĩa cho Bar::f
được quy định như final
, mặc dù nó là một hàm ảo ( final
có thể chỉ được sử dụng cho các chức năng ảo).
Và một số người có thể thích, theo phong cách, để chỉ rõ rằng đó Bar::f
là ảo, như vậy:
struct Bar: Foo
{
virtual void f() final {...}
};
Đối với tôi, việc sử dụng cả hai virtual
và final
chỉ định cho cùng một chức năng trong bối cảnh này (tương tự virtual
và override
) là một vấn đề dư thừa , nhưng đó là vấn đề về phong cách trong trường hợp này. Một số người có thể thấy rằng virtual
truyền đạt một cái gì đó có giá trị ở đây, giống như sử dụng extern
để khai báo hàm với liên kết ngoài (mặc dù tùy chọn này thiếu các vòng loại liên kết khác).