Dalam C ++, ada beberapa fitur yang dapat dianggap berpotensi berbahaya - dengan kesalahan perhitungan dalam desain atau pengkodean yang tidak akurat, mereka dapat dengan mudah menyebabkan kesalahan. Artikel ini menyediakan pilihan fitur seperti itu, memberikan tips tentang cara mengurangi dampak negatifnya.
Daftar isi
Praemonitus, praemunitus.
Diperingatkan berarti dipersenjatai. (lat.)
Pendahuluan
Dalam C ++ ada banyak fitur yang dapat dianggap berpotensi berbahaya - ketika salah perhitungan dalam desain atau pengkodean yang tidak akurat, mereka dapat dengan mudah menyebabkan kesalahan. Beberapa dari mereka dapat dikaitkan dengan masa kanak-kanak yang sulit, beberapa dengan standar C ++ 98 yang ketinggalan zaman, tetapi yang lain sudah dikaitkan dengan fitur-fitur dari C ++ modern. Pertimbangkan yang utama dan cobalah untuk memberikan saran tentang cara mengurangi dampak negatifnya.
1. Jenis
1.1. Petunjuk dan operator bersyarat
Kebutuhan kompatibilitas dengan C mengarah pada fakta bahwa dalam pernyataan if(...)
dan sejenisnya, Anda dapat mengganti setiap ekspresi numerik atau penunjuk, dan bukan hanya ekspresi seperti bool
. Masalahnya diperparah oleh konversi implisit dari bool
ke int
dalam ekspresi aritmatika dan prioritas beberapa operator. Ini mengarah, misalnya, ke kesalahan berikut:
if(a=b)
ketika dengan benar if(a==b)
,
if(a<x<b)
, ketika dengan benar if(a<x && x<b)
,
if(a&x==0)
, ketika dengan benar if((a&x)==0)
,
if(Foo)
ketika dengan benar if(Foo())
,
if(arr)
bila benar if(arr[0])
,
if(strcmp(s,r))
ketika benar if(strcmp(s,r)==0)
.
Beberapa kesalahan ini menyebabkan peringatan kompiler, tetapi bukan kesalahan. Penganalisa kode terkadang juga bisa membantu. Dalam C #, kesalahan seperti itu hampir tidak mungkin, pernyataan if(...)
dan sejenisnya memerlukan tipe bool
, Anda tidak dapat mencampur tipe bool
dan numerik dalam ekspresi aritmatika.
Bagaimana cara bertarung:
- Program tanpa peringatan. Sayangnya, ini tidak selalu membantu, beberapa kesalahan yang dijelaskan di atas tidak memberikan peringatan.
- Gunakan penganalisa kode statis.
- Teknik penerimaan kuno: ketika membandingkan dengan konstanta, letakkan di sebelah kiri, misalnya
if(MAX_PATH==x)
. Itu terlihat cantik kondominium (dan bahkan memiliki namanya sendiri - "notasi Yoda"), dan membantu dalam sejumlah kecil kasus dipertimbangkan. - Gunakan kualifikasi
const
seluas mungkin. Sekali lagi, itu tidak selalu membantu. - Biasakan diri Anda untuk menulis ekspresi logis yang benar:
if(x!=0)
alih-alih if(x)
. (Meskipun Anda dapat jatuh ke dalam perangkap prioritas operator di sini, lihat contoh ketiga.) - Menjadi sangat perhatian.
1.2. Konversi tersirat
C ++ mengacu pada bahasa yang diketik dengan kuat, tetapi konversi tipe implisit banyak digunakan untuk membuat kode lebih pendek. Konversi tersirat ini dalam beberapa kasus dapat menyebabkan kesalahan.
Konversi tersirat yang paling menyebalkan adalah konversi dari tipe numerik atau pointer ke bool
dan dari bool
ke int
. Transformasi ini (diperlukan untuk kompatibilitas dengan C) yang menyebabkan masalah yang dijelaskan dalam bagian 1.1. Konversi tersirat yang berpotensi menyebabkan hilangnya keakuratan data numerik (penyempitan konversi), misalnya, dari double
ke int
juga tidak selalu sesuai. Dalam banyak kasus, kompiler menghasilkan peringatan (terutama ketika mungkin ada kehilangan keakuratan data numerik), tetapi peringatan bukan kesalahan. Dalam C #, konversi antara tipe numerik dan bool
dilarang (bahkan eksplisit), dan konversi yang berpotensi menyebabkan hilangnya presisi dalam data numerik hampir selalu merupakan kesalahan.
Programmer dapat menambahkan konversi tersirat lainnya: (1) mendefinisikan konstruktor dengan satu parameter tanpa kata kunci explicit
; (2) definisi operator konversi tipe. Transformasi ini memecah celah keamanan tambahan berdasarkan pada prinsip pengetikan yang kuat.
Dalam C #, jumlah konversi implisit bawaan jauh lebih kecil, konversi kustom tersirat harus dinyatakan menggunakan kata kunci implicit
.
Bagaimana cara bertarung:
- Program tanpa peringatan.
- Berhati-hatilah dengan desain yang dijelaskan di atas, jangan menggunakannya tanpa kebutuhan ekstrim.
2. Resolusi Nama
2.1. Menyembunyikan Variabel dalam Lingkup Bertingkat
Di C ++, aturan berikut ini berlaku. Biarkan
Menurut aturan C ++, variabel
dideklarasikan dalam
menyembunyikan variabel
dideklarasikan dalam
Deklarasi pertama x
tidak harus dalam sebuah blok: itu bisa menjadi anggota kelas atau variabel global, hanya perlu terlihat di blok
Bayangkan sekarang situasi ketika Anda perlu memperbaiki kode berikut
Secara tidak sengaja, perubahan dilakukan:
Dan sekarang kode "sesuatu sedang dilakukan dengan
dari
" akan melakukan sesuatu dengan
dari
! Jelas bahwa semuanya tidak berfungsi seperti sebelumnya, dan untuk menemukan apa yang seringkali sangat sulit. Tidak sia-sia bahwa dalam C # dilarang untuk menyembunyikan variabel lokal (meskipun anggota kelas bisa). Perhatikan bahwa mekanisme menyembunyikan variabel dalam satu bentuk atau yang lain digunakan di hampir semua bahasa pemrograman.
Bagaimana cara bertarung:
- Deklarasikan variabel dalam ruang lingkup sesempit mungkin.
- Jangan menulis blok yang panjang dan bersarang.
- Gunakan konvensi pengkodean untuk membedakan secara visual pengidentifikasi dari cakupan yang berbeda.
- Menjadi sangat perhatian.
2.2. Kelebihan fungsi
Function overloading adalah fitur integral dari banyak bahasa pemrograman dan tidak terkecuali C ++. Tetapi kesempatan ini harus digunakan dengan hati-hati, jika tidak Anda dapat mengalami masalah. Dalam beberapa kasus, misalnya, ketika konstruktor kelebihan beban, programmer tidak punya pilihan, tetapi dalam kasus lain, penolakan terhadap kelebihan dapat dibenarkan. Pertimbangkan masalah yang muncul saat menggunakan fungsi kelebihan beban.
Jika Anda mencoba mempertimbangkan semua opsi yang mungkin muncul saat menyelesaikan kelebihan, maka aturan untuk menyelesaikan kelebihan menjadi sangat rumit, dan karenanya sulit diprediksi. Kompleksitas tambahan diperkenalkan oleh fungsi template dan kelebihan operator bawaan. C ++ 11 menambahkan masalah dengan tautan nilai dan daftar inisialisasi.
Masalah dapat dibuat oleh algoritme pencarian bagi kandidat untuk mengatasi kelebihan muatan di area visibilitas bersarang. Jika kompiler menemukan kandidat dalam ruang lingkup saat ini, maka pencarian lebih lanjut dihentikan. Jika kandidat yang ditemukan tidak cocok, bertentangan, dihapus atau tidak dapat diakses, kesalahan dihasilkan, tetapi tidak ada pencarian lebih lanjut yang dicoba. Dan hanya jika tidak ada kandidat dalam lingkup saat ini, pencarian bergerak ke lingkup selanjutnya yang lebih luas. Nama mekanisme persembunyian berfungsi, yang hampir sama dengan yang dibahas di bagian 2.1, lihat [Dewhurst].
Fungsi overloading dapat mengurangi pembacaan kode, yang berarti memicu kesalahan.
Menggunakan fungsi dengan parameter default sepertinya menggunakan fungsi kelebihan beban, meskipun, tentu saja, ada sedikit potensi masalah. Tetapi masalah dengan keterbacaan yang buruk dan kemungkinan kesalahan tetap ada.
Dengan sangat hati-hati, parameter kelebihan beban dan standar untuk fungsi virtual harus digunakan, lihat bagian 5.2.
C # juga mendukung fungsi yang berlebihan, tetapi aturan untuk menyelesaikan kelebihan sedikit berbeda.
Bagaimana cara bertarung:
- Jangan menyalahgunakan fungsi yang berlebihan, serta merancang fungsi dengan parameter default.
- Jika fungsi kelebihan beban, maka gunakan tanda tangan yang tidak diragukan saat menyelesaikan kelebihan.
- Jangan mendeklarasikan fungsi dengan nama yang sama dalam lingkup bersarang.
- Jangan lupa bahwa mekanisme fungsi jarak jauh (
=delete
) yang muncul di C ++ 11 dapat digunakan untuk melarang opsi overload tertentu.
3. Konstruktor, destruktor, inisialisasi, penghapusan
3.1. Fungsi anggota kelas yang dihasilkan oleh kompiler
Jika programmer tidak mendefinisikan fungsi anggota kelas dari daftar berikut - konstruktor default, copy constructor, operator penugasan copy, destructor - maka kompiler dapat melakukan ini untuknya. C ++ 11 menambahkan konstruktor bergerak dan operator penugasan pindah ke daftar ini. Fungsi anggota ini disebut fungsi anggota khusus. Mereka dihasilkan hanya jika digunakan, dan kondisi tambahan khusus untuk setiap fungsi terpenuhi. Kami menarik perhatian pada kenyataan bahwa penggunaan ini ternyata cukup tersembunyi (misalnya, ketika menerapkan warisan). Jika fungsi yang diperlukan tidak dapat dibuat, kesalahan dihasilkan. (Dengan pengecualian operasi relokasi, mereka digantikan oleh operasi copy.) Fungsi anggota yang dihasilkan oleh kompilator bersifat publik dan dapat di-embed. Detail tentang fungsi anggota khusus dapat ditemukan di [Meyers2].
Dalam beberapa kasus, bantuan seperti itu dari kompiler dapat menjadi "layanan beruang". Tidak adanya fungsi khusus khusus anggota dapat menyebabkan pembuatan tipe sepele, dan ini, pada gilirannya, menyebabkan masalah variabel tidak diinisialisasi, lihat bagian 3.2. Fungsi anggota yang dihasilkan bersifat publik, dan ini tidak selalu konsisten dengan desain kelas. Di kelas dasar, konstruktor harus dilindungi, kadang-kadang, untuk kontrol yang lebih baik atas siklus hidup objek, destruktor yang dilindungi diperlukan. Jika kelas memiliki deskriptor sumber daya mentah sebagai anggota dan memiliki sumber daya ini, maka programmer perlu mengimplementasikan copy constructor, copy copy operator, dan destructor. Apa yang disebut "aturan Tiga Besar" dikenal luas, yang menyatakan bahwa jika seorang programmer mendefinisikan setidaknya satu dari tiga operasi - copy constructor, copy copy operator atau destructor - maka ia harus mendefinisikan ketiga operasi. Konstruktor pemindahan dan operator penugasan pemindahan yang dihasilkan oleh kompilator juga jauh dari yang Anda butuhkan. Destructor yang dihasilkan oleh kompiler dalam beberapa kasus mengarah ke masalah yang sangat halus, yang hasilnya mungkin merupakan kebocoran sumber daya, lihat bagian 3.7.
Programmer dapat melarang pembuatan fungsi anggota khusus, dalam C ++ 11 perlu menggunakan konstruk "=delete"
ketika mendeklarasikan, dalam C ++ 98 mendeklarasikan fungsi anggota terkait pribadi dan tidak mendefinisikan.
Jika pemrogram merasa nyaman dengan fungsi anggota yang dihasilkan oleh kompiler, maka dalam C ++ 11 ia dapat menunjukkan ini secara eksplisit, dan tidak hanya menjatuhkan deklarasi. Untuk melakukan ini, Anda harus menggunakan konstruksi "=default"
ketika mendeklarasikan, sementara kode lebih baik dibaca dan fitur tambahan muncul terkait dengan mengelola tingkat akses.
Dalam C #, kompiler dapat menghasilkan konstruktor default, biasanya ini tidak menyebabkan masalah.
Bagaimana cara bertarung:
- Mengontrol kompiler menghasilkan fungsi anggota khusus. Jika perlu, terapkan sendiri atau larang.
3.2. Variabel tidak diinisialisasi
Konstruktor dan destruktor dapat disebut elemen kunci dari model objek C ++. Saat membuat objek, konstruktor harus dipanggil, dan ketika menghapus, destruktor dipanggil. Tetapi masalah kompatibilitas dengan C telah memaksa beberapa pengecualian, dan pengecualian ini disebut tipe sepele. Mereka diperkenalkan untuk mensimulasikan jenis sichny dan siklus hidup syshny variabel, tanpa panggilan wajib dari konstruktor dan destruktor. Kode C, jika dikompilasi dan dieksekusi dalam C ++, harus berfungsi seperti dalam C. Tipe sepele termasuk tipe numerik, pointer, enumerasi, serta kelas, struktur, serikat, dan array yang terdiri dari tipe sepele. Kelas dan struktur harus memenuhi beberapa kondisi tambahan: tidak adanya konstruktor kustom, destruktor, salin, fungsi virtual. Untuk kelas sepele, kompiler dapat menghasilkan konstruktor default dan destruktor. Konstruktor default nol objek, destruktor tidak melakukan apa pun. Tetapi konstruktor ini akan dihasilkan dan digunakan hanya jika secara eksplisit dipanggil ketika variabel diinisialisasi. Variabel tipe sepele akan diinisialisasi jika Anda tidak menggunakan beberapa varian inisialisasi eksplisit. Sintaks inisialisasi tergantung pada jenis dan konteks deklarasi variabel. Variabel statis dan lokal diinisialisasi ketika dideklarasikan. Untuk kelas, kelas dasar langsung dan anggota kelas non-statis diinisialisasi dalam daftar inisialisasi konstruktor. (C ++ 11 memungkinkan Anda untuk menginisialisasi anggota kelas non-statis saat mendeklarasikan, lihat nanti.) Untuk objek dinamis, ekspresi new T()
membuat objek yang diinisialisasi oleh konstruktor default, tetapi new T
untuk tipe sepele membuat objek yang tidak diinisialisasi. Saat membuat array dinamis dari tipe sepele, new T[N]
, elemen-elemennya akan selalu diinisialisasi. Jika turunan std::vector<T>
dibuat atau diperluas dan parameter tidak disediakan untuk inisialisasi eksplisit elemen, maka mereka dijamin akan memanggil konstruktor default. C ++ 11 memperkenalkan sintaks inisialisasi baru - menggunakan kurung kurawal. Sepasang kurung kosong berarti inisialisasi menggunakan konstruktor default. Inisialisasi tersebut dimungkinkan di mana-mana di mana inisialisasi tradisional digunakan, selain itu menjadi mungkin untuk menginisialisasi anggota kelas non-statis ketika mendeklarasikan, yang menggantikan inisialisasi dalam daftar inisialisasi konstruktor.
Variabel yang tidak diinisialisasi disusun sebagai berikut: jika ia didefinisikan dalam lingkup namespace
(secara global), ia akan memiliki semua bit nol, jika dibuat secara lokal atau secara dinamis, ia akan menerima serangkaian bit acak. Jelas bahwa penggunaan variabel semacam itu dapat menyebabkan perilaku program yang tidak dapat diprediksi.
Benar, kemajuan tidak berhenti, kompiler modern, dalam beberapa kasus, mendeteksi variabel yang tidak diinisialisasi dan melemparkan kesalahan. Penganalisa kode yang tidak diinisialisasi mendeteksi lebih baik.
Pustaka standar C ++ 11 memiliki templat yang disebut tipe properties (file header <type_traits>
). Salah satunya memungkinkan Anda untuk menentukan apakah jenisnya sepele. Ekspresi std::is_trivial<>::value
true
jika T
tipe trivial dan false
sebaliknya.
Struktur sysylic juga sering disebut Data Lama Biasa (POD). Kita dapat berasumsi bahwa POD dan "tipe trivial" adalah istilah yang hampir setara.
Dalam C #, variabel yang tidak diinisialisasi menyebabkan kesalahan, ini dikendalikan oleh kompiler. Bidang objek dari tipe referensi diinisialisasi secara default jika inisialisasi eksplisit tidak dilakukan. Bidang objek dari tipe signifikan diinisialisasi baik semua secara default, atau semua harus diinisialisasi secara eksplisit.
Bagaimana cara bertarung:
- Memiliki kebiasaan menginisialisasi variabel secara eksplisit. Variabel tidak diinisialisasi harus "memotong mata."
- Deklarasikan variabel dalam ruang lingkup sesempit mungkin.
- Gunakan penganalisa kode statis.
- Jangan mendesain tipe sepele. Untuk memastikan bahwa jenisnya tidak sepele, cukup mendefinisikan konstruktor khusus.
3.3. Prosedur Inisialisasi untuk Kelas Dasar dan Anggota Kelas Non-Statis
Ketika menerapkan konstruktor kelas, kelas dasar langsung dan anggota kelas non-statis diinisialisasi. Urutan inisialisasi ditentukan oleh standar: pertama, kelas dasar dalam urutan di mana mereka dinyatakan dalam daftar kelas dasar, kemudian anggota kelas yang tidak statis dalam urutan deklarasi. Jika perlu, inisialisasi eksplisit dari kelas dasar dan anggota non-statis menggunakan daftar inisialisasi konstruktor. Sayangnya, item dalam daftar ini tidak harus dalam urutan inisialisasi yang terjadi. Ini harus diperhitungkan jika, selama inisialisasi, item daftar menggunakan referensi ke item daftar lainnya. Kesalahan, tautan mungkin ke objek yang belum diinisialisasi. C ++ 11 memungkinkan Anda untuk menginisialisasi anggota kelas non-statis saat mendeklarasikan (menggunakan kurung kurawal). Dalam hal ini, mereka tidak perlu diinisialisasi dalam daftar inisialisasi konstruktor dan masalahnya dihapus sebagian.
Dalam C #, sebuah objek diinisialisasi sebagai berikut: pertama bidang diinisialisasi, dari sub-objek dasar ke turunan terakhir, maka konstruktor dipanggil dalam urutan yang sama. Masalah yang dijelaskan tidak terjadi.
Bagaimana cara bertarung:
- Menyimpan daftar inisialisasi konstruktor dalam urutan deklarasi.
- Cobalah untuk membuat inisialisasi kelas dasar dan anggota kelas independen.
- Gunakan inisialisasi anggota non-statis saat mendeklarasikan.
3.4. Prosedur Inisialisasi untuk Anggota Kelas Statis dan Variabel Global
Anggota kelas statis, serta variabel yang didefinisikan dalam lingkup namespace
(global) dalam unit kompilasi yang berbeda (file), diinisialisasi dalam urutan yang ditentukan oleh implementasi. Ini harus diperhitungkan jika selama inisialisasi variabel seperti menggunakan referensi satu sama lain. Tautan mungkin ke variabel tidak diinisialisasi.
Bagaimana cara bertarung:
- Ambil tindakan khusus untuk mencegah situasi ini. Misalnya, gunakan variabel statis lokal (tunggal), mereka diinisialisasi pada penggunaan pertama.
3.5. Pengecualian dalam destruktor
Destruktor seharusnya tidak melempar pengecualian. Jika Anda melanggar aturan ini, Anda bisa mendapatkan perilaku tidak terdefinisi, paling sering penghentian tidak normal.
Bagaimana cara bertarung:
- Hindari melempar pengecualian di destructor.
3.6. Menghapus objek dan array dinamis
Jika objek dinamis dari beberapa tipe T
T* pt = new T();
kemudian dihapus dengan operator delete
delete pt;
Jika array dinamis dibuat
T* pt = new T[N];
kemudian dihapus dengan operator delete[]
delete[] pt;
Jika Anda tidak mengikuti aturan ini, Anda bisa mendapatkan perilaku yang tidak terdefinisi, yaitu, apa pun bisa terjadi: kebocoran memori, kerusakan, dll. Lihat [Meyers1] untuk detailnya.
Bagaimana cara bertarung:
- Gunakan formulir
delete
benar.
3.7. Penghapusan dengan deklarasi kelas yang tidak lengkap
Omnivora dari operator delete
dapat menciptakan masalah tertentu, dapat diterapkan pada pointer bertipe void*
atau ke pointer ke kelas yang memiliki deklarasi tidak lengkap (preemptive). Operator delete
diterapkan ke pointer ke kelas adalah operasi dua fase, pertama, destruktor dipanggil, kemudian memori dibebaskan.Jika operator diterapkan delete
ke pointer ke kelas dengan deklarasi tidak lengkap, tidak ada kesalahan terjadi, kompilator hanya melewatkan panggilan ke destruktor (meskipun peringatan dikeluarkan). Pertimbangkan sebuah contoh:
class X;
Kode ini mengkompilasi bahkan jika delete
deklarasi kelas penuh tidak tersedia di dial-peer X
. Visual Studio menampilkan peringatan berikut:warning C4150: deletion of pointer to incomplete type 'X'; no destructor called
Jika ada implementasi X
dan CreateX()
, kemudian kode dikompilasi, jika ia CreateX()
mengembalikan pointer ke objek yang dibuat oleh operator new
, maka panggilan Foo()
berhasil dieksekusi, destructor tidak dipanggil. Jelas bahwa ini dapat menyebabkan sumber daya terkuras, jadi sekali lagi tentang perlunya berhati-hati tentang peringatan.
, -. , . , , , , . [Meyers2].
:
4. ,
4.1.
++ , . . . , 1.1.
:
std::out<<c?x:y;
(std::out<<c)?x:y;
std::out<<(c?x:y);
, , .
. <<
?:
std::out
void*
. ++ , . -, , . ?:
. , ( ).
: x&f==0
x&(f==0)
, (x&f)==0
, , , . - , , , , .
. / . / , /, . , x/4+1
x>>2+1
, x>>(2+1)
, (x>>2)+1
, .
C# , C++, , - .
:
4.2.
++ , . . , , . 4.1. — +
+=
. . , : ,
(), &&
, ||
. , (-), (short-circuit evaluation semantics), , . & ( ). & , .. .
, - (-) , . .
- , , . . [Dewhurst].
C# , , , .
:
4.3.
++ , . ( : ,
(), &&
, ||
, ?:
.) , , , . :
int x=0; int y=(++x*2)+(++x*3);
y
.
, . .
class X; class Y; void Foo(std::shared_ptr<X>, std::shared_ptr<Y>);
Foo()
:
Foo(std::shared_ptr<X>(new X()), std::shared_ptr<Y>(new Y()));
: X
, Y
, std::shared_ptr<X>
, std::shared_ptr<Y>
. Y
, X
.
:
auto p1 = std::shared_ptr<X>(new X()); auto p2 = std::shared_ptr<Y>(new Y()); Foo(p1, p2);
std::make_shared<Y>
( , ):
Foo(std::make_shared<X>(), std::make_shared<Y>());
. [Meyers2].
:
5.
5.1.
++98 , ( ), , ( , ). virtual
, , . ( ), , , . , , . , ++11 override
, , , . .
:
5.2.
. , , . . . [Dewhurst].
:
5.3.
, , . , , post_construct pre_destroy. , — . . , : ( ) . (, , .) , ( ), ( ). . [Dewhurst]. , , .
— - .
, C# , , , . C# : , , . , ( , ).
:
5.4.
, , delete
. , - .
:
6.
— C/C++, . . . « ».
C# unsafe mode, .
6.1.
/++ , : strcpy()
, strcat()
, sprinf()
, etc. ( std::vector<>
, etc.) , . (, , , . . Checked Iterators MSDN.) , : , , ; , .
C#, unsafe mode, .
:
- , .
- .
- z-terminated ,
_s
(. ).
6.2. Z-terminated
, . , :
strncpy(dst,src,n);
strlen(src)>=n
, dst
(, ). , , . . — . if(*str)
, if(strlen(str)>0)
, . [Spolsky].
C# string
.
:
6.3.
...
. printf
- , C. , , , , . , .
C# printf
, .
:
7.
7.1.
++ , , , . Berikut ini sebuah contoh:
const int N = 4, M = 6; int x,
:
int
;int
;N
int
;N
int
;- ,
char
int
; - ,
char
int
; - ,
char
int
; N
, char
int
;N
int
;M
N
int
;- ,
char
, long
int
.
, . ( .)
*
&
. ( .)
typedef
( using
-). , :
typedef int(*P)(long); PH(char);
, .
C# , .
:
7.2.
.
class X { public: X(int val = 0);
X x(5);
x
X
, 5.
X x();
x
, X
, x
X
, . X
, , :
X x; X x = X(); X x{};
, , , . [Sutter].
, , C++ ( ). . ( C++ .)
, , , , .
C# , , .
:
8.
8.1. inline
ODR
, inline
— . , . inline
(One Defenition Rule, ODR). . , . , ODR. static
: , , . static
inline
. , , ODR, . , . - , -. .
:
- «»
inline
. namespace
. , . - —
namespace
.
8.2.
. . , , , , .
:
- , .
- , : () , -.
using
-: using namespace
, using
-.- .
8.3. switch
— break
case
. ( .) C# .
:
8.4.
++ , — , — . ( class
struct
) , . ( , # Java.) — , .
- , . (
std::string
, std::vector
, etc.), , . - , , .
- , (slicing), , .
, , , . . , , . , . . — ( =delete
), — explicit
.
C# , .
:
8.5. Manajemen sumber daya
++ . , . - ( ), ++11 , , , .
C++ .
C# , . , . (using-) Basic Dispose.
:
8.6.
«» . , , C++ , STL- - .
. . , . . «», . COM- . (, .) , C++ . — . . . , («» ) , . .
# , . — .
:
8.7.
C++ , : , , . ( !) . , . , . , , . (, .)
C ( ), C++ C ( extern "C"
). C/C++ .
-. #pragma
- , , .
, , , .
, , COM. COM-, , ( , ). COM , , .
C# . , — , C#, C# C/C++.
:
8.8.
, . , . C++ . Sebaliknya
#define XXL 32
const int XXL=32;
. inline
.
# ( ).
:
9.
- . . . , .
- .
- . ++ — ++11/14/17.
- - , - .
- .
Referensi
[Dewhurst]
, . C++. .: . . — .: , 2012.
[Meyers1]
, . C++. 55 .: . . — .: , 2014.
[Meyers2]
, . C++: 42 C++11 C++14.: . . — .: «.. », 2016.
[Sutter]
, . C++.: . . — : «.. », 2015.
[Spolsky]
, . .: . . — .: -, 2008.