Yaygın olarak kullanılan iki bellek ayırma tekniği vardır: otomatik ayırma ve dinamik ayırma. Genellikle, her biri için karşılık gelen bir bellek bölgesi vardır: yığın ve yığın.
yığın
Yığın, belleği her zaman ardışık bir şekilde tahsis eder. Bunu yapabilir, çünkü belleği ters sırayla serbest bırakmanızı gerektirir (First-In, Last-Out: FILO). Bu, birçok programlama dilinde yerel değişkenler için bellek ayırma tekniğidir. Çok, çok hızlı çünkü minimum defter tutma gerektiriyor ve tahsis edilecek bir sonraki adres örtük.
Depolama alanı kapsamın sonunda otomatik olarak talep edildiği için C ++ 'da buna otomatik depolama adı verilir . Geçerli kod bloğunun yürütülmesi (kullanılarak sınırlandırılmış {}
) tamamlanır tamamlanmaz, o bloktaki tüm değişkenler için bellek otomatik olarak toplanır. Bu aynı zamanda yıkıcıların kaynakları temizlemek için çağrıldığı andır .
Yığın
Yığın daha esnek bir bellek ayırma moduna izin verir. Defter tutma daha karmaşıktır ve tahsis daha yavaştır. Örtük bırakma noktası olmadığından delete
veya delete[]
( free
C) tuşunu kullanarak belleği manuel olarak serbest bırakmanız gerekir . Bununla birlikte, örtük bir serbest bırakma noktasının olmaması, yığının esnekliğinin anahtarıdır.
Dinamik ayırmayı kullanma nedenleri
Yığın kullanımı daha yavaş olsa ve potansiyel olarak bellek sızıntılarına veya bellek parçalanmasına yol açsa bile, daha az sınırlı olduğu için dinamik ayırma için mükemmel iyi kullanım durumları vardır.
Dinamik ayırmayı kullanmanın iki temel nedeni:
Derleme zamanında ne kadar belleğe ihtiyacınız olduğunu bilmiyorsunuz. Örneğin, bir metin dosyasını bir dizeye okurken, genellikle dosyanın ne büyüklüğüne sahip olduğunu bilmezsiniz, bu nedenle programı çalıştırana kadar ne kadar bellek ayıracağına karar veremezsiniz.
Geçerli bloktan ayrıldıktan sonra da devam edecek bir bellek ayırmak istiyorsunuz. Örneğin string readfile(string path)
, bir dosyanın içeriğini döndüren bir işlev yazmak isteyebilirsiniz . Bu durumda, yığın tüm dosya içeriğini tutabilse bile, bir işlevden geri dönemez ve ayrılan bellek bloğunu koruyamazsınız.
Dinamik ayırma neden genellikle gereksizdir?
C ++ 'da yıkıcı adı verilen temiz bir yapı vardır . Bu mekanizma, kaynak ömrünü bir değişkenin ömrü ile hizalayarak kaynakları yönetmenizi sağlar. Bu teknik RAII olarak adlandırılır ve C ++ 'ın ayırt edici noktasıdır. Kaynakları nesnelere "sarar". std::string
mükemmel bir örnek. Bu pasaj:
int main ( int argc, char* argv[] )
{
std::string program(argv[0]);
}
aslında değişken miktarda bellek ayırır. std::string
Bunu yıkıcı yığın bırakması kullanarak nesne bellek ayırır. Bu durumda, did not elle dinamik bellek tahsisi faydalarını var hala tüm kaynakları yönetmek ve gerek.
Özellikle, bu snippet'te:
int main ( int argc, char* argv[] )
{
std::string * program = new std::string(argv[0]); // Bad!
delete program;
}
gereksiz dinamik bellek tahsisi var. Program daha fazla yazmayı (!) Gerektirir ve hafızayı yeniden yerleştirmeyi unutma riskini getirir. Bunu belirgin bir faydası olmadan yapar.
Otomatik depolama alanını neden mümkün olduğunca sık kullanmalısınız?
Temel olarak, son paragraf özetlemektedir. Otomatik depolamayı olabildiğince sık kullanmak programlarınızı yapar:
- yazmak için daha hızlı;
- çalıştırıldığında daha hızlı;
- bellek / kaynak sızıntılarına daha az eğilimli.
Bonus puanlar
Referans verilen soruda ek endişeler var. Özellikle, aşağıdaki sınıf:
class Line {
public:
Line();
~Line();
std::string* mString;
};
Line::Line() {
mString = new std::string("foo_bar");
}
Line::~Line() {
delete mString;
}
Aslında aşağıdakilerden çok daha riskli:
class Line {
public:
Line();
std::string mString;
};
Line::Line() {
mString = "foo_bar";
// note: there is a cleaner way to write this.
}
Bunun nedeni, std::string
bir kopya oluşturucuyu doğru şekilde tanımlamasıdır. Aşağıdaki programı düşünün:
int main ()
{
Line l1;
Line l2 = l1;
}
Orijinal sürümü kullanarak, bu program muhtemelen delete
aynı dizede iki kez kullandığı için çökecektir. Değiştirilen sürüm kullanıldığında, her Line
örnek kendi dize örneğine sahip olacak , her biri kendi belleğine sahip olacak ve her ikisi de programın sonunda serbest bırakılacaktır.
Diğer notlar
Yaygın kullanımı RAII çünkü her şeyden nedenlerden C ++ en iyi yöntem olarak kabul edilir. Bununla birlikte, hemen belirgin olmayan ek bir fayda vardır. Temel olarak, parçalarının toplamından daha iyidir. Bütün mekanizma bestelemektedir . Ölçekler.
Line
Sınıfı bir yapı taşı olarak kullanırsanız :
class Table
{
Line borders[4];
};
Sonra
int main ()
{
Table table;
}
dört std::string
örnek, dört Line
örnek, bir Table
örnek ve dizenin tüm içeriğini ayırır ve her şey otomatik olarak serbest bırakılır .