2015-08-18 19 views
12

Có ai giúp tôi hiểu hành vi này không? Để viết tắt:Hành vi lạ khi trích xuất giao diện đã biết từ thùng chứa đa hình

  • Tôi đã lưu trữ các đối tượng đa hình trong một vùng chứa chung.
  • Một số người trong số họ triển khai một giao diện cụ thể. Tôi có thể nói cái nào.
  • Nhưng tôi không thể sử dụng giao diện này.

Dưới đây là những gì tôi đã boilt nó xuống:

#include <iostream> 
#include <vector> 


// A base class 
struct Base { 
    // A polymorphic method 
    virtual void describe() const { 
     std::cout << "Base" << std::endl; 
    }; 
    virtual ~Base(){ 
     std::cout << " Base destroyed" << std::endl; 
    }; 
}; 

// A specific interface 
struct Interface { 
    virtual ~Interface(){ 
     std::cout << " Interface Destroyed" << std::endl; 
    }; 
    virtual void specific() = 0; 
}; 

// A derived class.. 
struct Derived : public Base, public Interface { 
    virtual void describe() const { 
     std::cout << "Derived" << std::endl; 
    }; 
    virtual void specific() { 
     std::cout << "Derived uses Interface" << std::endl; 
    }; 
    virtual ~Derived() { 
     std::cout << " Derived destroyed" << std::endl; 
    }; 
}; 

int main() { 

    // Test polymorphism: 
    Base* b(new Base()); 
    Derived* d(new Derived()); 
    b->describe(); // "Base" 
    d->describe(); // "Derived" 
    // Ok. 

    // Test interface: 
    d->specific(); // "Derived uses Interface" 
    Interface* i(d); 
    i->specific(); // "Derived uses Interface" 
    // Ok. 

    // Here is the situation: I have a container filled with polymorphic `Base`s 
    std::vector<Base*> v {b, d}; 
    // I know that this one implements the `Interface` 
    Interface* j((Interface*) v[1]); 
    j->specific(); // " Derived destroyed" 
        // " Interface destroyed" 
        // " Base destroyed" 
    // Why?! What did that object do to deserve this? 

    return EXIT_SUCCESS; // almost -_- 
} 

bất cứ ai có thể cho tôi biết những gì tôi đang mất tích ở đó?

Thú vị thực tế: Nếu tôi hoán đổi các định nghĩa của Base::~BaseBase::describe, thì đối tượng mô tả chính nó thay vì bị hủy. Làm thế nào đến thứ tự các vấn đề trong khai báo phương pháp?

Trả lời

21

Đây là lý do chính đáng để tránh phôi kiểu C. Khi bạn làm:

Interface* j((Interface*) v[1]); 

Đó là reinterpret_cast. Một dàn diễn viên kiểu C sẽ cố gắng thực hiện, theo thứ tự: const_cast, static_cast, static_cast rồi const_cast, reinterpret_cast, reinterpret_cast rồi const_cast. Tất cả các phôi này, trong trường hợp này, là sai! reinterpret_cast đặc biệt sẽ chỉ là hành vi không xác định và nó thành thật thậm chí không quan trọng tại sao bạn thấy hành vi cụ thể mà bạn nhìn thấy & dagger;. Hành vi không xác định là không xác định.

gì bạn muốn làm thay thế là:

Interface* j = dynamic_cast<Interface*>(v[1]); 

Đó là diễn viên đúng thông qua hệ thống phân cấp động chạy, và sẽ cung cấp cho bạn đúngInterface* tương ứng với v[1] (hoặc nullptr, nếu v[1] không có kiểu thời gian chạy này). Khi chúng tôi sửa lỗi đó, sau đó j->specific() in Derived uses Interface, như bạn mong đợi.


& dagger; Có thể, sự cố phải liên quan đến căn chỉnh vtable. Khi bạn thực hiện diễn giải lại, vì Base không có số specific, có thể bù đắp của hàm cụ thể đó với ~Base(), do đó, hiệu quả là bạn trực tiếp gọi hàm hủy - đó là lý do bạn thấy những gì bạn thấy .

+0

Không quan tâm, 'Giao diện * j = static_cast (v [1]);' cũng có vẻ hoạt động, có an toàn không? –

+0

@ChrisDrew Miễn là bạn biết * rằng '* v [1]' thực sự là một thể hiện của 'Derived' (hoặc một lớp dẫn xuất từ ​​đó), nó an toàn. – Angew

+1

@ChrisDrew Có, điều đó tốt vì bạn biết đó là một 'Có nguồn gốc *', do đó, 'static_cast' là OK. Và sau đó con trỏ để xuất phát ('Derived *') đến con trỏ đến cơ sở ('Giao diện *') là một chuyển đổi tiêu chuẩn, mà một trong những luôn luôn là OK. 'dynamic_cast' là an toàn hơn (nhưng chậm hơn) vì nếu' v [1] 'không phải là' Derived * ', bạn sẽ nhận được kết quả được xác định rõ ràng (một con trỏ null) thay vì một con trỏ tìm kiếm hợp lệ mang lại hành vi không xác định nếu bạn sử dụng nó. – Barry