SORU
27 Aralık 2008, CUMARTESİ


C DEMİRYOLU ve akıllı işaretçiler

C ile pratikte , nedir bu program nasıl uygulanır ve akıllı işaretçiler ile DEMİRYOLU kullanmanın yararları nelerdir smart pointers RAII,?

CEVAP
27 Aralık 2008, CUMARTESİ


DEMİRYOLU basit (ve belki gereksiz) bir örnek Dosya bir sınıftır. DEMİRYOLU olmadan kod şu şekilde: bir şey olabilir

File file("/path/to/file");
// Do stuff with file
file.close();

Diğer bir deyişle, bunu bitirdikten sonra dosyayı kapatıyoruz emin olmalıyız. Bu iki dezavantajları - öncelikle, her yerde kullanıyoruz Dosyası olacak denilen Dosya::close() - unut bunu yapmak için boştayız üzerine dosya artık daha ihtiyacımız var. İkinci sorun ise bir istisna dosyayı kapatmadan önce atılırsa ne olur?

Java ikinci sorun, nihayet bir madde kullanarak çözer:

try {
    File file = new File("/path/to/file");
    // Do stuff with file
} finally {
    file.close();
}

Hepatit C kronik sorunları DEMİRYOLU, Dosya yıkıcı dosyayı kapatma kullanarak çözer. Dosya nesne doğru zaman neyse olmalıdır), dosya bizim için halledilir kapanış tahrip olduğu sürece. Yani, bizim kod şimdi şöyle bir şey:

File file("/path/to/file");
// Do stuff with file
// No need to close it - destructor will do that for us

Bu Java ile yapılması sebebi cismin yerle bir olacağı üzerinde garantisi var, bu dosya gibi bir kaynağı serbest olacağı garanti.

Akıllı işaretçiler - çoğu zaman üzerine, biz sadece yığında nesneleri oluşturun. Örneğin (ve başka bir cevap bir örnek çalmak):

void foo() {
    std::string str;
    // Do cool things to or using str
}

Bu iyi çalışıyor - ama eğer str dönmek istersek ne olacak? Bu yazabiliriz:

std::string foo() {
    std::string str;
    // Do cool things to or using str
    return str;
}

, Bunun nesi yanlış? Peki, dönüş türü std::değer ile dönüyoruz demektir dize. Bu str kopyalama ve aslında kopya dönüş anlamına gelir. Bu pahalı olabilir, ve kopyalama maliyetini önlemek isteyebiliriz. Bu nedenle, işaretçi, başvuru veya dönüş fikir olabiliriz.

std::string* foo() {
    std::string str;
    // Do cool things to or using str
    return &str;
}

Ne yazık ki, bu kod çalışmıyor. Str için bir işaretçi iade ediyoruz - ama str biz foo çıktıktan sonra silinecek böylece yığında oluşturuldu(). Arayan işaretçi gelene kadar, başka bir deyişle, işe yaramaz (ve muhtemelen yararsız funky hataları her türlü neden olabilir bu yana kullanıyor

Peki, çözüm nedir? Öbek yeni kullanarak str yaratabiliriz - foo, () tamamlandı, str yok olmayacak.

std::string* foo() {
    std::string* str = new std::string();
    // Do cool things to or using str
    return str;
}

Tabii ki, bu çözüm de güzel şeyler değil. Sebebi str oluşturduk, ama biz hiçbir zaman silebilirsiniz. Bu çok küçük bir program için bir sorun olmayabilir, ama genel olarak, biz silmek emin olmak istiyoruz. Sadece arayan kişiyi hevesini aldıktan sonra nesneyi silmek gerektiğini söyleyebiliriz. Dezavantajı arayan ek karmaşıklık ekler ve yanlış, artık gerekli olduğu halde nesne silme sızıntı değil yani bir hafıza için yol olabilir, hangi bellek yönetmek zorunda olmasıdır.

Bu akıllı işaretçileri. Aşağıdaki örnek shared_ptr - akıllı işaretçiler farklı bakmanı öneriyorum gerçekten kullanmak istediklerini öğrenmek için kullanır.

shared_ptr<std::string> foo() {
    shared_ptr<std::string> str = new std::string();
    // Do cool things to or using str
    return str;
}

Şimdi, shared_ptr kaynak sayısı str için geçerli olacaktır. Örneğin

shared_ptr<std::string> str = foo();
shared_ptr<std::string> str2 = str;

Şimdi aynı dize için iki başvuru var. Str kalan başvurular var hayır bir kez silinir. Gibi, artık Kendin silme hakkında endişelenmenize gerek.

Bazı yorumlar, bu örnek için mükemmel (en az!) değil mi belirttiği gibi hızlı düzenleme: iki sebep. Öncelikle, dizeleri uygulanması nedeniyle, bir dize kopyalama ucuz olma eğilimindedir. İkincisi olarak da adlandırılan dönüş değeri optimizasyonu bilinen nedeniyle, değer döndürerek derleyici bazı zeka işleri hızlandırmak için yapabileceğiniz beri pahalı olabilir.

O yüzden, Haydi başka bir örnek Dosya bizim sınıf kullanmayı deneyin.

Hadi bir günlük olarak bir dosya kullanmak istiyoruz diyelim. Bu sadece ekleme modunda dosyayı açmak istiyoruz

File file("/path/to/file", File::append);
// The exact semantics of this aren't really important,
// just that we've got a file to be used as a log

Şimdi, izin diğer nesnelerin bir çift için: günlük olarak dosyamız hazır

void setLog(const Foo & foo, const Bar & bar) {
    File file("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

Ne yazık ki, bu korkunç örnek dosya bu yöntemi biter bitmez, foo ve bar artık geçersiz bir günlük dosyası var, yani kapalı olur biter. Yığın dosyası oluşturmak ve her iki foo ve bar için dosya gösterici geçirebiliriz:

void setLog(const Foo & foo, const Bar & bar) {
    File* file = new File("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

Ama sonra dosya silme için kim sorumludur? Eğer ne silmek dosya varsa, o zaman bir bellek ve kaynak sızıntı var. Foo veya bar dosyası ile ilk bitecek olup olmadığını bilmiyoruz, ya da dosyayı kendilerini silmek için bekleyemeyiz. Eğer foo bar ile bitirmeden önce dosyayı siler örneğin, bar artık geçersiz bir işaretçi var.

Tahmin edebileceğiniz gibi, akıllı işaretçileri bize yardım etmek için kullanabiliriz.

void setLog(const Foo & foo, const Bar & bar) {
    shared_ptr<File> file = new File("/path/to/file", File::append);
    foo.setLogFile(file);
    bar.setLogFile(file);
}

Şimdi, kimsenin ihtiyacı yok merak edilecek dosya silme - bir kez foo ve bar var bitmiş ve artık tüm başvuruları dosyası (muhtemelen nedeniyle foo ve bar olmak yok), dosya otomatik olarak silinir.

Bunu Paylaş:
  • Google+
  • E-Posta
Etiketler:

YORUMLAR

SPONSOR VİDEO

Rastgele Yazarlar

  • infodirt

    infodirt

    11 Mart 2009
  • kndx

    kndx

    11 Mart 2006
  • TotalxLuna

    TotalxLuna

    27 Kasım 2011