Dengan asumsi saya harus menggunakan C (tidak ada C ++ atau penyusun berorientasi objek) dan saya tidak memiliki alokasi memori dinamis, teknik apa sajakah yang dapat saya gunakan untuk mengimplementasikan suatu kelas, atau perkiraan yang baik dari suatu kelas? Apakah selalu merupakan ide yang baik untuk mengisolasi "kelas" ke file yang terpisah? Asumsikan bahwa kita dapat melakukan pra-alokasi memori dengan mengasumsikan jumlah instance tetap, atau bahkan mendefinisikan referensi ke setiap objek sebagai konstanta sebelum waktu kompilasi. Jangan ragu untuk membuat asumsi tentang konsep OOP mana yang perlu saya terapkan (akan bervariasi) dan menyarankan metode terbaik untuk masing-masing.
Pembatasan:
- Saya harus menggunakan C dan bukan OOP karena saya menulis kode untuk sistem tertanam, dan kompiler dan basis kode yang sudah ada di C.
- Tidak ada alokasi memori dinamis karena kami tidak memiliki cukup memori untuk menganggap wajar kami tidak akan kehabisan jika kami mulai mengalokasikannya secara dinamis.
- Compiler yang bekerja dengan kami tidak memiliki masalah dengan pointer fungsi
apr_
dan GLib memberinya awalang_
untuk membuat namespace) dan faktor pengorganisasian lainnya tanpa OOP. Jika Anda akan merestrukturisasi aplikasi, saya akan mempertimbangkan meluangkan waktu untuk mencoba membuat struktur prosedural yang lebih mudah dikelola.Jawaban:
Itu tergantung pada set fitur "berorientasi objek" yang tepat yang ingin Anda miliki. Jika Anda membutuhkan hal-hal seperti overloading dan / atau metode virtual, Anda mungkin perlu menyertakan pointer fungsi dalam struktur:
Ini akan memungkinkan Anda menerapkan kelas, dengan "mewarisi" kelas dasar, dan menerapkan fungsi yang sesuai:
Ini tentu saja mengharuskan Anda untuk juga mengimplementasikan konstruktor, yang memastikan fungsi pointer diatur dengan benar. Biasanya Anda akan mengalokasikan memori secara dinamis untuk instance, tetapi Anda dapat membiarkan penelepon juga melakukannya:
Jika Anda menginginkan beberapa konstruktor berbeda, Anda harus "mendekorasi" nama fungsi, Anda tidak dapat memiliki lebih dari satu
rectangle_new()
fungsi:Berikut adalah contoh dasar yang menunjukkan penggunaan:
Saya harap ini memberi Anda beberapa ide, setidaknya. Untuk kerangka kerja berorientasi objek yang sukses dan kaya di C, lihat ke perpustakaan GObject glib .
Perhatikan juga bahwa tidak ada "kelas" eksplisit yang dimodelkan di atas, setiap objek memiliki pointer metode sendiri yang sedikit lebih fleksibel daripada yang biasanya Anda temukan di C ++. Juga, biaya memori. Anda bisa lolos dari itu dengan memasukkan pointer metode dalam
class
struktur, dan menemukan cara untuk setiap instance objek untuk referensi kelas.sumber
const ShapeClass *
atauconst void *
sebagai argumen? Tampaknya bahwa yang terakhir mungkin sedikit lebih bagus pada warisan, tapi aku bisa melihat argumen kedua cara ...float (*computeArea)(const ShapeClass *shape);
mengatakan ituShapeClass
adalah tipe yang tidak dikenal.struct
referensi itu sendiri, itu memerlukan deklarasi sebelum didefinisikan. Ini dijelaskan dengan sebuah contoh di sini dalam jawaban Lundin . Mengubah contoh untuk menyertakan deklarasi maju harus menyelesaikan masalah Anda;typedef struct ShapeClass ShapeClass; struct ShapeClass { float (*computeArea)(const ShapeClass *shape); };
Saya harus melakukannya sekali juga untuk pekerjaan rumah. Saya mengikuti pendekatan ini:
Contoh sederhana adalah ini:
sumber
typedef struct Queue Queue;
di sana.Jika Anda hanya menginginkan satu kelas, gunakan array
struct
s sebagai data "objek" dan berikan pointer ke fungsi "anggota". Anda dapat menggunakantypedef struct _whatever Whatever
sebelum mendeklarasikanstruct _whatever
untuk menyembunyikan implementasi dari kode klien. Tidak ada perbedaan antara "objek" danFILE
objek pustaka standar C tersebut .Jika Anda menginginkan lebih dari satu kelas dengan fungsi pewarisan dan virtual, maka itu umum untuk memiliki pointer ke fungsi sebagai anggota struct, atau pointer bersama ke tabel fungsi virtual. The GObject perpustakaan menggunakan kedua ini dan trik typedef, dan secara luas digunakan.
Ada juga buku tentang teknik untuk ini tersedia secara online - Object Oriented Programming dengan ANSI C .
sumber
Anda dapat melihat di GOBject. ini adalah pustaka OS yang memberi Anda cara verbose untuk melakukan objek.
http://library.gnome.org/devel/gobject/stable/
sumber
C Antarmuka dan Implementasi: Teknik untuk Membuat Perangkat Lunak yang Dapat Digunakan Kembali , David R. Hanson
Buku ini sangat baik dalam meliput pertanyaan Anda. Ada dalam seri Addison Wesley Professional Computing.
Paradigma dasarnya adalah seperti ini:
sumber
Saya akan memberikan contoh sederhana tentang bagaimana OOP harus dilakukan dalam C. Saya menyadari ini adalah dari tahun 2009 tetapi ingin menambahkan ini pula.
Konsep dasar melibatkan penempatan 'kelas warisan' di bagian atas struct. Dengan cara ini, mengakses 4 byte pertama dalam struct juga mengakses 4 byte pertama dalam 'kelas yang diwariskan' (Dengan asumsi non-crazy optimalisations). Sekarang, ketika pointer dari struct dilemparkan ke 'kelas yang diwariskan', 'kelas yang diwarisi' dapat mengakses 'nilai-nilai yang diwarisi' dengan cara yang sama dengan mengakses anggota-anggotanya secara normal.
Ini dan beberapa konvensi penamaan untuk fungsi konstruktor, destruktor, alokasi, dan deallocarion (saya sarankan init, clean, baru, gratis) akan membantu Anda.
Adapun fungsi Virtual, gunakan pointer fungsi di struct, mungkin dengan Class_func (...); bungkus juga. Adapun templat (sederhana), tambahkan parameter size_t untuk menentukan ukuran, memerlukan pointer * kosong, atau memerlukan tipe 'kelas' hanya dengan fungsionalitas yang Anda pedulikan. (mis. int GetUUID (Objek * sendiri); GetUUID (& p);)
sumber
Gunakan a
struct
untuk mensimulasikan data anggota kelas. Dalam hal cakupan metode, Anda dapat mensimulasikan metode pribadi dengan menempatkan prototipe fungsi pribadi dalam file .c dan fungsi publik dalam file .h.sumber
sumber
Dalam kasus Anda, perkiraan yang baik dari kelas bisa menjadi ADT . Tapi tetap saja tidak akan sama.
sumber
Strategi saya adalah:
Adakah yang melihat masalah, lubang, potensi jebakan, atau manfaat / kelemahan tersembunyi dari salah satu variasi pendekatan ini? Jika saya menemukan kembali metode desain (dan saya kira saya harus), bisakah Anda mengarahkan saya ke nama metode itu?
sumber
Lihat juga jawaban ini dan yang ini
Itu mungkin. Itu selalu tampak seperti ide bagus pada saat itu tetapi setelah itu menjadi mimpi buruk pemeliharaan. Kode Anda menjadi penuh dengan potongan-potongan kode yang mengikat semuanya. Seorang programmer baru akan memiliki banyak masalah dalam membaca dan memahami kode jika Anda menggunakan pointer fungsi karena tidak akan jelas fungsi apa yang disebut.
Menyembunyikan data dengan fungsi get / set mudah diterapkan di C tetapi berhenti di situ. Saya telah melihat beberapa upaya dalam hal ini di lingkungan tertanam dan pada akhirnya selalu merupakan masalah pemeliharaan.
Karena kalian semua siap memiliki masalah pemeliharaan saya akan menghindari.
sumber
Pendekatan saya adalah memindahkan
struct
dan semua fungsi yang terkait utamanya ke file sumber yang terpisah sehingga dapat digunakan "dengan mudah".Bergantung pada kompiler Anda, Anda mungkin dapat memasukkan fungsi ke dalam
struct
, tetapi itu adalah ekstensi yang sangat spesifik compiler, dan tidak ada hubungannya dengan versi terakhir dari standar yang saya gunakan secara rutin :)sumber
Kompiler c ++ pertama sebenarnya adalah preprocessor yang menerjemahkan kode C ++ ke C.
Jadi sangat mungkin untuk memiliki kelas dalam C. Anda dapat mencoba dan menggali preprocessor C ++ lama dan melihat solusi apa yang dibuatnya.
sumber
cfront
; itu mengalami masalah ketika pengecualian ditambahkan ke C ++ - penanganan pengecualian tidak sepele.GTK dibangun sepenuhnya di atas C dan menggunakan banyak konsep OOP. Saya telah membaca kode sumber GTK dan itu sangat mengesankan, dan jelas lebih mudah dibaca. Konsep dasarnya adalah bahwa setiap "kelas" hanyalah sebuah struct, dan fungsi statis yang terkait. Fungsi statis semua menerima struct "instance" sebagai parameter, melakukan apa pun yang diperlukan, dan mengembalikan hasil jika perlu. Misalnya, Anda mungkin memiliki fungsi "GetPosition (CircleStruct obj)". Fungsi ini hanya akan menggali melalui struct, mengekstrak angka posisi, mungkin membangun objek PositionStruct baru, menempelkan x dan y di PositionStruct baru, dan mengembalikannya. GTK bahkan mengimplementasikan pewarisan seperti ini dengan menanamkan struct di dalam struct. cukup pintar.
sumber
Apakah Anda ingin metode virtual?
Jika tidak maka Anda hanya mendefinisikan satu set fungsi pointer di struct itu sendiri. Jika Anda menetapkan semua fungsi pointer ke fungsi C standar maka Anda akan dapat memanggil fungsi dari C dalam sintaksis yang sangat mirip dengan bagaimana Anda akan di bawah C ++.
Jika Anda ingin memiliki metode virtual itu menjadi lebih rumit. Pada dasarnya Anda harus mengimplementasikan VTable Anda sendiri untuk setiap struct dan menetapkan pointer fungsi ke VTable tergantung pada fungsi yang dipanggil. Anda kemudian akan memerlukan satu set pointer fungsi dalam struct itu sendiri yang pada gilirannya memanggil pointer fungsi di VTable. Inilah, pada dasarnya, apa yang dilakukan C ++.
TBH meskipun ... jika Anda menginginkan yang terakhir maka Anda mungkin lebih baik hanya menemukan kompiler C ++ yang dapat Anda gunakan dan kompilasi ulang proyek. Saya tidak pernah mengerti obsesi dengan C ++ tidak dapat digunakan dalam embedded. Saya sudah sering menggunakannya dan berfungsi cepat dan tidak memiliki masalah memori. Tentu Anda harus sedikit lebih berhati-hati tentang apa yang Anda lakukan tetapi sebenarnya tidak terlalu rumit.
sumber
C bukan bahasa OOP, seperti yang Anda tunjukkan dengan benar, jadi tidak ada cara bawaan untuk menulis kelas yang benar. Anda bertaruh terbaik adalah dengan melihat struct , dan pointer fungsi , ini akan membiarkan Anda membangun perkiraan sebuah kelas. Namun, karena C bersifat prosedural, Anda mungkin ingin mempertimbangkan untuk menulis lebih banyak kode mirip-C (yaitu tanpa mencoba menggunakan kelas).
Juga, jika Anda bisa menggunakan C, Anda bisa menggunakan C ++ dan mendapatkan kelas.
sumber