2012-04-16 41 views
6

đề cập khác so questionLẫn lộn về tên lẩn trốn và chức năng ảo

xem xét mã:

class Base { 
public: 
    virtual void gogo(int a){ 
     printf(" Base :: gogo (int) \n"); 
    }; 

    virtual void gogo(int* a){ 
     printf(" Base :: gogo (int*) \n"); 
    }; 
}; 

class Derived : public Base{ 
public: 
    virtual void gogo(int* a){ 
     printf(" Derived :: gogo (int*) \n"); 
    }; 
}; 

int main(){ 

    // 1)  
    Derived * obj = new Derived ; 
    obj->gogo(7); // this is illegal because of name hiding 


    // 2)  
    Base* obj = new Derived ; 
    obj->gogo(7); // this is legal 
} 

Đối với trường hợp 2)

Cuộc gọi obj->gogo(7) được giải quyết tại thời gian chạy.

obj->gogo(7) là hợp pháp. Dường như ngụ ý rằng vtable của Derived chứa ptr đến virtual void gogo(int a) cần được ẩn.

nhầm lẫn của tôi là, kể từ khi tên ẩn gây ra trường hợp 1) là bất hợp pháp, thì làm sao các cuộc gọi trong 2) đã được giải quyết tại thời gian chạy

a) Liệu vtable của nguồn gốc chứa con trỏ đến gogo (int).

b) Nếu a) không đúng, Độ phân giải cuộc gọi cho các hàm ảo có thể chuyển thành vtable của lớp cơ sở hay không.

+0

@AndersK Hàm 'Base :: gogo (int)' thực sự bị ẩn bởi 'Derived :: gogo (int *)'. Nhưng câu lệnh 'using Base :: gogo;' trong lớp 'Derived' sẽ giải quyết vấn đề cụ thể này. –

+0

@MichaelWild yep, tôi đã thấy sai lầm của mình. –

Trả lời

0

Vì bạn đã khai báo số thứ hai objBase*, vtable cung cấp tất cả các phương thức Base. Mặc dù cho các phương thức ảo đã được overriden bởi Derived, các phiên bản overriden được gọi là, các phương pháp khác (hoặc phương pháp quá tải) vẫn là những người đã được tuyên bố trong Base.

Tuy nhiên, nếu bạn khai báo con trỏ là Derived*, vtable sẽ cung cấp cho nó phương thức Derived, ẩn các tên có cùng tên trong Base. Do đó, obj->gogo(7); sẽ không hoạt động. Tương tự như vậy, điều này cũng là bất hợp pháp:

Base* obj = new Derived(); 

// legal, since obj is a pointer to Base, it contains the gogo(int) method. 
obj->gogo(7); 

// illegal, it has been cast into a pointer to Derived. gogo(int) is hidden. 
(reinterpret_cast<Derived*>(obj))->gogo(7); 

này là hợp pháp:

Derived* obj = new Derived ; 
obj->Base::gogo(7); // legal. 

Xem here.

+0

Sẽ không phải là 'dynamic_cast' thích hợp hơn trong ví dụ của bạn? – Griwes

+0

Theo tôi biết điều này không liên quan gì đến việc tìm kiếm 'vtable' thực tế nhưng với việc đánh máy tĩnh và giấu tên (xem câu trả lời của Bo Persson). Nếu kiểu là 'Derived', các ngữ nghĩa đó được áp dụng, nếu kiểu là' Base', thì ngữ nghĩa khác sẽ được áp dụng. – KillianDS

5

Bạn đang nhầm lẫn các cuộc gọi chức năng ảo và độ phân giải quá tải.

Tất cả các lớp dẫn xuất đều có vtables chứa tất cả các hàm ảo, từ lớp cơ sở và bất kỳ hàm ảo riêng bổ sung nào. Điều này được sử dụng để giải quyết các cuộc gọi tại thời gian chạy, như trong trường hợp của bạn 2).

Trong trường hợp 1) bạn gặp lỗi do quá tải ở độ phân giải thời gian biên dịch. Do ẩn tên, lớp Derived chỉ có một hàm có thể gọi. Lựa chọn duy nhất của bạn là gọi hàm đó, với số int*.

+0

Không có lỗi nào. Vì cả hai 'gogo()' overloads đã được khai báo trong 'Base', việc ghi đè lên một sẽ không ẩn cái kia. –

+0

điều này giải thích hành vi trên. C++ tiêu chuẩn (hoặc bất kỳ cuốn sách/tài liệu) mô tả điều tương tự. Tôi có nghĩa là nó có thể nhận được một trích dẫn. – fizzbuzz

+0

@fizzbuzz - Tên được khai báo trong phạm vi bên trong luôn ẩn tên trong phạm vi bên ngoài. Trong trường hợp này, lớp dẫn xuất là phạm vi bên trong và lớp cơ sở phạm vi bên ngoài. –

1

Về cơ bản, quá tải hàm chỉ xảy ra khi các hàm cùng tên được định nghĩa trong cùng một phạm vi. Bây giờ, lớp cơ sở có phạm vi và lớp dẫn xuất riêng của nó có riêng của nó. Vì vậy, khi bạn không xác định lại một hàm trong lớp dẫn xuất và gọi hàm đó, trình biên dịch sẽ kiểm tra phạm vi bắt nguồn, phát hiện ra rằng không có hàm nào được định nghĩa trong đó. Sau đó, nó kiểm tra phạm vi của lớp cơ sở, phát hiện ra hàm và theo đó liên kết việc gọi hàm đến định nghĩa cụ thể này.

Tuy nhiên, khi bạn xác định lại chức năng có chữ ký khác nhau, trình biên dịch khớp với cuộc gọi với chức năng này, thấy sự thiếu nhất quán và đơn giản là than phiền.

bạn có thể thay đổi hành vi này bằng cách thêm "using Base :: gogo;" trong defenision class có nguồn gốc. Tôi hy vọng điều này giải thích.

2

Bạn muốn ghi đè hàm quá tải nhưng các quy tắc ẩn không hoạt động như thế này.

"Quy tắc ẩn nói rằng một thực thể trong phạm vi bên trong ẩn những thứ có cùng tên trong phạm vi bên ngoài".

Lưu ý rằng không có chữ ký nào khác nhau, tức là gogo(int* a) sẽ ẩn tất cả các chức năng gogo(whatever) từ Cơ sở. Ghi đè chỉ xảy ra khi các hàm của bạn có cùng tên, cùng chữ ký và ảo (do đó, chỉ gogo(int* a) sẽ bị ghi đè).

Sách C++ FAQ đề xuất sử dụng "quá tải ảo không gọi là ảo không quá tải". (chương 29.05). Về cơ bản bạn tạo phi ảo chức năng quá tải trong lớp cơ sở:

gogo(int a) and gogo(int* a)

mà sẽ gọi các hàm ảo tương ứng:

gogo_i ảo (int a) và gogo_pi ảo (int * a)

Và ghi đè các ảo này trong lớp Có nguồn gốc.