2012-06-25 21 views
11

Giao diện (lớp đa hình chỉ với các hàm ảo thuần túy) có vtable không? Vì các giao diện không thực hiện chức năng đa hình và không thể được xây dựng trực tiếp nên sẽ không cần đến trình liên kết để đặt vtable. Là vậy sao? Im đặc biệt quan tâm đến trình biên dịch MSVC.Giao diện vtable

+0

Lưu ý quan trọng * rất * là tính năng 'declspec (novtable)' trong MSVC: nó cho phép các giao diện, đặc biệt là các giao diện COM, bỏ qua vtable. Điều này có một số ý nghĩa thú vị và quan trọng khi nói đến thừa kế (lực lượng thừa kế đơn, nhưng làm cho đối tượng cuối cùng chỉ có một bảng duy nhất cung cấp đa hình hơn so với cách khác). – ssube

Trả lời

6

Vâng, họ làm. Và có một số lý do chính đáng cho điều đó.

Lý do chính đáng đầu tiên là ngay cả các phương pháp ảo thuần túy cũng có triển khai. Hoặc ngầm hoặc rõ ràng. Nó tương đối dễ dàng để loại bỏ một thủ thuật gọi một hàm ảo thuần túy, vì vậy bạn về cơ bản có thể cung cấp một định nghĩa cho một trong số của bạn, gọi nó và xem điều gì xảy ra. Vì lý do đó, cần có một bảng ảo ở nơi đầu tiên.

Có một lý do khác để đặt một bảng ảo vào một lớp cơ sở ngay cả khi tất cả các phương thức của nó là thuần ảo và không có thành viên dữ liệu nào khác. Khi đa hình được sử dụng, một con trỏ tới một lớp cơ sở được truyền đi xung quanh chương trình. Để gọi một phương thức ảo, trình biên dịch/thời gian chạy nên tìm ra sự bù đắp tương đối của bảng ảo từ con trỏ cơ sở. Nếu C++ không có nhiều thừa kế, người ta có thể giả định một số không bù đắp từ lớp cơ sở trừu tượng (ví dụ), trong trường hợp đó nó có thể không có vtable ở đó (nhưng chúng ta vẫn cần nó vì lý do # 1). Nhưng vì có nhiều thừa kế liên quan, một thủ thuật "vtable ở đó là 0 offset" sẽ không hoạt động vì có thể có hai hoặc ba vtables tùy thuộc vào số (và kiểu) của các lớp cơ sở.

Có thể có các lý do khác mà tôi chưa từng làm.

Hy vọng điều đó sẽ hữu ích.

+0

Lập luận tốt! Đã không nghĩ về điều đó. –

+2

Bí quyết bạn đề cập đến, đó có phải là một trong số đó không yêu cầu hàm ảo thuần túy được xác định (theo các quy tắc _odr_) hay không gây ra hành vi không xác định vì tôi không biết về bất kỳ thủ thuật nào như vậy? –

+3

Tôi không bị thuyết phục bởi lý do đầu tiên của bạn. Tôi khá chắc chắn rằng cách duy nhất để gọi một hàm ảo thuần túy là (a) gọi nó là không thực, không sử dụng vtable, hoặc (b) gọi hành vi không xác định bằng cách gọi nó từ hàm tạo/destructor của một phần đối tượng được xây dựng, không bắt buộc phải gọi nó. Hay bạn có biết một mẹo khác mà tôi không? –

5

Từ quan điểm thuần túy C++, đó là câu hỏi học thuật. Các chức năng ảo không cần phải được thực hiện với vtables, nếu chúng không có cách nào để di chuyển chúng.

Nếu bạn đặc biệt quan tâm đến trình biên dịch MSVC, bạn có thể muốn trang trí các giao diện của mình với __declspec(novtable).

(Nói chung, trong việc triển khai phổ biến, một lớp trừu tượng có thể cần một vtable, ví dụ .:

struct Base { 
    Base(); 
    virtual void f() {} 
    virtual void g() = 0; 
}; 

void h(Base& b) { 
    b.f(); // Call f on a Base that is not (yet) a Derived 
      // vtable for Base required 
} 

Base::Base() { 
    h(*this); 
} 

struct Derived : Base { 
    void g() {} 
}; 

int main() { 
    Derived d; 
} 

)

+0

Tôi muốn xem làm thế nào chúng có thể được thực hiện không sử dụng các bảng ảo hoặc bất kỳ cách tiếp cận nào khác mà ít nhất sizeof (void *) phải được thêm vào một lớp phục vụ như là một "điểm khởi đầu" ... –

+1

@VladLazarenko: Tôi nghĩ rằng đó là hầu như được chấp nhận trên toàn cầu rằng một giải pháp dựa trên vptr/vtable là tối ưu nhưng việc triển khai thực thi như lưu trữ một bản đồ địa chỉ đến thông tin kiểu động của một dạng nào đó ít nhất là về mặt lý thuyết. –

+0

Bạn có điểm.Tôi nghĩ điều tôi muốn nói là dù bạn gọi nó như thế nào đi nữa, bạn sẽ cần ít nhất một điểm bắt đầu để truy cập nó, có thể là con trỏ đến vtable, một con trỏ để băm với địa chỉ hoặc băm chính nó vào một số băm chung với bản đồ con trỏ. –

1

Vtable là không cần thiết, nhưng hiếm khi được tối ưu hóa. MSVC cung cấp phần mở rộng __declspec(novtable), cho trình biên dịch một cách rõ ràng rằng vtable có thể được gỡ bỏ. Trong trường hợp không có, trình biên dịch sẽ phải kiểm tra chính nó rằng vtable không được sử dụng. Đây không phải là đặc biệt khó khăn, nhưng vẫn còn xa tầm thường. Và vì nó không cung cấp lợi ích tốc độ thực trong mã thông thường, kiểm tra không được thực hiện trong bất kỳ trình biên dịch nào mà tôi biết.