2010-12-24 35 views
5

Hãy xem xét các đoạn mã sau:Hành vi của đối tượng C++ Reference

class Window // Base class for C++ virtual function example 
    { 
     public: 
      virtual void Create() // virtual function for C++ virtual function example 
      { 
       cout <<"Base class Window"<<endl; 
      } 
    }; 

    class CommandButton : public Window 
    { 
     public: 
      void Create() 
      { 
       cout<<"Derived class Command Button - Overridden C++ virtual function"<<endl; 
      } 
    }; 

    int main() 
    { 
     Window *button = new CommandButton; 
     Window& aRef = *button; 
     aRef.Create(); // Output: Derived class Command Button - Overridden C++ virtual function 
     Window bRef=*button; 
     bRef.Create(); // Output: Base class Window 

     return 0; 
    } 

Cả ArefBREF được giao * nút, nhưng tại sao là hai đầu ra khác nhau. Sự khác biệt giữa việc gán cho loại Tham chiếu và loại Không tham chiếu là gì?

Trả lời

10

Bạn đã gặp sự cố cắt.

Window bRef =*button; 

Ở đây bRef không phải là tham chiếu mà là đối tượng. Khi bạn gán một kiểu có nguồn gốc vào bRef, bạn đang cắt phần có nguồn gốc rời khỏi bạn chỉ với một đối tượng Window được tạo thành một CommandButton.

Điều đang xảy ra là bRef được tạo trong câu lệnh trên bằng cách sử dụng trình tạo bản sao tạo ra trình biên dịch cho cửa sổ lớp. Tất cả hàm tạo này làm là sao chép các phần tử thành viên từ RHS đến đối tượng mới được xây dựng. Kể từ khi lớp học không chứa thành viên không có gì đang xảy ra.

Lưu ý phụ: Lớp học có thành viên ảo cũng phải có trình phá hủy ảo.

+0

Vì vậy, với người xây dựng bản sao này ràng buộc trễ đơn giản là không xảy ra, phải không? –

+0

Pháo thủ: Liên kết muộn không có liên quan ở đây. Bạn đang tạo đối tượng được nhập tĩnh mới không phải là đối tượng được nhập động (vì bạn cần tham chiếu hoặc con trỏ). –

+0

destructor ảo hoặc được bảo vệ. Trong nhiều thiết kế, chức năng của thành viên được gọi là đa hình mà không xóa đa hình. –

2
Window bRef=*button; 
bRef.Create(); // Output: Base class Window 

Loại tĩnh và động là bRef chỉ là Window. Cơ chế ảo chỉ hoạt động với các tham chiếu và các con trỏ. bRef là một đối tượng không phải là tham chiếu hoặc con trỏ.

1
Window bRef=*button; 
bRef.Create(); // Output: Base class Window 

Đây bRef không phải là tham chiếu đến button (bạn vừa nêu tên). bRef chỉ nhận subobject cơ bản là Window.

+0

Nhưng tác dụng của bài tập này tôi đang làm là gì. Vì nút * đã được khởi tạo bằng CommandButton, đây có phải là lỗi biên dịch không? –

+0

Không. Lớp Cửa sổ của bạn có một trình tạo bản sao Window (const Window &). Vì CommandButton là một cửa sổ, nó có thể là đối số cho hàm tạo bản sao đó. – cgmb

+0

@ Slavik81: Có, vậy? Làm thế nào nó làm mất hiệu lực những gì tôi nói? – Nawaz

7
  • aRefWindowtĩnh loại nhưng CommandButtonđộng loại
  • bRef chỉ đơn giản là một đối tượng kiểu Window (các CommandButton'phần' đã bị mất trong các bản sao)

Điều này thường được gọi là object slicing một Nó thường bị ngăn cản bằng cách tạo các lớp cơ sở hoặc trừu tượng (bằng cách cung cấp một hàm ảo thuần túy) hoặc không thể sao chép được (ví dụ sử dụng boost::noncopyable), vì một trong hai giải pháp sẽ làm cho mã không biên dịch được trên dòng Window& aRef = *button;.


Bây giờ, tại sao bRef.Create() gọi Window::Create? Vâng, không có gì hơn một Window trong bRef vì vậy thực sự không có nhiều thay thế. Về cơ bản, điều này giống như tuyên bố một số Window và gọi số Create trên đó: thực tế là bRef được sao chép từ một phiên bản CommandButton không liên quan vì phần CommandButton bị mất trong bản sao.

Tôi sẽ cố gắng làm cho điều này rõ ràng hơn bằng cách trích dẫn tiêu chuẩn (10.3/6):

[Lưu ý: việc giải thích các cuộc gọi của một hàm ảo phụ thuộc vào loại của đối tượng mà nó là gọi (năng động kiểu), trong khi việc giải thích của một cuộc gọi của chức năng thành viên phi ảo phụ thuộc chỉ chỉ trên loại con trỏ hoặc tham chiếu biểu thị đối tượng đó (các loại tĩnh) (5.2.2). ]

Chỉ thông qua một con trỏ hoặc tham chiếu, có thể loại tĩnh của một đối tượng khác với loại động của nó.