Downcast dùng dynamic_cast trả về NULL nhưng vẫn có thể gọi hàm?
Hi mọi người, vừa qua trong quá trình tìm hiểu downcast mình có một thắc mắc nhỏ như sau:
Giả sử mình có 2 class A và B kế thừa A :
class A{
public:
virtual void Func() { cout << "in A" << endl; }
};
class B : public A
{
public:
virtual void Func() { cout << "in B" << endl; }
void Jump() {cout << "B can fly" << endl;}
};
Trong hàm main() mình thực hiện như sau:
A* ob = new A();
if (dynamic_cast<B*>(ob) == NULL)
cout << "Null" << endl;
dynamic_cast<B*>(ob)->Jump();
Như vậy mình đã thực hiện downcast con trỏ ob từ kiểu Base xuống Derive. Kết quả của việc cast này trả ra Null nhưng khi mình dùng nó để truy xuất hàm Jump thì vẫn truy xuất thành công??? Đây là điều mà mình thắc mắc tại sao lại có thể truy xuất được bằng con trỏ Null!?
Bên cạnh đó, khi mình dùng con trỏ này truy xuất 1 hàm virtual, cụ thể là hàm Func() thì bị dump ngay
Đây là một câu hỏi hay mà khó Phải lật lại sách coi mới giải thích được.
Đây đúng là con trỏ
NULL
, nhưng làNULL
pointer có kiểuB
(Derived). Khi nó gọi hàmJump
thì nó vẫn thực hiện được. Vì con trỏ kiểuB
biết được nó có hàmJump
. Nhưng ở đây Khang còn may mắn một điều nữa đó là hàmJump
này là hàmnon-virtual
(không cần phải trỏ tới vtable) và không sử dụng bất cứ thứ gì thuộc về một class riêng biệt (tức là con trỏ this).Jump
như sau, có sử dụng con trỏthis
.Thì khi này con trỏ
NULL
kiểuB
sẽ chết ngay, vì nó không biếtx
ở đâu.Trong trường hợp này ta đã biết
ob
là con trỏNULL
kiểuB
. Con trỏNULL
thì nó không cóvtable
, hay nói chính xác hơn là nó không trỏ tớivtable
nào cả. Vì thế cho nên khi ta gọi tới hàm virtual thì sẽ bị crash.He He thank a @ltd. Ngon lành rồi. Hồi đầu e cũng có thử tạo thêm 1 biến ở class B. Lúc đó xài còn trỏ Null là banh ngay Ok e hiểu được vấn đề rồi. Nhân tiện e có thể thắc mắc thêm 1 tí về cái dynamic_cast và vtable ko ạ?
Bonus Querry: E đọc trên mạng và tài liệu, đa phần đều nói dynamic_cast dựa vào thông tin của vtable để thực hiện nhưng không thấy nói rõ. A có thể hiện thực nó ra bằng hình ảnh 1 tí được không
Anh thấy video này nói về vtable rất trực quan, anh hiểu được vtable là nhờ video này
Mình thử làm 1 bài test nhỏ:
Và đây là hàm main, ( mình compile trên x64)
Kết quả: ta thấy rằng chỉ class nào có virtual function thì mới có vpointer. (Trong các tài liệu C++ đều có nói về cái này, mình làm test thử để chắc thêm )
Việc downcast từ A xuống B không thể tạo ra thêm variable được (cũng như vd của a Đạt thêm 1 biến int vào, vì mình new A, thì chỉ đc cấp phát memory bằng size của A thôi). Nên nếu gọi 1 virtual function thì sẽ bị dump ngay, vì virtual function phải được gọi bằng vpointer trỏ tới vtable. Mà lúc này ta làm gì có vpointer của class A
Còn việc so sánh với NULL, thử để function gọi hàm của B trong block của if xem thử nó có gọi đc ko. Có thể trong C++ mình nên so sánh với nullptr thì hay hơn là NULL, vì NULL chỉ là:
Và việc gọi được hàm của B thì chắc là do cách nó phân giải lời gọi hàm thông qua cái gọi là name mangling trong C++ lúc compiled time
Câu hỏi này hay quá
Thank man, sắp tới sẽ cố gắng làm rõ được cái vpointer cái pointer trỏ tới class nó liên hệ sao. Vì lúc downcast nó có sử dụng thông tin của Vtable để lựa chọn class derive. Mà cài này còn đang hiểu nhập nhằng quá chưa share dc
Nhưng về bản chất thì virtual giúp mình hiện thực từ class cha xuống để sử dụng nhiều class con. Cũng ít phổ biến việc sử dụng downcast từ class cha xuống. Chỉ là lúc làm cái này thấy nó kì nên bay lên hỏi