2010-07-27 25 views
6

Cần bao nhiêu vptrs cho một đối tượng có clas (con) có thừa kế đơn với một lớp cơ sở mà nhiều thừa hưởng base1 và base2. Chiến lược để xác định có bao nhiêu vptrs một đối tượng đã cung cấp nó có vài thừa kế đơn và đa thừa kế. Mặc dù tiêu chuẩn không chỉ định về vptrs nhưng tôi chỉ muốn biết cách triển khai thực hiện chức năng ảo như thế nào.Làm thế nào nhiều vptr sẽ là một đối tượng của lớp (sử dụng đơn/đa thừa kế) có?

Trả lời

6

Tại sao bạn quan tâm? Câu trả lời đơn giản là đủ, nhưng tôi đoán bạn muốn một cái gì đó hoàn chỉnh hơn.

Đây không phải là một phần của tiêu chuẩn, do đó, mọi triển khai đều miễn phí theo ý muốn, nhưng nguyên tắc chung là trong triển khai sử dụng con trỏ bảng ảo, dưới dạng ước lượng gần nhất, cho công văn động bạn cần nhiều nhất là nhiều con trỏ tới các bảng ảo vì có các lớp mà thêm một phương thức ảo mới vào hệ thống phân cấp. (Trong một số trường hợp, các bảng ảo có thể được mở rộng, và các cơ sở và các loại có nguồn gốc chia sẻ một đơn vptr)

// some examples: 
struct a { void foo(); };   // no need for virtual table 
struct b : a { virtual foo1(); }; // need vtable, and vptr 
struct c : b { void bar(); };  // no extra virtual table, 1 vptr (b) suffices 
struct d : b { virtual bar(); }; // extra vtable, need b.vptr and d.vptr 

struct e : d, b {};     // 3 vptr, 2 for the d subobject and one for 
            // the additional b 
struct f : virtual b {}; 
struct g : virtual b {}; 
struct h : f, g {};     // single vptr, only b needs vtable and 
            // there is a single b 

Về cơ bản mỗi subobject của một loại đòi hỏi cử động riêng của mình (có thể không trực tiếp tái sử dụng các cha mẹ) sẽ cần bảng ảo và vptr của riêng nó.

Trong các trình biên dịch thực tế hợp nhất các vtables khác nhau thành một vtable đơn. Khi d thêm chức năng ảo mới trên tập hợp các hàm trong b, trình biên dịch sẽ hợp nhất hai bảng vào một bảng đơn bằng cách thêm các vị trí mới vào cuối vtable, vì vậy vtable cho d sẽ là phiên bản mở rộng của vtable cho b với các phần tử phụ ở cuối duy trì tính tương thích nhị phân (tức là d vtable có thể được hiểu là b vtable để truy cập các phương thức có sẵn trong b) và đối tượng d sẽ có một đơn vptr.

Trong trường hợp nhiều thứ thừa kế trở nên phức tạp hơn vì mỗi cơ sở cần có bố cục giống như một đối tượng con của đối tượng hoàn chỉnh hơn nếu nó là một đối tượng riêng biệt, vì vậy sẽ có thêm vptrs trỏ đến các vùng khác nhau trong vtable của đối tượng hoàn chỉnh.

Cuối cùng trong trường hợp những thứ thừa kế ảo trở nên phức tạp hơn, và có thể có nhiều vtables cho cùng đối tượng hoàn chỉnh với vptr đang được cập nhật khi tiến trình xây dựng/hủy diệt (vptr luôn được cập nhật khi tiến trình xây dựng/hủy diệt) mà không thừa kế ảo vptr sẽ trỏ đến vtables của cơ sở, trong khi trong trường hợp thừa kế ảo sẽ có nhiều vtables cho cùng một loại)

+1

"' struct d: b {virtual bar();}; // thêm vtable, cần b.vptr và d.vptr' "Tôi không nghĩ có bất kỳ trình biên dịch nào giới thiệu nhiều hơn một vptr trong một lớp với SI không ảo. – curiousguy

4

Hàng chữ in nhỏ

Bất cứ điều gì liên quan đến vptr/vtable không được xác định, vì vậy điều này sẽ được trình biên dịch phụ thuộc vào các chi tiết tốt, nhưng các trường hợp đơn giản được xử lý giống nhau bởi hầu hết các comp hiện đại iler (Tôi viết "gần như" chỉ trong trường hợp).

Bạn đã được cảnh báo.

Object bố trí: thừa kế không ảo

Nếu bạn kế thừa từ lớp cơ sở, và họ có một vptr, bạn tự nhiên có nhiều thừa hưởng vptr trong lớp học của bạn.

Câu hỏi đặt ra là: Khi nào trình biên dịch sẽ thêm vptr vào một lớp đã có vptr kế thừa?

Trình biên dịch sẽ cố gắng tránh thêm vptr dư thừa:

struct B { 
    virtual ~B(); 
}; 

struct D : B { 
    virtual void foo(); 
}; 

Đây B có vptr, vì vậy D không nhận được vptr riêng của nó, nó reuses vptr hiện có; vtable của B được gia hạn với mục nhập cho foo(). Vtable cho D là "nguồn gốc" từ vtable cho B, pseudo-code:

struct B_vtable { 
    typeinfo *info; // for typeid, dynamic_cast 
    void (*destructor)(B*); 
}; 

struct D_vtable : B_vtable { 
    void (*foo)(D*); 
}; 

Hàng chữ in nhỏ, một lần nữa: đây là một đơn giản hóa một vtable thực tế, để có được những ý tưởng.

Thừa kế ảo

Để không thừa kế đơn ảo, hầu như không có chỗ cho sự thay đổi giữa triển khai. Đối với thừa kế ảo, có nhiều biến thể hơn giữa các trình biên dịch.

struct B2 : virtual A { 
}; 

Có một chuyển đổi B2*-A*, do đó, một đối tượng B2 phải cung cấp chức năng này:

  • hoặc với một A* viên
  • hoặc với một thành viên int: offset_of_A_from_B2
  • hoặc sử dụng vptr của nó, bằng cách lưu trữ offset_of_A_from_B2 trong vtable

Nói chung, một lớp học sẽ không sử dụng lại vptr của lớp cơ sở ảo của nó (nhưng nó có thể trong một trường hợp rất đặc biệt).

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