C++'ta nesneleri kopyalarken başlangıçta üye bazında kopyalama mekanizması kullanılır: her nesne üyesi için kendi kopyalama işlemi çağrılır. Yerleşik türler için bu güvenlidir, ancak dinamik kaynaklar için bir sorun ortaya çıkar: yalnızca işaretçiler kopyalanır, veriler değil.
Eğer nesne, heap bellekte tahsis edilmiş bir bellek alanına işaret eden bir işaretçi içeriyorsa, iki nesne kopyalandıktan sonra aynı bellek alanına işaret edeceklerdir. Bu durumda bir nesne yok edildiğinde bellek serbest bırakılacak ve diğerinin işaretçisi geçersiz hale gelecektir ("yabancı" işaretçi). Bu, çalışma zamanı hatalarına ve bellek sızıntılarına yol açar.
Kopyanın bağımsız olması için derin kopyalama gereklidir — bayt bazında kopyalama ve kendi tamponunun tahsis edilmesi gerekir. Bu, özel bir kopyalama yapıcı ve atama operatörü yazarak gerçekleştirilir.
Kod örneği:
class MyString { char* data; public: MyString(const char* s) { data = new char[strlen(s)+1]; strcpy(data, s); } // Derin kopyalama yapıcı MyString(const MyString& src) { data = new char[strlen(src.data) + 1]; strcpy(data, src.data); } // Derin kopyalama atama MyString& operator=(const MyString& src) { if (this != &src) { delete[] data; data = new char[strlen(src.data) + 1]; strcpy(data, src.data); } return *this; } ~MyString() { delete[] data; } };
Anahtar özellikler:
Sınıf sadece işaretçi içeriyorsa ve bellek tahsis edilmiyorsa, neden özel bir yıkıcıya ihtiyaç var?
Yıkıcı, sınıf içinde bellek (veya başka bir kaynak) açık şekilde tahsis ettiyseniz gereklidir. Eğer işaretçi bellek tahsis etmiyorsa, varsayılan yıkıcı yeterlidir.
Dinamik bellek içeren bir sınıfta operator= uygulanmazsa ve kopyalama yapıcı tanımlanmışsa ne olur?
Eğer kopyalama yapıcısını manuel olarak tanımladıysanız, derleyici operator= yöntemini otomatik olarak uygulamaz; ya örtük olarak tanımlanır ya da derleyici hata/uyarı verir (standartlara bağlıdır). Bu, atama sırasında kötü tanımlanmış davranışa yol açar: üye bazında kopyalama gerçekleşir ve çift serbest bırakma veya bellek sızıntısı oluşur.
Kod örneği:
MyString a("hi"); MyString b = a; // Tamam: sizin kopyalama yapıcınız MyString c("bye"); c = a; // Problem! Eğer operator= manuel olarak uygulanmamışsa, yüzeysel kopyalama oluşacak
Kendisine atama yaparken manuel olarak operator= uygulamanın tehlikesi nedir?
Kaynakları paylaşırken this!=&rhs kontrolü yapılmadan kendisine atama yapıldığında, delete[] data çalıştırılacak ve ardından zaten yok edilmiş dizinin kopyalanması gerçekleşecektir, bu da segfault'a neden olur. Kendini koruma: her zaman self-assignment'ı kontrol edin.
if (this != &rhs) { ... }
Geliştirici, derin kopyalama uygulamadan, yerleşik işaretçiye sahip bir sınıf nesnesini kopyalar. Kopyalama sonrasında birkaç nesne, tek bir bellek alanını paylaşır. İki yıkıcı çift serbest bırakmayı tetikler, programın çökmesine neden olur.
Artılar:
Eksiler:
Geliştirici, kopyalama yapıcısını, atama operatörünü ve yıkıcıyı düzgün bir şekilde uygular. Her nesne kendi belleğine sahiptir.
Artılar:
Eksiler: