2010-05-25 38 views
5

Đây là thêm một sự tò mò hơn bất cứ điều gì khác ...Trình biên dịch lưu trữ các phương thức cho các lớp C++ ở đâu?

Giả sử tôi có một lớp C++ Kitty như sau:

class Kitty 
{ 
    void Meow() 
    { 
     //Do stuff 
    } 
} 

Liệu các nơi trình biên dịch mã cho Meow() trong mỗi trường hợp của Kitty?

Rõ ràng lặp lại cùng một mã ở mọi nơi đòi hỏi nhiều bộ nhớ hơn. Nhưng mặt khác, việc phân nhánh đến một vị trí tương đối trong bộ nhớ lân cận đòi hỏi ít hướng dẫn lắp ráp hơn là phân nhánh tới vị trí tuyệt đối trong bộ nhớ trên các bộ vi xử lý hiện đại, vì vậy điều này có khả năng nhanh hơn.

Tôi cho rằng đây là chi tiết triển khai, do đó các trình biên dịch khác nhau có thể hoạt động khác nhau.

Xin lưu ý rằng tôi không xem xét các phương pháp tĩnh hoặc ảo tại đây.

+0

Hãy sẵn sàng cho sự tấn công của câu trả lời cho câu hỏi thường gặp (mặc dù +1, nó * là * một câu hỏi hay :)) .. –

+0

Để làm rõ, tôi không quan tâm đến nội tuyến. Tôi biết cách hoạt động. – Mashmagar

Trả lời

3

Tôi tin rằng cách tiêu chuẩn cho các phương pháp ví dụ sẽ được thực hiện giống như bất kỳ phương pháp tĩnh nào, chỉ một lần, nhưng có con trỏ this được truyền trên thanh ghi cụ thể hoặc trên ngăn xếp để thực hiện cuộc gọi.

1

Không, trình biên dịch chỉ tạo mã cho Meow một lần và mỗi trường hợp sử dụng Kitty cung cấp thành viên được biên dịch ngoài dòng. Nếu trình biên dịch có thể và chọn nội tuyến chức năng thì nó sẽ được nhân bản tại mỗi điểm sử dụng (thay vì với mỗi trường hợp của Kitty).

4

Trong quá trình triển khai thông thường, chỉ có một bản sao của bất kỳ chức năng cụ thể nào. Sự liên kết giữa mã và dữ liệu cho một cá thể đối tượng đã cho được thiết lập bằng cách chuyển một tham số ẩn (được gọi là this trong hàm), đó là một con trỏ tới cá thể đối tượng (và dữ liệu của nó).

Đối với các chức năng ảo, mọi thứ trở nên phức tạp hơn: mỗi lớp nhận được vtable chứa một bộ con trỏ tới các hàm ảo và mỗi đối tượng sẽ trỏ đến vtable cho lớp của nó. Các hàm ảo được gọi bằng cách tìm con trỏ vtable, nhìn vào offset chính xác, và gọi hàm được trỏ tới bởi con trỏ đó.

+0

Nếu tôi hiểu bạn một cách chính xác, dưới mui xe nó đang làm những gì Python rõ ràng làm trên mọi chức năng thành viên, yêu cầu tham chiếu đến cá thể. Vâng? – Mashmagar

+0

Bất kể bạn sử dụng con trỏ 'this' nào khi sử dụng hàm thành viên hoặc biến thành viên, nó luôn được sử dụng. Trong trường hợp các hàm thành viên, điều đó có nghĩa là truyền 'this' cho hàm thành viên làm tham số đầu tiên (vô hình) của nó. Nếu đó là một hàm ảo, bảng ảo làm phức tạp mọi thứ, nhưng về cơ bản, bạn luôn chuyển con trỏ 'this' này tới các hàm thành viên làm paramater đầu tiên của chúng. Đó là lý do tại sao một số toán tử quá tải (như '<<') phải là các hàm của người bạn chứ không phải là các hàm thành viên. –

+0

@Mashmagar: vâng, cả hai đều khá giống nhau. –

2

Không, đây không phải là cách thực hiện.
Các phương thức không phải là virtual hoàn toàn giống với bất kỳ hàm nào khác nhưng có đối số bổ sung cho con trỏ this.

Phương thức là virtualinvoked using a v-table. bảng v là danh sách các con trỏ hàm được lưu trữ bên cạnh dữ liệu đối tượng. Trong một nghĩa nào đó, điều này gần với những gì bạn mô tả nhưng vẫn còn, cơ thể của hàm luôn giống nhau cho tất cả các trường hợp của đối tượng.
Điều này có thể được chứng minh nếu bạn có biến số static trong phương thức. Biến tĩnh sẽ giống nhau cho các phương thức được gọi từ các cá thể khác nhau.

+0

Trên thực tế, hầu hết các triển khai đặt con trỏ * vào vtable trong đối tượng chứ không phải chính vtable. Con trỏ này thường được gọi là * vptr *. – fredoverflow

0

Trình biên dịch tạo mục nhập cho mọi lớp (không phải đối tượng) bên trong cấu trúc dữ liệu của riêng nó. Mục này cho lớp có chứa các con trỏ tới các phương thức cho lớp đó.

Một đối tượng được biểu diễn trong bộ nhớ dưới dạng con trỏ đến lớp cha và một tập hợp các trường mẫu của nó (vì chúng khác nhau đối với mọi đối tượng.) Sau đó, khi một phương thức được gọi, đối tượng đi theo con trỏ đến sau đó đi theo con trỏ đến phương thức thích hợp. Một con trỏ tới đối tượng cũng được cung cấp cho phương thức, hoạt động như con trỏ này.

Phương pháp ảo phức tạp hơn một chút, nhưng chúng được thực hiện theo cách tương tự.

Nếu bạn muốn biết thêm, hãy xem bạn có thể tham gia lớp ngôn ngữ lập trình hay không.

Dưới đây là một nỗ lực nghèo ở nghệ thuật ASCII để giải thích nó:

obj      class 
+------------+   +----------+ 
| ptrToClass |----------->| method1 | ----------> toSomewhere(ptrToObj) 
|------------|   |----------| 
| field1  |   | method2 | ----------> toSomewhereElse(ptrToObj) 
+------------+   +----------+ 
2

Bởi vì bạn có định nghĩa của Meow bên trong định nghĩa lớp, Meow là ngầm inline.

nội tuyến là gợi ý cho trình biên dịch để thay thế cuộc gọi bằng nội dung thực tế của hàm. Nhưng nó chỉ là một gợi ý - trình biên dịch có thể chọn bỏ qua gợi ý.

Nếu trình biên dịch tuân theo gợi ý, mỗi cuộc gọi sẽ được thay thế bằng nội dung hàm. Điều đó có nghĩa là trình biên dịch sẽ tạo mã mỗi khi Meow được gọi thay vì tạo ra một cuộc gọi hàm.

Nếu trình biên dịch bỏ qua gợi ý, trình biên dịch/liên kết sẽ sắp xếp để có một phiên bản duy nhất mà tất cả các cuộc gọi sẽ được hướng đến (vì nội tuyến, chiến lược cổ điển là mọi đơn vị dịch sử dụng hàm này sẽ có được một bản sao riêng biệt với các hướng dẫn cho trình liên kết để chỉ giữ một phiên bản).

Cuối cùng, hãy chuyển sang các giải thích mà chức năng không trực tuyến. Trong trường hợp này, nó được yêu cầu cho các coder để đảm bảo định nghĩa xuất hiện trong chính xác một đơn vị dịch thuật và tất cả các cuộc gọi sẽ được gửi đến một phiên bản này.

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