2010-01-28 37 views
8

Tôi đang viết một số mã liên quan đến thừa kế từ lớp con trỏ đếm ngược cơ bản; và một số phức tạp của C++ xuất hiện. Tôi đã giảm nó như sau:Con trỏ C++ niềm vui đa thừa kế

Giả sử tôi có:

class A{}; 
class B{}; 
class C: public A, public B {}; 

C c; 
C* pc = &c; 
B* pb = &c; 
A* pa = &c; 

// does pa point to a valid A object? 
// does pb point to a valid B object? 

// does pa == pb ? 

Bên cạnh đó, thực hiện:

// pc == (C*) pa ? 
// pc == (C*) pb ? 

Cảm ơn!

+2

những gì bạn có nghĩa là bởi ==? Rõ ràng, các địa chỉ giống nhau. –

+9

Các địa chỉ không phải là necesarrily giống nhau. –

+1

Trong một triển khai đa thừa kế (virtual) mà tôi đã thấy, một số offset được thêm vào địa chỉ cơ sở của đối tượng lớp dẫn xuất để truy cập vào các thuộc tính và phương thức của B. Tuy nhiên điều này không hiển thị cho người dùng khi trình biên dịch thao tác nội bộ. Do đó trong chương trình của bạn, bạn vẫn có thể thấy địa chỉ của A, B và C giống nhau. – mukeshkumar

Trả lời

7
  • làm điểm pa cho đối tượng A hợp lệ?
  • có pb trỏ đến đối tượng B hợp lệ không?

Vâng, C* được chuyển đổi để papb trỏ đến địa chỉ chính xác.

  • không pa == pb?

Không, thường là không. Không thể có đối tượng A và đối tượng B ở cùng một địa chỉ.

Hơn nữa, không

  • pc == (C *) pa?
  • pc == (C *) pb?

Dàn diễn viên chuyển con trỏ trở lại đến địa chỉ của đối tượng C, vì vậy cả hai đẳng là đúng sự thật.

+1

'dynamic_cast' không phù hợp ở đây và kiểu C là chính xác (mặc dù biểu mẫu không đúng). Theo §5.4/7, việc đúc kiểu C có thể gọi một 'static_cast' trong đó" - một con trỏ tới một đối tượng của kiểu lớp cơ sở không phải ảo, một giá trị của loại lớp cơ sở không phải ảo, hoặc một con trỏ tới thành viên của non- loại lớp cơ sở ảo có thể được chuyển đổi rõ ràng thành con trỏ, tham chiếu hoặc con trỏ thành thành viên của loại lớp dẫn xuất tương ứng. " – Potatoswatter

+0

Vâng, bạn nói đúng, diễn viên năng động sẽ chỉ cần thiết nếu 'pa' sẽ được đúc thành một' B * ', ví dụ. – sth

+0

Trong trường hợp đó, biểu thức 'dynamic_cast' sẽ chỉ là một cách ưa thích để nói' NULL'. An A * không thể là B * vì vậy nó sẽ luôn thất bại. – Potatoswatter

1
pc == pa; 
pc == pb; 

Không được xác định, tùy thuộc vào cấu trúc lớp học.

pc == (C*) pa; 
pc == (C*) pb; 

Thats ok.

pa == pb; 

số

Họ trỏ đến đối tượng hợp lệ?

Yes 
2

C nhúng một AB.

class C: public A, public B {}; 

rất giống với mã C

struct C { 
    A self_a; 
    B self_b; 
}; 

(B*) &c; tương đương với static_cast< B* >(&c) cũng tương tự như &c.self_b nếu bạn đang sử dụng thẳng C.

Nói chung, bạn không thể dựa vào các con trỏ cho các loại khác nhau có thể hoán đổi cho nhau hoặc có thể so sánh được.

0

Những gì bạn nhận được là một cái gì đó như thế này trong bộ nhớ

---------- 
| A data | 
---------- 
| B data | 
---------- 
| C data | 
---------- 

Vì vậy, nếu bạn muốn toàn bộ đối tượng C bạn sẽ nhận được một con trỏ đến đầu của bộ nhớ. Nếu bạn muốn chỉ có một "phần", bạn sẽ nhận được cùng một địa chỉ vì đó là nơi các thành viên dữ liệu được đặt. Nếu bạn muốn phần "B" bạn nhận được sự khởi đầu + sizeof (A) + sizeof (bất kể trình biên dịch nào bổ sung cho vtable). Vì vậy, trong ví dụ, pc! = Pb (có thể là pc! = Pa) nhưng pa không bao giờ bằng pb.

+1

Tôi cầu xin sự khác biệt về 'pc! = Pb'. Nhập mã của câu hỏi vào DevStudio 2005, tôi nhận được tất cả các con trỏ có cùng giá trị. Và đó có thể là những gì tiêu chuẩn C++ nói sẽ xảy ra, trong trường hợp này - không có phương pháp ảo và không có thành viên dữ liệu. OP đã đăng một câu hỏi khác, nơi các lớp cơ sở có các thành viên, do đó, có một câu trả lời khác nhau, đó là câu trả lời này nhiều hơn hoặc ít hơn. – Skizz

+0

@Skizz: những gì bạn đang thấy được gọi là tối ưu hóa lớp cơ sở trống http://www.google.com/search?q=empty+base+class+optimization nó được thực hiện bởi tất cả các trình biên dịch hiện đại nhưng không được yêu cầu. – Potatoswatter

+1

lớp A {}; lớp B {}; lớp C: công khai A, công khai B {}; int main() { C c; C * pc (&c); A * pa (pc); B * pb (pc); printf ("C = 0x% 08X \ nB = 0x% 08X \ nA = 0x% 08X \ n", pc, pb, pa) ; } đầu ra từ VS2008 phát hành xây dựng C = 0x0018FEFF B = 0x0018FF00 A = 0x0018FEFF Nếu bạn thay đổi thứ tự trong đó C được thừa hưởng từ A và B, bạn sẽ nhận được giá trị khác nhau cho A và B –

0

Item 28 Meaning of Pointer Comparison trong C++ Common Knowledge: Essential Intermediate Programming) giải thích sự quan trọng của con trỏ đối tượng trong C++:

Trong C++, một đối tượng có thể có nhiều, địa chỉ hợp lệ, và con trỏ so sánh không phải là một câu hỏi về địa chỉ. Đó là một câu hỏi về nhận dạng đối tượng.

Hãy nhìn vào các mã:

class A{}; 
class B{}; 
class C: public A, public B {}; 

C c; 
C* pc = &c; 
B* pb = &c; 
A* pa = &c; 

class C xuất phát từ cả hai class Aclass B, vì vậy class C là cả class Aclass B. đối tượng C c có 3 địa chỉ hợp lệ: địa chỉ cho class A, class Bclass C. Việc thực hiện phụ thuộc vào trình biên dịch, vì vậy bạn không thể giả định cách bố trí bộ nhớ của class C, và nó có thể như thế này:

---------- <- pc (0x7ffe7d10e1e0) 
|  | 
---------- <- pa (0x7ffe7d10e1e4) 
| A data | 
---------- <- pb (0x7ffe7d10e1e8) 
| B data | 
---------- 
| C data | 
---------- 

Trong trường hợp trên, mặc dù giá trị địa chỉ của pc, papb không giống nhau, tất cả chúng đều đề cập đến cùng một đối tượng (c), do đó trình biên dịch phải đảm bảo rằng pc so sánh bằng cả hai papb, tức là, pc == papc == pb. Trình biên dịch hoàn thành việc so sánh này bằng cách điều chỉnh giá trị của một trong các con trỏ được so sánh với độ lệch thích hợp. Ví dụ:

pc == pa 

được phiên dịch sang:

pc ? ((uintptr_t)pc + 4 == (uintptr_t)pa) : (pa == 0) 

Trong số những thứ khác, vì AB không có quan hệ thừa kế, chúng ta không thể so sánh papb trực tiếp.

Đối với câu hỏi của bạn:

(1) does pa point to a valid A object? 
(2) does pb point to a valid B object? 
Yes, refer the above diagram. 

(3) pc == (C*) pa ? 
(4) pc == (C*) pb ? 
Yes, No need to add (C*). 

(5) does pa == pb ? 
No. We can't compare them.