Ada dua teknik alokasi memori yang banyak digunakan: alokasi otomatis dan alokasi dinamis. Secara umum, ada wilayah memori yang sesuai untuk masing-masing: tumpukan dan tumpukan.
Tumpukan
Tumpukan selalu mengalokasikan memori secara berurutan. Itu dapat melakukannya karena mengharuskan Anda untuk melepaskan memori dalam urutan terbalik (First-In, Last-Out: FILO). Ini adalah teknik alokasi memori untuk variabel lokal dalam banyak bahasa pemrograman. Ini sangat, sangat cepat karena membutuhkan pembukuan minimal dan alamat berikutnya untuk dialokasikan adalah implisit.
Dalam C ++, ini disebut penyimpanan otomatis karena penyimpanan diklaim secara otomatis pada akhir ruang lingkup. Segera setelah eksekusi blok kode saat ini (dibatasi penggunaan {}
) selesai, memori untuk semua variabel di blok itu dikumpulkan secara otomatis. Ini juga merupakan momen di mana para penghancur dipanggil untuk membersihkan sumber daya.
Tumpukan
Tumpukan memungkinkan untuk mode alokasi memori yang lebih fleksibel. Pembukuan lebih kompleks dan alokasi lebih lambat. Karena tidak ada titik rilis implisit, Anda harus melepaskan memori secara manual, menggunakan delete
atau delete[]
(free
dalam C). Namun, tidak adanya titik rilis tersirat adalah kunci untuk fleksibilitas tumpukan.
Alasan menggunakan alokasi dinamis
Bahkan jika menggunakan tumpukan lebih lambat dan berpotensi menyebabkan kebocoran memori atau fragmentasi memori, ada kasus penggunaan yang sangat baik untuk alokasi dinamis, karena kurang terbatas.
Dua alasan utama untuk menggunakan alokasi dinamis:
Anda tidak tahu berapa banyak memori yang Anda butuhkan pada waktu kompilasi. Misalnya, ketika membaca file teks menjadi string, Anda biasanya tidak tahu ukuran file itu, jadi Anda tidak dapat memutuskan berapa banyak memori yang dialokasikan sampai Anda menjalankan program.
Anda ingin mengalokasikan memori yang akan bertahan setelah meninggalkan blok saat ini. Misalnya, Anda mungkin ingin menulis fungsi string readfile(string path)
yang mengembalikan konten file. Dalam hal ini, bahkan jika tumpukan dapat menampung seluruh isi file, Anda tidak dapat kembali dari fungsi dan mempertahankan blok memori yang dialokasikan.
Mengapa alokasi dinamis seringkali tidak perlu
Di C ++ ada konstruksi rapi yang disebut destruktor . Mekanisme ini memungkinkan Anda untuk mengelola sumber daya dengan menyelaraskan masa pakai sumber daya dengan masa pakai variabel. Teknik ini disebut RAII dan merupakan titik pembeda dari C ++. Ini "membungkus" sumber daya menjadi objek. std::string
adalah contoh sempurna. Cuplikan ini:
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
sebenarnya mengalokasikan sejumlah variabel memori. The std::string
objek mengalokasikan memori menggunakan tumpukan dan rilis di destructor. Dalam hal ini, yang Anda lakukan tidak perlu mengelola sumber daya apa pun secara manual dan masih mendapat manfaat dari alokasi memori dinamis.
Secara khusus, ini menyiratkan bahwa dalam cuplikan ini:
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}
ada alokasi memori dinamis yang tidak dibutuhkan. Program ini membutuhkan lebih banyak pengetikan (!) Dan memperkenalkan risiko lupa untuk membatalkan alokasi memori. Ini melakukan ini tanpa manfaat nyata.
Mengapa Anda harus menggunakan penyimpanan otomatis sesering mungkin
Pada dasarnya, paragraf terakhir merangkumnya. Menggunakan penyimpanan otomatis sesering mungkin membuat program Anda:
- lebih cepat mengetik;
- lebih cepat saat dijalankan;
- kurang rentan terhadap kebocoran memori / sumber daya.
Poin bonus
Dalam pertanyaan yang dirujuk, ada kekhawatiran tambahan. Secara khusus, kelas berikut:
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
delete mString;
}
Sebenarnya jauh lebih berisiko untuk digunakan daripada yang berikut:
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
Alasannya adalah bahwa std::string
mendefinisikan konstruktor salinan dengan benar. Pertimbangkan program berikut:
int main ()
{
Line l1;
Line l2 = l1;
}
Menggunakan versi asli, program ini kemungkinan akan macet, karena digunakan delete
pada string yang sama dua kali. Menggunakan versi yang dimodifikasi, setiap Line
instance akan memiliki instance string sendiri , masing-masing dengan ingatannya sendiri dan keduanya akan dirilis pada akhir program.
Catatan lain
Penggunaan ekstensif RAII dianggap sebagai praktik terbaik dalam C ++ karena semua alasan di atas. Namun, ada manfaat tambahan yang tidak segera terlihat. Pada dasarnya, ini lebih baik daripada jumlah bagian-bagiannya. Seluruh mekanisme terbentuk . Itu bersisik.
Jika Anda menggunakan Line
kelas sebagai blok penyusun :
class Table
{
Line borders[4];
};
Kemudian
int main ()
{
Table table;
}
mengalokasikan empat std::string
instance, empat Line
instance, satu Table
instance dan semua konten string dan semuanya dibebaskan secara otomatis .