Apakah praktik yang buruk untuk menggunakan kompiler C ++ hanya untuk fungsi yang berlebihan?
Sudut IMHO, ya, dan saya harus menjadi skizofrenia untuk menjawab yang satu ini karena saya suka kedua bahasa tetapi tidak ada hubungannya dengan efisiensi, tetapi lebih seperti keamanan dan penggunaan bahasa secara idiomatis.
Sisi C
Dari sudut pandang C, saya merasa sangat boros untuk membuat kode Anda membutuhkan C ++ hanya untuk menggunakan fungsi yang berlebihan. Kecuali jika Anda menggunakannya untuk polimorfisme statis dengan template C ++, itu adalah gula sintaksis sepele yang diperoleh sebagai ganti untuk beralih ke bahasa yang sama sekali berbeda. Lebih jauh lagi jika Anda ingin mengekspor fungsi Anda ke dylib (mungkin atau mungkin tidak menjadi masalah praktis), Anda tidak dapat lagi melakukannya dengan sangat praktis untuk konsumsi yang tersebar luas dengan semua lambang-lambang yang ditandai dengan nama.
Sisi C ++
Dari sudut pandang C ++, Anda seharusnya tidak menggunakan C ++ seperti C dengan fungsi yang berlebihan. Ini bukan dogmatisme gaya tetapi terkait dengan penggunaan praktis C ++ sehari-hari.
Jenis kode C normal Anda hanya masuk akal dan "aman" untuk ditulis jika Anda bekerja melawan sistem tipe C yang melarang hal-hal seperti copy ctors in structs
. Setelah Anda bekerja di sistem tipe C ++ yang lebih kaya, fungsi sehari-hari yang bernilai sangat tinggi seperti memset
dan memcpy
tidak menjadi fungsi yang harus Anda sandarkan sepanjang waktu. Alih-alih, itu adalah fungsi yang biasanya ingin Anda hindari seperti wabah, karena dengan tipe C ++, Anda tidak boleh memperlakukannya seperti bit dan byte mentah yang akan disalin dan diacak serta dibebaskan. Bahkan jika kode Anda hanya menggunakan hal-hal seperti memset
pada primitif dan POD UDT saat ini, saat siapa pun menambahkan ctor ke UDT yang Anda gunakan (termasuk hanya menambahkan anggota yang memerlukannya, sepertistd::unique_ptr
member) terhadap fungsi-fungsi seperti itu atau fungsi virtual atau semacamnya, itu membuat semua kode C-style normal Anda rentan terhadap perilaku yang tidak terdefinisi. Ambillah dari Herb Sutter sendiri:
memcpy
dan memcmp
melanggar sistem tipe. Menggunakan memcpy
untuk menyalin objek seperti menghasilkan uang menggunakan mesin fotokopi. Menggunakan memcmp
untuk membandingkan objek adalah seperti membandingkan macan tutul dengan menghitung bintik-bintik mereka. Alat dan metode mungkin tampak melakukan pekerjaan itu, tetapi terlalu kasar untuk melakukannya. Objek C ++ adalah semua tentang penyembunyian informasi (bisa dibilang prinsip paling menguntungkan dalam rekayasa perangkat lunak; lihat Butir 11): Objek menyembunyikan data (lihat Butir 41) dan menyusun abstraksi yang tepat untuk menyalin data itu melalui konstruktor dan operator penugasan (lihat Item 52 hingga 55) . Bulldozing atas semua yang memcpy
terjadi merupakan pelanggaran serius terhadap penyembunyian informasi, dan sering menyebabkan kebocoran memori dan sumber daya (paling banter), macet (lebih buruk), atau perilaku tidak terdefinisi (terburuk) - Standar C ++ Coding.
Begitu banyak pengembang C yang tidak setuju dengan ini dan memang demikian, karena filosofi ini hanya berlaku jika Anda menulis kode dalam C ++. Anda kemungkinan besar sedang menulis kode yang sangat bermasalah jika Anda menggunakan fungsi seperti memcpy
semua waktu dalam kode yang membangun sebagai C ++ , tapi itu baik-baik saja jika Anda melakukannya di C . Kedua bahasa ini sangat berbeda dalam hal ini karena perbedaan dalam sistem tipe. Sangat menggoda untuk melihat subset dari fitur yang dimiliki keduanya dan percaya satu dapat digunakan seperti yang lain, terutama pada sisi C ++, tetapi kode C + (atau kode C -) umumnya jauh lebih bermasalah daripada C dan Kode C ++.
Demikian juga Anda tidak boleh menggunakan, katakanlah, malloc
dalam konteks gaya-C (yang menyiratkan tidak ada EH) jika dapat memanggil fungsi C ++ secara langsung yang dapat dilemparkan, sejak itu Anda memiliki titik keluar implisit dalam fungsi Anda sebagai akibat dari pengecualian yang Anda tidak dapat menangkap kode C-style secara efektif, sebelum dapat free
mengingatnya. Jadi, setiap kali Anda memiliki file yang membangun sebagai C ++ dengan .cpp
ekstensi atau apa pun dan tidak semua jenis hal-hal seperti malloc
, memcpy
, memset
,qsort
, dll, maka ia meminta masalah lebih lanjut di telepon jika belum, kecuali jika itu adalah detail implementasi dari kelas yang hanya bekerja dengan tipe primitif, di mana pada saat itu masih perlu melakukan penanganan pengecualian untuk menjadi pengecualian aman. Jika Anda menulis kode C ++ Anda malah ingin umumnya mengandalkan RAII dan menggunakan hal-hal seperti vector
, unique_ptr
, shared_ptr
, dll, dan menghindari semua normal C-gaya pengkodean bila memungkinkan.
Alasan Anda dapat bermain dengan pisau cukur dalam tipe data C dan x-ray dan bermain dengan bit dan byte mereka tanpa cenderung menyebabkan kerusakan jaminan dalam tim (meskipun Anda masih bisa benar-benar melukai diri sendiri dengan cara apa pun) bukan karena apa C jenis dapat dilakukan, tetapi karena apa yang mereka tidak akan pernah bisa lakukan. Saat Anda memperluas sistem tipe C untuk memasukkan fitur-fitur C ++ seperti ctors, dtors, dan vtables, bersama dengan penanganan pengecualian, semua kode C idiomatik akan dirender jauh, jauh lebih berbahaya daripada saat ini, dan Anda akan melihat jenis baru dari filosofi dan pola pikir berkembang yang akan mendorong gaya pengkodean yang sama sekali berbeda, seperti yang Anda lihat di C ++, yang sekarang mempertimbangkan bahkan menggunakan malpraktik penunjuk mentah untuk kelas yang mengelola memori yang bertentangan dengan, katakanlah, sumber daya yang sesuai dengan RAII unique_ptr
. Pola pikir itu tidak berevolusi dari rasa aman absolut. Itu berevolusi dari apa yang perlu secara khusus C ++ aman terhadap fitur-fitur seperti penanganan-pengecualian mengingat apa yang hanya diperbolehkan melalui sistem tipenya.
Pengecualian-Keamanan
Sekali lagi, saat Anda berada di tanah C ++, orang akan mengharapkan kode Anda aman-pengecualian. Orang-orang mungkin mempertahankan kode Anda di masa mendatang, mengingat bahwa itu sudah ditulis dan dikompilasi dalam C ++, dan cukup gunakan std::vector, dynamic_cast, unique_ptr, shared_ptr
, dll. Dalam kode yang disebut baik secara langsung atau tidak langsung oleh kode Anda, percaya itu tidak berbahaya karena kode Anda sudah "seharusnya" C ++ kode. Pada saat itu kita harus menghadapi kemungkinan bahwa segala sesuatu akan melempar, dan kemudian ketika Anda mengambil kode C baik-baik saja dan indah seperti ini:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future.
...
free(x);
return success;
}
return fail;
}
... sekarang rusak. Perlu ditulis ulang agar aman dari pengecualian:
int some_func(int n, ...)
{
int* x = calloc(n, sizeof(int));
if (x)
{
try
{
f(n, x); // some function which, now being a C++ function, may
// throw today or in the future (maybe someone used
// std::vector inside of it).
}
catch (...)
{
free(x);
throw;
}
...
free(x);
return success;
}
return fail;
}
Kotor! Itulah sebabnya sebagian besar pengembang C ++ akan menuntut ini:
void some_func(int n, ...)
{
vector<int> x(n);
f(x); // some function which, now being a C++ function, may throw today
// or in the future.
}
Di atas adalah kode yang sesuai dengan pengecualian-aman RAII dari jenis yang umumnya disetujui pengembang C ++ karena fungsi tidak akan bocor pada baris kode mana yang memicu keluar tersirat sebagai akibat dari a throw
.
Pilih Bahasa
Anda harus merangkul sistem tipe C ++ dan filosofi dengan RAII, pengecualian-keselamatan, templat, OOP, dll. Atau merangkul C yang sebagian besar berputar di sekitar bit dan byte mentah. Anda seharusnya tidak membentuk perkawinan yang tidak suci antara kedua bahasa ini, dan sebaliknya memisahkan mereka ke dalam bahasa yang berbeda untuk diperlakukan dengan sangat berbeda alih-alih mengaburkan keduanya.
Bahasa-bahasa ini ingin menikahi Anda. Anda biasanya harus memilih satu daripada berkencan dan bermain-main dengan keduanya. Atau Anda bisa menjadi poligami seperti saya dan menikahi keduanya, tetapi Anda harus sepenuhnya mengubah pemikiran Anda ketika menghabiskan waktu dengan satu sama lain dan menjaga mereka terpisah satu sama lain sehingga mereka tidak saling bertarung.
Ukuran Biner
Hanya karena penasaran saya mencoba mengambil implementasi daftar bebas saya dan patokan sekarang dan porting ke C ++ karena saya benar-benar ingin tahu tentang ini:
[...] tidak tahu seperti apa bentuk C karena saya belum pernah menggunakan kompiler C.
... dan ingin tahu apakah ukuran biner akan mengembang sama sekali hanya sebagai C ++. Itu mengharuskan saya untuk menaburkan gips eksplisit di seluruh tempat yang jelek (salah satu alasan saya suka benar-benar menulis hal-hal tingkat rendah seperti pengalokasi dan struktur data dalam C lebih baik) tetapi hanya butuh satu menit.
Ini hanya membandingkan rilis rilis MSVC 64-bit untuk aplikasi konsol sederhana dan dengan kode yang tidak menggunakan fitur C ++, bahkan operator tidak kelebihan - hanya perbedaan antara membangunnya dengan C dan menggunakan, katakanlah, <cstdlib>
alih-alih <stdlib.h>
dan hal-hal seperti itu, tetapi saya terkejut menemukan itu membuat perbedaan nol dengan ukuran biner!
Biner adalah 9,728
byte ketika dibangun di C, dan juga 9,278
byte saat dikompilasi sebagai kode C ++. Saya sebenarnya tidak mengharapkan itu. Saya pikir hal-hal seperti EH setidaknya akan menambah sedikit di sana (pikir itu setidaknya akan seperti seratus byte yang berbeda), meskipun mungkin itu bisa mengetahui bahwa tidak perlu menambahkan instruksi yang berhubungan dengan EH karena saya hanya menggunakan perpustakaan standar C dan tidak ada lemparan. Saya memikirkan sesuatuakan menambahkan sedikit ke ukuran biner bagaimanapun, seperti RTTI. Bagaimanapun, itu agak keren untuk melihat itu. Tentu saja saya tidak berpikir Anda harus menggeneralisasi dari hasil yang satu ini, tetapi setidaknya sedikit terkesan. Itu juga tidak berdampak pada tolok ukur, dan tentu saja, karena saya membayangkan ukuran biner yang identik juga berarti instruksi mesin yang dihasilkan identik.
Yang mengatakan, siapa yang peduli tentang ukuran biner dengan masalah keselamatan dan teknik yang disebutkan di atas? Jadi sekali lagi, pilih bahasa dan raih filosofinya alih-alih mencoba untuk mengastastasinya; itu yang saya rekomendasikan.
//
komentar. Jika berhasil, mengapa tidak?