Menilai dari kata-kata pertanyaan Anda (Anda menggunakan kata "sembunyikan"), Anda sudah tahu apa yang sedang terjadi di sini. Fenomena itu disebut "nama bersembunyi". Untuk beberapa alasan, setiap kali seseorang mengajukan pertanyaan tentang mengapa penyembunyian nama terjadi, orang yang menjawab mengatakan bahwa ini disebut "penyembunyian nama" dan menjelaskan cara kerjanya (yang mungkin sudah Anda ketahui), atau menjelaskan cara menimpanya (yang Anda tidak pernah bertanya tentang), tetapi tampaknya tidak ada yang peduli untuk menjawab pertanyaan "mengapa" yang sebenarnya.
Keputusan, alasan di balik nama bersembunyi, yaitu mengapa itu sebenarnya dirancang ke dalam C ++, adalah untuk menghindari perilaku yang berlawanan dengan intuisi, tak terduga dan berpotensi berbahaya yang mungkin terjadi jika rangkaian fungsi kelebihan beban yang diwarisi dibiarkan bercampur dengan perangkat saat ini. kelebihan di kelas yang diberikan. Anda mungkin tahu bahwa dalam C ++ resolusi kelebihan bekerja dengan memilih fungsi terbaik dari sekumpulan kandidat. Ini dilakukan dengan mencocokkan jenis argumen dengan jenis parameter. Aturan-aturan yang cocok kadang-kadang bisa menjadi rumit, dan seringkali mengarah pada hasil yang mungkin dianggap tidak logis oleh pengguna yang tidak siap. Menambahkan fungsi baru ke satu set yang sudah ada sebelumnya mungkin menghasilkan perubahan yang agak drastis dalam hasil resolusi kelebihan beban.
Sebagai contoh, katakanlah kelas dasar B
memiliki fungsi anggotafoo
yang mengambil parameter tipe void *
, dan semua panggilan untuk foo(NULL)
diselesaikan B::foo(void *)
. Katakanlah tidak ada nama yang disembunyikan dan ini B::foo(void *)
terlihat di banyak kelas yang berbeda B
. Namun, katakanlah dalam beberapa keturunan [, remote tidak langsung] D
kelas B
fungsi foo(int)
didefinisikan. Sekarang, tanpa menyembunyikan nama D
memiliki keduanya foo(void *)
dan foo(int)
terlihat serta berpartisipasi dalam resolusi kelebihan. Fungsi mana yang akan foo(NULL)
diselesaikan oleh panggilan , jika dilakukan melalui objek bertipe D
? Mereka akan menyelesaikannya D::foo(int)
, karena int
merupakan kecocokan yang lebih baik untuk integral nol (yaituNULL
) daripada jenis pointer apa pun. Jadi, di seluruh hierarki panggilan untuk foo(NULL)
menyelesaikan ke satu fungsi, sementara di D
(dan di bawah) mereka tiba-tiba memutuskan untuk yang lain.
Contoh lain diberikan dalam Desain dan Evolusi C ++ , halaman 77:
class Base {
int x;
public:
virtual void copy(Base* p) { x = p-> x; }
};
class Derived{
int xx;
public:
virtual void copy(Derived* p) { xx = p->xx; Base::copy(p); }
};
void f(Base a, Derived b)
{
a.copy(&b); // ok: copy Base part of b
b.copy(&a); // error: copy(Base*) is hidden by copy(Derived*)
}
Tanpa aturan ini, status b akan diperbarui sebagian, yang mengarah ke pemotongan.
Perilaku ini dianggap tidak diinginkan ketika bahasa dirancang. Sebagai pendekatan yang lebih baik, diputuskan untuk mengikuti spesifikasi "name hiding", yang berarti bahwa setiap kelas dimulai dengan "clean sheet" sehubungan dengan setiap nama metode yang dinyatakannya. Untuk mengesampingkan perilaku ini, diperlukan tindakan eksplisit dari pengguna: awalnya merupakan deklarasi ulang dari metode yang diwarisi (saat ini tidak digunakan lagi), sekarang merupakan penggunaan eksplisit dari deklarasi penggunaan.
Seperti yang Anda amati dengan benar di posting asli Anda (saya merujuk pada komentar "Bukan polimorfik"), perilaku ini mungkin dilihat sebagai pelanggaran hubungan IS-A antara kelas-kelas. Ini benar, tetapi tampaknya saat itu diputuskan bahwa pada akhirnya nama persembunyian akan terbukti menjadi kejahatan yang lebih kecil.