"Jika Anda benar-benar ingin gula OO - gunakan C ++" - adalah tanggapan langsung yang saya dapatkan dari salah satu teman saya ketika saya menanyakan hal ini. Saya tahu ada dua hal yang salah di sini. OO pertama BUKAN 'gula', dan kedua, C ++ TIDAK menyerap C.
Kita perlu menulis server dalam C (front-end yang akan menggunakan Python), dan jadi saya mencari cara yang lebih baik untuk mengelola program C besar.
Pemodelan sistem besar dalam hal objek, dan interaksi objek membuatnya lebih mudah dikelola, dipelihara, dan diperluas. Tetapi ketika Anda mencoba menerjemahkan model ini ke dalam C yang tidak mengandung objek (dan yang lainnya), Anda ditantang dengan beberapa keputusan penting.
Apakah Anda membuat perpustakaan khusus untuk memberikan abstraksi OO yang dibutuhkan sistem Anda? Hal-hal seperti objek, enkapsulasi, pewarisan, polimorfisme, pengecualian, pub / sub (peristiwa / sinyal), ruang nama, introspeksi, dll. (Misalnya GObject atau COS ).
Atau, Anda cukup menggunakan konstruksi C dasar ( struct
dan fungsi) untuk memperkirakan semua kelas objek Anda (dan abstraksi lainnya) dengan cara ad-hoc. (misalnya, beberapa jawaban untuk pertanyaan ini di SO )
Pendekatan pertama memberi Anda cara terstruktur untuk mengimplementasikan seluruh model Anda dalam C. Tetapi juga menambahkan lapisan kompleksitas yang harus Anda pertahankan. (Ingat, kompleksitas adalah apa yang ingin kita kurangi dengan menggunakan objek sejak awal).
Saya tidak tahu tentang pendekatan kedua, dan seberapa efektif pendekatan ini untuk mendekati semua abstraksi yang mungkin Anda butuhkan.
Jadi, pertanyaan sederhana saya adalah: Apa praktik terbaik dalam mewujudkan desain berorientasi objek di C. Ingat saya tidak meminta BAGAIMANA melakukannya. Ini dan pertanyaan ini membicarakannya, dan bahkan ada buku tentang ini. Yang saya lebih tertarik adalah beberapa saran / contoh realistis yang membahas masalah nyata yang muncul ketika dong ini.
Catatan: tolong jangan menyarankan mengapa C tidak boleh digunakan untuk C ++. Kami telah melewati tahap itu.
sumber
extern "C"
dan dapat digunakan dari python. Anda dapat melakukannya secara manual atau Anda dapat meminta SWIG membantu Anda. Jadi keinginan untuk python frontend bukan alasan untuk tidak menggunakan C ++. Itu tidak mengatakan bahwa tidak ada alasan yang sah untuk tetap tinggal dengan C.Jawaban:
Dari jawaban saya untuk Bagaimana saya harus menyusun proyek kompleks di C (bukan OO tetapi tentang mengelola kompleksitas dalam C):
Dari jawaban saya untuk Apa konvensi penamaan yang khas untuk fungsi publik dan pribadi OO C (saya pikir ini adalah praktik terbaik):
Selain itu, banyak alat UML dapat menghasilkan kode C dari diagram UML. Sumber terbuka adalah Topcased .
sumber
Saya pikir Anda perlu membedakan OO dan C ++ dalam diskusi ini.
Mengimplementasikan objek dimungkinkan dalam C, dan itu cukup mudah - cukup buat struktur dengan pointer fungsi. Itu "pendekatan kedua" Anda, dan saya akan melakukannya. Pilihan lain adalah tidak menggunakan pointer fungsi di struct, melainkan meneruskan struct data ke fungsi dalam panggilan langsung, sebagai pointer "konteks". Itu lebih baik, IMHO, karena lebih mudah dibaca, lebih mudah dilacak, dan memungkinkan penambahan fungsi tanpa mengubah struct (mudah pada warisan jika tidak ada data yang ditambahkan). Itu sebenarnya bagaimana C ++ biasanya mengimplementasikan
this
pointer.Polimorfisme menjadi lebih rumit karena tidak ada warisan bawaan dan dukungan abstraksi, jadi Anda harus memasukkan struct induk ke dalam kelas anak Anda, atau melakukan banyak copy paste, salah satu dari opsi-opsi itu benar-benar mengerikan, walaupun secara teknis sederhana. Sejumlah besar bug menunggu untuk terjadi.
Fungsi virtual dapat dengan mudah dicapai melalui pointer fungsi yang menunjuk ke fungsi yang berbeda seperti yang diperlukan, sekali lagi - sangat rawan bug ketika dilakukan secara manual, banyak pekerjaan yang membosankan dalam menginisialisasi pointer tersebut dengan benar.
Mengenai ruang nama, pengecualian, template, dll - Saya pikir jika Anda terbatas pada C - Anda harus menyerah saja. Saya telah bekerja menulis OO dalam C, tidak akan melakukannya jika saya punya pilihan (di tempat kerja itu saya benar-benar berjuang untuk memperkenalkan C ++, dan para manajer "terkejut" betapa mudahnya mengintegrasikannya. dengan sisa modul C di akhir.).
Tak perlu dikatakan lagi jika Anda bisa menggunakan C ++ - gunakan C ++. Tidak ada alasan untuk tidak melakukannya.
sumber
Berikut adalah dasar-dasar cara membuat orientasi objek di C
1. Membuat Objek dan Enkapsulasi
Biasanya - satu menciptakan objek seperti
object_instance = create_object_typex(parameter);
Metode dapat didefinisikan dalam salah satu dari dua cara di sini.
Perhatikan bahwa dalam kebanyakan kasus,
object_instance (or object_instance_private_data)
dikembalikan adalah tipevoid *.
. Aplikasi tidak dapat menjadi referensi anggota individu atau fungsi ini.Lebih jauh dari ini setiap metode menggunakan object_instance ini untuk metode selanjutnya.
2. Polimorfisme
Kita dapat menggunakan banyak fungsi dan fungsi pointer untuk mengesampingkan fungsionalitas tertentu dalam waktu berjalan.
misalnya - semua object_methods didefinisikan sebagai pointer fungsi yang dapat diperluas ke metode publik maupun pribadi.
Kita juga dapat menerapkan fungsi overloading dalam arti terbatas, dengan cara menggunakan
var_args
yang sangat mirip dengan bagaimana jumlah variabel argumen didefinisikan dalam printf. ya, ini tidak sefleksibel dalam C ++ - tapi ini cara terdekat.3. Menentukan warisan
Mendefinisikan pewarisan agak sulit tetapi seseorang dapat melakukan hal berikut dengan struktur.
di sini
doctor
danengineer
diturunkan dari orang dan dimungkinkan untuk mengetikkannya ke tingkat yang lebih tinggi, katakanlahperson
.Contoh terbaik dari ini digunakan dalam GObject dan objek yang diturunkan dari itu
4. Membuat kelas virtual Saya mengutip contoh kehidupan nyata oleh perpustakaan bernama libjpeg yang digunakan oleh semua browser untuk decoding jpeg. Ini menciptakan kelas virtual yang disebut error_manager dimana aplikasi dapat membuat contoh konkret dan memasok kembali -
Perhatikan di sini bahwa JMETHOD berkembang dalam penunjuk fungsi melalui makro yang perlu dimuat dengan metode yang benar masing-masing.
Saya telah mencoba mengatakan banyak hal tanpa terlalu banyak penjelasan pribadi. Tapi saya berharap orang bisa mencoba barang sendiri. Namun, maksud saya hanya untuk menunjukkan bagaimana hal-hal dipetakan.
Juga, akan ada banyak argumen bahwa ini bukan properti yang benar yang setara dengan C ++. Saya tahu bahwa OO di C-tidak akan seketat itu definisi. Tetapi bekerja seperti itu akan memahami beberapa prinsip inti.
Yang penting bukan bahwa OO seketat di C ++ dan JAVA. Ini adalah bahwa seseorang dapat secara struktural mengatur kode dengan pemikiran OO dalam pikiran dan mengoperasikannya seperti itu.
Saya akan sangat menyarankan orang untuk melihat desain libjpeg yang sebenarnya dan sumber daya berikut
Sebuah. Pemrograman berorientasi objek dalam C
b. ini adalah tempat yang baik di mana orang bertukar ide
c. dan inilah buku lengkapnya
sumber
Orientasi objek bermuara pada tiga hal:
1) Desain program modular dengan kelas otonom.
2) Perlindungan data dengan enkapsulasi pribadi.
3) Warisan / polimorfisme dan berbagai sintaks membantu lainnya seperti konstruktor / destruktor, template dll.
Sejauh ini, 1 adalah yang paling penting, dan juga sepenuhnya bebas bahasa, ini semua tentang desain program. Dalam C Anda melakukan ini dengan membuat "modul kode" otonom yang terdiri dari satu file .h dan satu file .c. Anggap ini setara dengan kelas OO. Anda dapat memutuskan apa yang harus ditempatkan di dalam modul ini melalui akal sehat, UML atau metode apa pun dari desain OO yang Anda gunakan untuk program C ++.
2 juga cukup penting, tidak hanya untuk melindungi akses sengaja yang disengaja ke data pribadi, tetapi juga untuk melindungi terhadap akses yang tidak disengaja, yaitu "namespace clutter". C ++ melakukan ini dengan cara yang lebih elegan daripada C, tetapi masih dapat dicapai dalam C dengan menggunakan kata kunci statis. Semua variabel yang Anda nyatakan sebagai pribadi dalam kelas C ++, harus dinyatakan sebagai statis di C dan ditempatkan pada ruang lingkup file. Mereka hanya dapat diakses dari dalam modul kode mereka sendiri (kelas). Anda dapat menulis "setter / getter" seperti yang Anda lakukan di C ++.
3 bermanfaat tetapi tidak perlu. Anda dapat menulis program OO tanpa warisan atau tanpa konstruktor / penghancur. Hal-hal ini baik untuk dimiliki, mereka pasti dapat membuat program lebih elegan dan mungkin juga lebih aman (atau sebaliknya jika digunakan dengan sembarangan). Tetapi mereka tidak perlu. Karena C tidak mendukung kedua fitur bermanfaat ini, Anda hanya perlu melakukannya tanpa mereka. Konstruktor dapat diganti dengan fungsi init / destruct.
Warisan dapat dilakukan melalui berbagai trik struct, tetapi saya akan menyarankan untuk tidak melakukannya, karena itu mungkin hanya akan membuat program Anda lebih kompleks tanpa keuntungan (warisan secara umum harus diterapkan dengan hati-hati, tidak hanya dalam bahasa C tetapi dalam bahasa apa pun).
Akhirnya, setiap trik OO dapat dilakukan dalam buku C. Axel-Tobias Schreiner "pemrograman berorientasi objek dengan ANSI C" dari awal 90-an membuktikan ini. Namun, saya tidak akan merekomendasikan buku itu kepada siapa pun: itu menambah kompleksitas, tidak menyenangkan aneh untuk program C Anda yang tidak sepadan dengan keributan. (Buku ini tersedia gratis di sini untuk mereka yang masih tertarik meskipun saya sudah diperingatkan.)
Jadi saran saya adalah menerapkan 1) dan 2) di atas dan melewati sisanya. Ini adalah cara penulisan program C yang telah terbukti berhasil selama lebih dari 20 tahun.
sumber
Meminjam beberapa pengalaman dari berbagai runtime Objective-C, menulis kemampuan OO polimorfik yang dinamis dalam C tidak terlalu sulit (membuatnya cepat dan mudah digunakan, di sisi lain, tampaknya masih berlangsung setelah 25 tahun). Namun, jika Anda menerapkan kemampuan objek gaya Objective-C tanpa memperluas sintaks bahasa maka kode yang Anda gunakan cukup berantakan:
objc_msgSend(object, selector, …)
. Dengan mengetahui kelas apa objek adalah instance, ia dapat menemukan implementasi yang cocok dengan pemilih dan dengan demikian menjalankan fungsi yang benar.Itu semua adalah bagian dari pustaka tujuan umum OO yang dirancang untuk memungkinkan banyak pengembang untuk menggunakan dan memperluas kelas masing-masing, sehingga bisa jadi berlebihan untuk proyek Anda sendiri. Saya sering mendesain proyek C sebagai proyek "statis" yang berorientasi pada kelas dengan menggunakan struktur dan fungsi: - setiap kelas adalah definisi dari struktur C yang menetapkan tata letak ivar - setiap contoh hanyalah contoh dari struktur yang sesuai - objek tidak dapat "Pesan", tetapi fungsi seperti metode terlihat seperti
MyClass_doSomething(struct MyClass *object, …)
didefinisikan. Ini membuat hal-hal lebih jelas dalam kode daripada pendekatan ObjC tetapi kurang fleksibel.Tempat trade-off terletak tergantung pada proyek Anda sendiri: kedengarannya seperti programmer lain tidak akan menggunakan antarmuka C Anda sehingga pilihan turun ke preferensi internal. Tentu saja, jika Anda memutuskan ingin sesuatu seperti perpustakaan runtime objc, maka ada perpustakaan runtime objc lintas-platform yang akan melayani.
sumber
GObject tidak benar-benar menyembunyikan kompleksitas apa pun dan memperkenalkan beberapa kompleksitasnya sendiri. Saya akan mengatakan bahwa hal ad-hoc lebih mudah daripada GObject kecuali Anda memerlukan hal-hal GObject canggih seperti sinyal atau mesin antarmuka.
Masalahnya sedikit berbeda dengan COS karena yang datang dengan preprocessor yang memperluas sintaks C dengan beberapa konstruksi OO. Ada preprocessor serupa untuk GObject, para G Object Builder .
Anda juga bisa mencoba bahasa pemrograman Vala , yang merupakan bahasa tingkat tinggi penuh yang dikompilasi ke C dan dengan cara yang memungkinkan menggunakan perpustakaan Vala dari kode C biasa. Ia dapat menggunakan GObject, kerangka objeknya sendiri atau cara ad-hoc (dengan fitur terbatas).
sumber
Pertama, kecuali ini adalah pekerjaan rumah atau tidak ada kompiler C ++ untuk perangkat target Saya tidak berpikir Anda harus menggunakan C, Anda dapat menyediakan antarmuka C dengan tautan C dari C ++ dengan cukup mudah.
Kedua, saya akan melihat seberapa besar Anda akan bergantung pada polimorfisme dan pengecualian dan fitur-fitur lain yang dapat disediakan kerangka kerja, jika tidak banyak struct sederhana dengan fungsi terkait akan jauh lebih mudah daripada kerangka berfitur lengkap, jika sebagian besar dari Anda desain membutuhkan mereka kemudian menggigit peluru dan menggunakan kerangka kerja sehingga Anda tidak harus mengimplementasikan fitur sendiri.
Jika Anda belum benar-benar memiliki desain untuk membuat keputusan, maka lakukan spike dan lihat apa yang dikatakan kode tersebut kepada Anda.
terakhir itu tidak harus menjadi satu atau pilihan lain (meskipun jika Anda pergi kerangka kerja dari awal Anda mungkin juga tetap dengan itu), harus dimungkinkan untuk memulai dengan struct sederhana untuk bagian-bagian sederhana dan hanya menambahkan perpustakaan sesuai kebutuhan.
EDIT: Jadi keputusan oleh manajemen yang tepat mari kita abaikan poin pertama.
sumber