2012-04-02 24 views
10

Có, tôi biết rằng downcast sử dụng dynamic_cast không thể biên dịch nếu Base không phải là đa hình, nhưng vấn đề của tôi không phải là về việc này.`dynamic_cast` từ Base to Derived

class Base { 
    public: 
     virtual void bar() 
     { 
      cout << "bar\n"; 
     } 
}; 

class Derived: public Base { 
    public: 
     void foo() 
     { 
      cout << "foo\n"; 
     } 
}; 

int main() 
{ 
    Base *pb; 
    Derived *pd; 

    pb = new Derived; //Base* points to a Derived object 
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo(); //outputs foo 

    pb = new Base; //Base* points to a Base object 
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo(); //outputs foo, too. Why? 
} 

Tôi nghĩ khi pb = new Derived;, pb thực trỏ đến một đối tượng Derived nằm trong heap. Sau pd = dynamic_cast<Derived*>(pb);, pd cũng trỏ đến đối tượng Derived, vì vậy pd->foo() phải OK.

Nhưng khi pb = new Base;, những gì pb điểm để là một đối tượng Base trong đống, sau đó sau khi pd = dynamic_cast<Derived*>(pb);, làm thế nào có thể pd->foo() công trình? Đã dynamic_cast biến đối tượng Base trong đống thành đối tượng Derived?

Trả lời

17

Trong C++, mỗi thể hiện của một lớp có phiên bản riêng của kiểu dữ liệu, nhưng tất cả các lớp đều có cùng chức năng trong bộ nhớ (ngoài chức năng nội dòng). Trong trường hợp của bạn, khi bạn nói điều gì đó như:

pd->foo(); 

Bạn đang chủ yếu gọi Derived::foo, mà là một chức năng trong bộ nhớ và trình biên dịch biết nó ở đâu. Vấn đề là, nó không phụ thuộc vào số pd. Tuy nhiên, nếu bạn có một cái gì đó như thế này:

class Derived : public Base { 
    private: 
     int a; 

    public: 
     Derived() { a = 100; } 

     void foo() { 
      std::cout<<a<<std::endl; 
     } 
}; 

Sau đó, pd->foo() sẽ gây ra lỗi phân đoạn. Tại đây, dàn diễn viên năng động của bạn đã không thành công và khi được gọi là Derived::foo, nó được chuyển qua 0 làm đối tượng this. Nó đã được sử dụng tốt trong trường hợp trước, vì đối tượng this không bao giờ được sử dụng. Tuy nhiên, trong trường hợp thứ hai, nó được sử dụng và do đó, gây ra một lỗi Segmentation.

+0

Khi tôi nói 'pd-> foo();', thì 'foo()' sẽ được gọi là không có vấn đề 'pd' là' NULL' hay không? – Alcott

+0

@Alcott yes, nhưng tham số 'this' sẽ được chuyển thành NULL (vì đó là giá trị của' pd'). Do đó, sự cố xảy ra khi bạn hủy bỏ nó bằng cách truy cập 'a' trong ví dụ của Rohan. – littleadv

+1

Phần lớn phụ thuộc vào trình biên dịch, và như @Luchian Grigore đã đề cập, mọi thứ đều có thể xảy ra. Vì vậy, trong hầu hết các trường hợp, có, nhưng nó là một cái gì đó bạn không thể đếm trên. –

7

Trong số foo bạn không truy cập this, trong trường hợp này phải là NULL. Đó là những gì dynamic_cast trả về khi không thể thực hiện được phép đúc.

Về cơ bản, bạn đang ở trong khu vực "hành vi chưa được xác định" tại đây.

+0

FYI, tôi đã +1 câu trả lời của bạn vì nó đúng. * Nhưng * vẫn không thấy làm thế nào một cái gì đó như thế này là may mắn. Bạn có thể chạy vào các lỗi khó chịu mà khó theo dõi vì chương trình không bị lỗi khi cần. –

+1

@LuchianGrigore [Luck] (http://en.wiktionary.org/wiki/luck#Noun) (n) 1. Điều gì đó xảy ra với ai đó bằng cơ hội, một cơ hội xảy ra. – Potatoswatter

6

Bạn đang chạy vào hành vi không xác định. Bạn nên kiểm tra kiểu trả về là dynamic_cast.

pd = dynamic_cast<Derived*>(pb); 

Điều này trả về null và bạn gọi hàm trên con trỏ NULL. Chuyện gì cũng có thể xảy ra.

+0

Nhưng nếu 'pd == NULL', thì tại sao không có lỗi thời gian chạy? – Alcott

+0

@Alcott đó là hành vi không xác định. Chuyện gì cũng có thể xảy ra. –

+1

@Alcott về mặt kỹ thuật câu trả lời của Luchian là chính xác. Nhưng thực tế lý do là vì nó đơn giản hơn cho các nhà phát triển trình biên dịch để bỏ qua điều này, vì chúng được cho phép (vì tiêu chuẩn không định nghĩa phải làm gì trong trường hợp này), thay vì thực hiện kiểm tra thời gian chạy phức tạp và chi phí hoạt động mọi truy cập con trỏ. Vì vậy, bất cứ điều gì xảy ra - xảy ra, và bất cứ điều gì là chính xác. – littleadv

1

Vui lòng luôn kiểm tra xem dàn diễn viên có thành công hay không trước khi thử sử dụng. Tôi đoán đó là lợi thế của việc sử dụng tính năng truyền. Bạn có thể kiểm tra xem nó thành công hay không.

pd = dynamic_cast<Derived*>(pb); 
if(pd!=NULL) 
    pd->foo(); 

Nếu dàn diễn viên thất bại pd có giá trị NULL. Không sử dụng pd trừ khi bạn chắc chắn nó có giá trị. và sau đó chỉ de tham chiếu nó

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