Apakah situasinya akrab ketika tempat pada flashdisk telah habis, dan Anda perlu mendorong yang tidak termakan, mengorbankan sesuatu yang Anda butuhkan? Mari kita coba untuk mengorbankan yang tidak perlu, itu bersembunyi di tempat yang agak tak terduga.
Saya ingin membuat server telnet untuk mengendalikan berbagai peralatan pada modul WIZnet W5500 yang populer dan murah. Semua yang diperlukan untuk ini adalah bagian dari perpustakaan standar Arduino, hasilnya dapat ditemukan di
sini . Tapi ini bukan tentang dia. Hal pertama yang benar-benar mengejutkan saya adalah bahwa ini sederhana dalam kode fungsi mengambil lebih dari setengah dari flash ATmega328P. Tentu saja, ada banyak kode di perpustakaan Ethernet, tetapi tidak semuanya digunakan, kompiler harus membuang kode yang tidak digunakan dari firmware yang dirakit. Periksa apakah ini benar.
Kami pergi ke direktori tempat perakitan berlangsung - jalan menuju itu dapat dilihat dalam pesan kompilasi, dan lakukan objdump -t <elf firmware file> untuk mendapatkan tabel karakter. Kami melihat di dalamnya banyak fungsi yang berhubungan dengan Ethernet, termasuk yang kebutuhannya tidak jelas, misalnya fungsi untuk bekerja dengan UDP. Artinya, seolah-olah penghapusan fungsi yang tidak perlu tidak terjadi. Ada apa?
Jawabannya mungkin tampak tidak terduga - semuanya adalah warisan kelas yang mengimplementasikan Ethernet dari kelas dasar dengan banyak fungsi virtual. Kompilator menganggap bahwa suatu fungsi (atau metode kelas) digunakan ketika ada tautan ke sana di tempat lain dalam kode. Tetapi untuk membuat tautan seperti itu, fungsinya tidak harus dipanggil. Sudah cukup untuk menyimpan alamatnya. Bahkan jika kita tidak melakukannya secara eksplisit, C ++ melakukannya untuk kita dengan menempatkan pointer ke suatu fungsi di tabel fungsi virtual. Bahkan jika kita tidak pernah menggunakan fungsi virtual ini, itu akan ada di firmware. Jika suatu fungsi didefinisikan dalam kelas dasar sebagai virtual murni (tanpa implementasi), maka kita tidak memiliki pilihan lain selain untuk mengimplementasikannya, bahkan jika kita tidak membutuhkannya sama sekali, sehingga meningkatkan ukuran kode firmware.
Kami memverifikasi kebenaran hipotesis kami. Ambil pustaka Ethernet dari github, misalnya di
sini , agar tidak menyentuh standar, dan memodifikasinya. Kami menghapus warisan, dan kami membuat fungsi virtual hanya dengan metode. Cara melakukannya dengan hati-hati, dengan cara yang dapat dibalik, bisa Anda lihat di
sini . Hasil: ukuran kode berkurang 4460 byte - lebih dari seperempat dari ukuran aslinya.
Tentu saja, fungsi pewarisan dan virtual berguna. Namun, membuat kelas dasar dengan fungsi virtual murni hanya untuk mendefinisikan antarmuka untuk implementasi selanjutnya tidak selalu dibenarkan. Pertama, Anda perlu memastikan bahwa Anda benar-benar akan menggunakan antarmuka ini dengan objek dari tipe yang berbeda, atau fungsi yang diterapkan di kelas dasar (seperti di kelas Print) akan berguna bagi Anda.