2010-06-21 50 views
6

Tôi có một con trỏ lớp cơ sở trỏ đến đối tượng lớp dẫn xuất. Tôi đang gọi hàm foo() bằng cách sử dụng hai cách khác nhau trong mã bên dưới. Tại sao Derived::foo() được gọi trong trường hợp đầu tiên? Không nên (*obj).foo() gọi Base::foo() hoạt động vì nó đã bị hủy đăng ký?Gọi hàm ảo bằng đối tượng dereference

class Base 
    { 
    public: 
     Base() {} 
     virtual void foo() { std::cout << "Base::foo() called" << std::endl; } 
     virtual ~Base() {}; 
    }; 

    class Derived: public Base 
    { 
    public: 
     Derived() : Base() {} 
     virtual void foo() { std::cout << "Derived::foo() called" << std::endl; } 
     virtual ~Derived() {}; 
    }; 

    int main() { 
     Base* obj = new Derived(); 
    // SCENARIO 1 
     (*obj).foo(); 
// SCENARIO 2 
     Base obj1 = *obj; 
     obj1.foo(); 

     return 0; 
    } 

Trả lời

15
// SCENARIO 1 
(*obj).foo(); 

Lưu ý rằng

  1. obj là sự nhầm lẫn ở đây, vì nó không đề cập đến một đối tượng, nhưng để một con trỏ ,
  2. (*ptr).foo() chỉ là một vòng xoay cách làm ptr->foo().

*ptr không dẫn đến một đối tượng, nhưng trong một tham khảoBase& đến đối tượng. Và một cuộc gọi chức năng ảo thông qua một tham chiếu phải chịu công văn động, giống như cuộc gọi thông qua một con trỏ.

// SCENARIO 2 
Base obj1 = *ptr; 
obj1.foo(); 

Những gì bạn làm ở đây là bạn tạo một đối tượng hoàn toàn mới thông qua slicing: nó chỉ có những phần lớp cơ sở của *ptr. Những gì bạn muốn thay thế là:

Base& ref = *ptr; 
ref.foo(); 
3

Trường hợp 2 tạo đối tượng hoàn toàn mới kiểu Base. Như vậy, khi chúng tôi thực hiện obj1.foo(), đối tượng không có Nguồn gốc; không có cách nào chúng ta gọi hàm Derived.

Trong trường hợp 1, tuy nhiên, đối tượng là, trong thực tế, một thể hiện của nguồn gốc, mà chúng ta đang truy cập thông qua một con trỏ cơ sở. Đây chính xác là tình huống các chức năng ảo được thiết kế cho; triển khai của lớp dẫn xuất được sử dụng.

+0

Giá trị mô tả cắt. –

0

Đa hình hoạt động trên tài liệu tham khảo (kết quả của dereferencing con trỏ) giống như trên con trỏ.

0

Ý bạn là gì bởi "vì nó đã bị hủy đăng ký"?

Lớp cơ sở con trỏ obj trỏ đến đối tượng lớp dẫn xuất và vì bạn đã khai báo hàm foo() ảo lớp dẫn xuất foo() sẽ được gọi.

1

Sẽ giúp ích nếu bạn suy nghĩ một chút về việc triển khai. Trong kịch bản thứ hai, bạn đang thực sự tạo một đối tượng mới kiểu Base, nó sẽ đi kèm với một bảng chức năng ảo mới. Nhưng trong kịch bản đầu tiên, *obj sẽ "trỏ tới", hay đúng hơn là tham chiếu, một đối tượng vẫn có bảng chức năng ảo của một đối tượng kiểu Derived.

0

(Đó là câu hỏi khá lạ. Tôi thà hy vọng một ai đó hỏi tại sao trong trường hợp thứ hai Derived::foo không được gọi.)

Trong ngôn ngữ C++, trong đó phiên bản của chức năng ảo được gọi là hoàn toàn độc lập với những gì đã, đang và những gì đã không được "dereferenced". Dereferencing làm cho không có sự khác biệt nào. Điều duy nhất quan trọng là loại động động của đối tượng được sử dụng trong cuộc gọi.

Trong trường hợp đầu tiên, Derived::foo được gọi vì loại động của đối tượng *objDerived.

Trong trường hợp thứ hai, loại động là obj1Base, do đó, Base::foo được gọi.

Nói cách khác, mọi thứ hoạt động như mong đợi. Mà làm cho một thắc mắc wat làm bạn hỏi câu hỏi của bạn. Điều gì khiến bạn mong đợi điều gì đó khác biệt?

1

Là một sự bổ sung cho các câu trả lời khác.

Thuật ngữ kỹ thuật cho những gì đang diễn ra trong Kịch bản 2 của bạn là cắt đối tượng.

Dưới đây là mục wikipedia:

http://en.wikipedia.org/wiki/Object_slicing

Và đây là một câu hỏi khác trên stackoverflow trên đối tượng cắt:

What is object slicing?

1

Trong trường hợp đầu tiên trên phiên bản có nguồn gốc của foo() sẽ được gọi là do những lý do rõ ràng được giải thích ở trên. Ngoài các câu trả lời khác, *(*Obj).func()* đồng nghĩa với *Obj->func()*.

Trong trường hợp thứ hai một đối tượng mới của lớp Base đang được khởi tạo thông qua các nhà xây dựng bản sao và vì nó là một đối tượng Base lớp nó sẽ gọi phiên bản Base lớp foo().

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