Sebelumnya, saya hanya menggunakan bahasa Pemrograman Berorientasi Objek (C ++, Ruby, Python, PHP), dan sekarang saya sedang belajar C. Saya merasa sulit untuk menemukan cara yang tepat untuk melakukan sesuatu dalam suatu bahasa tanpa konsep suatu 'Obyek'. Saya menyadari bahwa mungkin untuk menggunakan paradigma OOP dalam C, tapi saya ingin belajar cara C-idiomatik.
Saat memecahkan masalah pemrograman, hal pertama yang saya lakukan adalah membayangkan sebuah objek yang akan menyelesaikan masalah. Langkah apa yang saya ganti dengan ini, ketika menggunakan paradigma Pemrograman Imperatif non-OOP?
object-oriented
c
Mas Bagol
sumber
sumber
qux = foo.bar(baz)
menjadiqux = Foo_bar(foo, baz)
.Jawaban:
struct
.Itu dia.
Bagaimana Anda menulis kelas? Itu cukup banyak bagaimana Anda menulis file .C. Memang, Anda tidak mendapatkan hal-hal seperti metode polimorfisme dan pewarisan, tetapi Anda dapat mensimulasikan mereka dengan nama fungsi dan komposisi yang berbeda pula.
Untuk membuka jalan, pelajari Pemrograman Fungsional. Sungguh menakjubkan apa yang dapat Anda lakukan tanpa kelas, dan beberapa hal benar-benar berfungsi lebih baik tanpa overhead kelas.
Orientasi Objek Bacaan Lebih Lanjut
dalam ANSI C
sumber
typedef
itustruct
dan membuat sesuatu seperti kelas . dantypedef
tipe -ed dapat dimasukkan ke dalam tipe-tipe lainstruct
yang dapat digunakan sendiritypedef
. apa yang tidak Anda dapatkan dengan C adalah kelebihan operator dan warisan kelas yang dangkal dan anggota di dalamnya Anda dapatkan di C ++. dan Anda tidak mendapatkan banyak sintaks aneh dan tidak wajar yang Anda dapatkan dengan C ++. Saya sangat menyukai konsep OOP, tetapi saya pikir C ++ adalah realisasi OOP yang jelek. Saya suka C karena itu adalah bahasa yang lebih kecil dan tidak menggunakan sintaks dari bahasa yang lebih baik dibiarkan berfungsi.a lot
of things actually work better without the overhead of classes
a lot of things actually work better with addition of class-based OOP
. Sumber: TypeScript, Dart, CoffeeScript dan semua cara lain industri berusaha untuk menjauh dari bahasa OOP fungsional / prototipe.Baca SICP dan pelajari Skema, dan ide praktis tipe data abstrak . Maka coding dalam C itu mudah (karena dengan SICP, sedikit C, dan sedikit PHP, Ruby, dll ... pemikiran Anda akan cukup luas, dan Anda akan mengerti bahwa pemrograman berorientasi objek mungkin bukan gaya terbaik di semua kasus, tetapi hanya untuk beberapa jenis program). Hati-hati dengan alokasi memori dinamis C , yang mungkin merupakan bagian tersulit. The C99 atau C11 standar bahasa pemrograman dan yang C standar perpustakaan sebenarnya sangat miskin (tidak tahu tentang TCP atau direktori!), Dan Anda akan sering perlu beberapa perpustakaan eksternal atau antarmuka (misalnyaPOSIX , libcurl untuk perpustakaan klien HTTP, libonion untuk perpustakaan server HTTP, GMPlib untuk bignum, beberapa perpustakaan seperti libunistring untuk UTF-8, dll ...).
"Objek" Anda sering kali dalam C
struct
-s terkait , dan Anda menentukan set fungsi yang beroperasi pada mereka. Untuk fungsi pendek atau sangat sederhana, pertimbangkan untuk mendefinisikannya, dengan yang relevanstruct
, sepertistatic inline
pada beberapa file headerfoo.h
menjadi#include
-d di tempat lain.Perhatikan bahwa pemrograman berorientasi objek bukan satu-satunya paradigma pemrograman . Dalam beberapa kesempatan, paradigma lain bermanfaat ( pemrograman fungsional à la Ocaml atau Haskell atau bahkan Skema atau Commmon Lisp, pemrograman logika à la Prolog, dll. ... Baca juga blog J.Pitrat tentang deklarasi kecerdasan buatan). Lihat buku Scott: Bahasa Pemrograman Pragmatik
Sebenarnya, seorang programmer di C, atau di Ocaml, biasanya tidak ingin kode dalam gaya pemrograman berorientasi objek. Tidak ada alasan untuk memaksa diri Anda memikirkan benda-benda saat itu tidak berguna.
Anda akan mendefinisikan beberapa
struct
dan fungsi-fungsi yang beroperasi pada mereka (seringkali melalui pointer). Anda bisa memerlukan beberapa serikat yang ditandai (sering, astruct
dengan anggota tag, sering beberapaenum
, dan beberapaunion
di dalam), dan Anda mungkin menemukan berguna untuk memiliki anggota array yang fleksibel di akhir beberapastruct
-s Anda .Lihat di dalam kode sumber dari beberapa perangkat lunak gratis yang ada di C (lihat github & sourceforge untuk menemukan beberapa). Mungkin, menginstal dan menggunakan distribusi Linux akan berguna: dibuat hampir hanya dari perangkat lunak bebas, ia memiliki kompiler perangkat lunak C gratis yang hebat ( GCC , Dentang / LLVM ) dan alat pengembangan. Lihat juga Pemrograman Linux Lanjut jika Anda ingin mengembangkan untuk Linux.
Jangan lupa untuk mengompilasi dengan semua peringatan dan info debug, misalnya
gcc -Wall -Wextra -g
-terutama selama fase pengembangan & debugging- dan belajar menggunakan beberapa alat, misalnya valgrind untuk memburu kebocoran memori ,gdb
debugger, dll. Berhati-hatilah untuk memahami dengan baik apa yang tidak terdefinisi perilaku dan sangat menghindarinya (ingat bahwa suatu program dapat memiliki beberapa UB dan kadang-kadang tampaknya "bekerja").Ketika Anda benar-benar membutuhkan konstruksi berorientasi objek (khususnya warisan ), Anda dapat menggunakan pointer ke struktur terkait dan fungsi. Anda bisa memiliki mesin vtable Anda sendiri , masing-masing memiliki "objek" dimulai dengan pointer ke pointer
struct
fungsi yang mengandung. Anda mengambil keuntungan dari kemampuan untuk melemparkan tipe pointer ke tipe pointer lain (dan fakta bahwa Anda bisa melemparkan dari yangstruct super_st
berisi jenis bidang yang sama dengan yang memulai astruct sub_st
untuk meniru warisan). Perhatikan bahwa C cukup untuk mengimplementasikan sistem objek yang cukup canggih - khususnya dengan mengikuti beberapa konvensi -, seperti diperlihatkan GObject (dari GTK / Gnome).Ketika Anda benar-benar membutuhkan penutupan , Anda akan sering meniru mereka dengan panggilan balik , dengan konvensi bahwa setiap fungsi yang menggunakan panggilan balik dilewatkan baik oleh pointer fungsi dan beberapa data klien (dikonsumsi oleh pointer fungsi ketika memanggil itu). Anda juga dapat memiliki (secara konvensional) closure-like
struct
-s Anda sendiri (mengandung beberapa pointer fungsi dan nilai-nilai tertutup).Karena C adalah bahasa tingkat yang sangat rendah, penting untuk mendefinisikan dan mendokumentasikan konvensi Anda sendiri (terinspirasi oleh praktik dalam program C lainnya), khususnya tentang manajemen memori, dan mungkin beberapa konvensi penamaan juga. Sangat berguna untuk memiliki beberapa gagasan tentang arsitektur set instruksi . Jangan lupa bahwa sebuah C compiler dapat melakukan banyak optimasi pada kode Anda (jika Anda bertanya kepada), sehingga tidak peduli terlalu banyak tentang melakukan mikro-optimasi dengan tangan, cuti bahwa untuk compiler Anda (
gcc -Wall -O2
untuk kompilasi dioptimalkan dirilis perangkat lunak). Jika Anda peduli tentang tolok ukur dan kinerja mentah, Anda harus mengaktifkan pengoptimalan (setelah program Anda didebug).Jangan lupa bahwa kadang - kadang metaprogramming berguna . Cukup sering, perangkat lunak besar yang ditulis dalam C berisi beberapa skrip atau program ad-hoc untuk menghasilkan beberapa kode C yang digunakan di tempat lain (dan Anda mungkin juga memainkan beberapa trik preprosesor C yang kotor , misalnya X-macro ). Ada beberapa generator program C yang berguna (mis. Yacc atau gnu bison untuk menghasilkan parser, gperf untuk menghasilkan fungsi hash yang sempurna, dll ...). Pada beberapa sistem (terutama Linux & POSIX) Anda bahkan dapat menghasilkan beberapa kode C saat runtime dalam
generated-001.c
file, kompilasi ke objek bersama dengan menjalankan beberapa perintah (sepertigcc -O -Wall -shared -fPIC generated-001.c -o generated-001.so
) saat runtime, secara dinamis memuat objek yang dibagikan menggunakan dlopen& dapatkan penunjuk fungsi dari nama menggunakan dlsym . Saya melakukan trik-trik semacam itu dalam MELT (bahasa khusus domain mirip Lisp yang mungkin berguna bagi Anda, karena memungkinkan penyesuaian dari kompiler GCC ).Waspadai konsep dan teknik pengumpulan sampah ( penghitungan referensi sering kali merupakan teknik untuk mengelola memori dalam C, dan itu adalah bentuk buruk pengumpulan sampah yang tidak menangani dengan baik dengan referensi melingkar ; Anda bisa memiliki petunjuk lemah untuk membantu tentang itu, tapi mungkin rumit). Pada beberapa kesempatan, Anda mungkin mempertimbangkan untuk menggunakan pengumpul sampah Boehm yang konservatif .
sumber
Cara program dibangun pada dasarnya mendefinisikan tindakan (fungsi) mana yang harus dilakukan untuk menyelesaikan masalah (itulah sebabnya itu disebut bahasa prosedural). Setiap tindakan akan sesuai dengan suatu fungsi. Kemudian Anda perlu menentukan jenis informasi apa yang masing-masing fungsi akan terima dan informasi apa yang mereka butuhkan untuk kembali.
Program biasanya dipisahkan dalam file (modul) setiap file biasanya akan memiliki sekelompok fungsi yang terkait. Di awal setiap file Anda mendeklarasikan (di luar fungsi apa pun) variabel yang akan digunakan oleh semua fungsi dalam file itu. Jika Anda menggunakan kualifikasi "statis" variabel-variabel itu hanya akan terlihat di dalam file itu (tetapi tidak dari file lain). Jika Anda tidak menggunakan kualifikasi "statis" pada variabel yang ditentukan di luar fungsi, mereka akan dapat diakses dari file lain juga dan file-file lain ini harus mendeklarasikan variabel sebagai "extern" (tetapi tidak mendefinisikannya) sehingga kompiler akan mencari mereka dalam file lain.
Jadi singkatnya Anda pertama kali berpikir tentang prosedur (fungsi) maka Anda memastikan semua fungsi memiliki akses ke informasi yang mereka butuhkan.
sumber
API C sering - mungkin bahkan, biasanya - memang memiliki antarmuka berorientasi objek jika Anda melihatnya dengan cara yang benar.
Dalam C ++:
Dalam C:
Seperti yang Anda ketahui, dalam C ++ dan berbagai bahasa OO formal lainnya, di bawah tenda metode objek mengambil argumen pertama yang merupakan pointer ke objek, seperti versi C di
bar()
atas. Untuk contoh di mana ini muncul ke permukaan dalam C ++, pertimbangkan bagaimanastd::bind
dapat digunakan agar sesuai dengan metode objek untuk berfungsi tanda tangan:Seperti yang telah ditunjukkan orang lain, perbedaan nyata adalah bahwa bahasa OO formal dapat menerapkan polimorfisme, kontrol akses, dan berbagai fitur bagus lainnya. Tetapi esensi pemrograman berorientasi objek, penciptaan dan manipulasi struktur data yang kompleks dan diskrit, sudah merupakan praktik mendasar dalam C.
sumber
Salah satu alasan utama orang didorong untuk belajar C adalah itu salah satu bahasa pemrograman tingkat tinggi yang paling rendah. Bahasa OOP membuatnya lebih mudah untuk berpikir tentang model data dan templating code dan message passing, tetapi pada akhirnya, mikroprosesor mengeksekusi kode langkah-demi-langkah, melompat masuk dan keluar dari blok kode (fungsi dalam C) dan memindahkan referensi ke variabel (pointer di C) sekitar sehingga bagian yang berbeda dari suatu program dapat berbagi data. Anggaplah C sebagai bahasa rakitan dalam bahasa Inggris - memberikan instruksi langkah demi langkah ke mikroprosesor komputer Anda - dan Anda tidak akan salah. Sebagai bonus, sebagian besar antarmuka sistem operasi bekerja seperti panggilan fungsi C daripada paradigma OOP,
sumber
uint16_t blah(uint16_t x) {return x*x;}
akan bekerja secara identik pada mesinunsigned int
yang 16 bit, atau 33 bit atau lebih besar. Beberapa kompiler untuk mesin denganunsigned int
17 hingga 32 bit, bagaimanapun, mungkin menganggap panggilan ke metode itu ...uint16_t
, akan menghasilkan 9, Standar tidak mengamanatkan perilaku seperti itu ketika mengalikan nilai tipeuint16_t
pada platform 17 hingga 32-bit.Saya juga I OO asli (C ++ umumnya) yang kadang-kadang harus bertahan hidup di dunia C. Bagi saya rintangan terbesar pada dasarnya adalah berurusan dengan penanganan kesalahan dan manajemen sumber daya.
Dalam C ++ kita harus melempar untuk melewati kesalahan dari tempat itu terjadi sepanjang perjalanan kembali ke tingkat atas di mana kita dapat menghadapinya dan kita memiliki destruktor untuk secara otomatis membebaskan memori kita dan sumber daya lainnya.
Anda mungkin memperhatikan bahwa banyak API C menyertakan fungsi init yang memberi Anda kekosongan yang sudah diketik * yang benar-benar merupakan pointer ke struct. Lalu Anda meneruskan ini sebagai argumen pertama untuk setiap panggilan API. Intinya itu menjadi pointer "ini" Anda dari C ++. Ini akan digunakan untuk semua struktur data internal yang disembunyikan (konsep yang sangat OO). Anda juga dapat menggunakannya untuk mengelola memori, mis. Memiliki fungsi yang disebut myapiMalloc yang mengosongkan memori Anda dan mencatat malloc dalam versi C dari penunjuk ini sehingga Anda dapat memastikannya dibebaskan ketika API Anda kembali. Juga seperti yang saya temukan baru-baru ini Anda dapat menggunakannya untuk menyimpan kode kesalahan dan menggunakan setjmp dan longjmp untuk memberi Anda perilaku yang sangat mirip dengan melempar tangkapan. Menggabungkan kedua konsep memberi Anda banyak fungsi dari program C ++.
Sekarang Anda memang mengatakan bahwa Anda tidak ingin belajar memaksa C menjadi C ++. Itu bukan yang saya jelaskan (setidaknya tidak sengaja). Ini hanyalah metode (mudah-mudahan) dirancang dengan baik untuk mengeksploitasi fungsionalitas C. Ternyata memiliki beberapa rasa OO - mungkin itu sebabnya bahasa OO dikembangkan, mereka adalah cara untuk memformalkan / menegakkan / memfasilitasi konsep yang beberapa orang temukan sebagai praktik terbaik.
Jika Anda pikir ini untuk OO merasa untuk Anda maka alternatifnya adalah memiliki hampir setiap fungsi mengembalikan kode kesalahan yang Anda harus memastikan Anda memeriksa setiap fungsi panggilan dan menyebarkan tumpukan panggilan. Anda harus memastikan bahwa semua sumber daya dibebaskan tidak hanya di akhir setiap fungsi, tetapi di setiap titik pengembalian (yang berpotensi terjadi setelah setiap panggilan fungsi yang dapat mengembalikan kesalahan yang menunjukkan Anda tidak dapat melanjutkan). Ini bisa menjadi sangat membosankan dan cenderung membuat Anda berpikir saya mungkin tidak perlu berurusan dengan kegagalan alokasi memori potensial (atau file read atau port connect ...), saya hanya akan berasumsi bahwa itu akan berfungsi atau saya ' Saya akan menulis kode "menarik" sekarang dan kembali dan berurusan dengan penanganan kesalahan - yang tidak pernah terjadi.
sumber