2016-07-10 42 views
6

Tôi đã tìm thấy một vài câu hỏi khác dường như ngụ ý câu hỏi của tôi, nhưng không có câu hỏi nào trả lời trực tiếp câu hỏi đó (thậm chí the one có cùng tên).Tại sao upcasting một con trỏ đến một lớp cơ sở đôi khi thay đổi giá trị con trỏ?

Tôi có một lớp con C++ có nguồn gốc từ hai cha mẹ. Tôi tạo ra một đối tượng của lớp đó, sau đó đưa tham chiếu của nó đến từng cha mẹ của nó. Các giá trị truyền không giống nhau, mặc dù giá trị này vẫn là giá trị của tham chiếu con. Dưới đây là các mã:

class Mom 
{ 
public: 
    int a = 1; 
}; 

class Dad 
{ 
public: 
    int b = 2; 
}; 

class Child : public Mom, public Dad 
{ 
public: 
    int c = 3; 
}; 

int main() 
{ 
    Child* c = new Child; // 0x00C5A618 

    Mom* m = c; // 0x00C5A618 
    Dad* d = c; // 0x00C5A61C 

    return 0; 
} 

Bây giờ, tôi đoán rằng các địa chỉ thu được bằng cách áp dụng static_cast <> là của các subobjects thực tế. Khi điều đó xảy ra, Mom là subobject đầu tiên, vì vậy nó thực sự có cùng địa chỉ là Child. Dad là subobject thứ hai, do đó, nó nằm ở một địa chỉ khác.

Theo tôi được biết, C-style phôi, như thế này

Mom* m = (Mom*)c; 
Dad* d = (Dad*)c; 

sẽ áp dụng một static_cast <> khi nó sẽ là pháp lý để làm như vậy. Thật vậy, khi tôi thực sự biên dịch và thực thi phiên bản đó, tôi thấy hành vi tương tự như trước đây.

Tiếp tục saga sắc sảo này, sử dụng phôi tiềm ẩn, như

Mom* m = c; 
Dad* d = c; 

triển lãm này hành vi tương tự là tốt, mà từ đó tôi suy ra rằng phôi ngầm cũng được áp dụng static_cast <> khi các diễn viên rõ ràng tương đương sẽ là hợp pháp.

downcasting trở lại Child làm điều tương tự:

Child* k = static_cast<Child*>(d); // 0x00C5A618 

Giá trị trong k khác với giá trị trong d, đã được thiết lập trở lại với giá trị ban đầu trong c.

Tôi đoán rằng, trong trường hợp này, có kiểm tra thời gian chạy để xem liệu, thực sự, d trỏ đến một đối tượng Dad của đối tượng Child.

Cho đến nay, tất cả đều có ý nghĩa, nhưng tôi ngạc nhiên rằng không có điều chỉnh địa chỉ như vậy (nếu đó là điều đúng để gọi nó) cho phân cấp đơn lẻ có nguồn gốc. Nghĩa là, nếu hệ thống cấp bậc của tôi chỉ đơn giản là Mom ->Dad ->Child, đúc một tham chiếu đến một Child thành một con trỏ đến hoặc là một Mom hoặc một Dad bao giờ thay đổi giá trị:

class Mom 
{ 
public: 
    int a = 1; 
}; 

class Dad : public Mom 
{ 
public: 
    int b = 2; 
}; 

class Child : public Dad 
{ 
public: 
    int c = 3; 
}; 

int main() 
{ 
    Child* c = new Child; // 0x00C5A618 

    Mom* m = c; // 0x00C5A618 
    Dad* d = c; // 0x00C5A618 

    Child* k = static_cast<Child*>(d); // 0x00C5A618 

    return 0; 
} 

tôi đã mong đợi nó để thay đổi kích thước của bộ nhớ cần thiết cho mỗi lớp con bổ sung hoặc bốn byte (kích thước của một int). Nhưng, họ thì không. Tất cả các con trỏ trong ví dụ trên, c, m, dk, có cùng giá trị.

Vì vậy, bây giờ tôi đang đoán rằng các đối tượng đã được đặt ra trong bộ nhớ với các đối tượng Mom tại địa chỉ thấp nhất, thêm không gian cần thiết cho đối tượng Dad sắp tới, và thêm không gian cần thiết cho việc Child đến cuối cùng. Kể từ số Dad là-a Mom, địa chỉ Dad sẽ giống với địa chỉ Mom, phải không?

Tương tự, một số Child cũng là Mom, vì vậy địa chỉ của địa chỉ cũng giống như địa chỉ của địa chỉ phụ của địa chỉ Mom.

Vậy ...

Tôi nghĩ rằng nó đang làm việc như thế này: trong bộ nhớ, các đối tượng theo nhau như vậy mà, trong hoàn toàn đơn lẻ có nguồn gốc hệ thống phân cấp, upcasting và downcasting luôn mang lại cùng một địa chỉ, trong khi ở một nhân hệ thống phân cấp có nguồn gốc, một số upcasts mang lại một địa chỉ khác, bởi vì không phải tất cả các subobject sẽ bắt đầu tại cùng một địa chỉ với đối tượng dẫn xuất. Như thế này:

Possible Memory Layout for a Singly Derived Hierarchy 

+-------+-----+-----+---+ 
| Child : Dad : Mom : a | 
|  |  +-----+---+ 
|  |   : b | 
|  +-----------+---+ 
|     : c | 
+-----------------------+ 


Possible Memory Layout for a Multiply Derived Hierarchy 

+-------+-----+---+ 
| Child : Mom : a | 
|  +-----+---+ 
|  : Dad : b | 
|  +-----+---+ 
|    : c | 
+-----------------+ 

Xin lỗi mà tôi đã lan man một chút, nhưng câu hỏi của tôi là thế này: là một thực tế rằng một bị ném lên trời để một số lớp cơ sở của một đối tượng nhân có nguồn gốc thay đổi giá trị của các tài liệu tham khảo là kết quả của thực tế là giá trị sau khi truyền là địa chỉ của đối tượng cơ sở trong bộ nhớ?

+1

Có ........... – Soren

Trả lời

3

Có, bạn đã đúng. Upcasting một con trỏ cho địa chỉ của subobject cha được lưu trữ ở đâu đó bên trong lớp con. Thông thường, subobject cha được lưu trữ ở đầu của đối tượng con, do đó con trỏ tới con và subobject cơ sở giống nhau.

Trong trường hợp đa thừa kế, tất cả phụ huynh không thể cùng một bộ nhớ (chúng ta hãy bỏ qua khả năng tối ưu hóa cơ sở trống), vì vậy tất cả, nhưng một, địa chỉ lớp cơ sở sẽ khác với địa chỉ con.

2

Tôi đoán rằng, trong trường hợp này, có một kiểm tra thời gian chạy để xem, quả thật vậy, d điểm đến một subobject Dad của một đối tượng Child.

Không, nếu bạn muốn kiểm tra thời gian chạy, hãy sử dụng dynamic_cast (nhưng loại nguồn phải được đa hình). Với static_cast trình biên dịch sẽ chỉ giả định loại là chính xác, nếu không hành vi không xác định xảy ra.

Tôi nghĩ rằng nó đang làm việc như thế này: trong bộ nhớ, các đối tượng theo nhau như vậy mà, trong hoàn toàn đơn lẻ có nguồn gốc hệ thống phân cấp, upcasting và downcasting luôn mang lại cùng một địa chỉ

này không được bảo đảm; tiêu chuẩn lá nó không xác định. Trình biên dịch không phải bố trí lớp cơ sở và các thành phần con để các lớp con lớp cơ sở đến trước, mặc dù có vẻ như trình biên dịch của bạn thực hiện trong các trường hợp này; nhưng có thể có các trường hợp khác mà địa chỉ có thể khác nhau, chẳng hạn như nếu lớp dẫn xuất có chứa các hàm ảo và lớp cơ sở thì không. (Trong trường hợp này, cách bố trí chưa vptr đầu tiên, sau đó lớp cơ sở subobject, sau đó subobjects thành viên.)

là một thực tế rằng một bị ném lên trời để một số lớp cơ sở của một đối tượng nhân có nguồn gốc thay đổi giá trị của các tài liệu tham khảo kết quả của thực tế là giá trị sau khi truyền là địa chỉ của đối tượng cơ sở trong bộ nhớ?

Có.

+0

Đó là một số nội dung uốn cong tâm trí! Là một lập trình viên C cũ, ý tưởng rằng việc đúc một con trỏ sẽ mang lại một giá trị khác với con trỏ có chính nó là khó chấp nhận. Chứng minh thêm rằng người ta không thể chỉ giả định rằng mã C giống như trong C++ ứng xử theo cách nó sẽ trong chính hãng C. –

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