C++ programlamasında, nesneleri birleştirmek için sıkça iki yöntem kullanılır: aggregasyon ve kompozisyon. Bu kavramlar, sınıflar arasındaki farklı ilişkileri yansıtır ve ilişkili nesnelerin yaşam döngüsünü ve yok etme sorumluluğunu etkiler.
Sorunun Tarihi:
Nesne yönelimli tasarımda nesneler arasındaki bağımlılıkları ayırmak her zaman önemli olmuştur. Nesne tabanlı dillerin (Smalltalk, C++, Java) ortaya çıkmasıyla birlikte, "kısım - bütün" ilişkilerini en iyi nasıl modelleyeceğine dair bir soru gündeme gelmiştir. C++'de bu, bellek ve nesnelerin yaşam döngüsü üzerinde manuel kontrol nedeniyle özellikle önemlidir.
Sorun:
Agregasyon ve kompozisyon arasındaki yanlış seçim, bellek sızıntılarına, kaynakların çoğaltılmasına veya nesnelerin yıkımıyla ilgili hatalara yol açabilir. Ayrıca, bu kavramlar sıkça karıştırılır.
Çözüm:
Kod örneği:
// Kompozisyon: class Engine {}; class Car { Engine engine; // Engine, Car ile birlikte oluşturulur ve yok edilir }; // Ağregasyon: class Person {}; class Team { std::vector<Person*> members; // Person nesnelerine işaret eder, onlara sahip değildir };
Anahtar özellikler:
Eğer bir sınıf üyesinde bir nesneye işaret eden bir pointer varsa, bu her zaman agregasyon mudur?
Hayır! Eğer sınıf bu pointer üzerinde sahiplik taşırsa (örneğin, std::unique_ptr aracılığıyla), bu yine kompozisyondur. İlişki türü, alanın türünden değil, yaşam döngüsündeki sorumluluktan belirlenir.
class House { std::unique_ptr<Room> room; // kompozisyon, House Room'a sahiptir };
Kompozisyon, referans veya raw pointer aracılığıyla uygulanabilir mi?
Uygulanabilir — ancak yalnızca nesne sahibi tarafından oluşturulup yok ediliyorsa ve referans veya pointer optimizasyon için kullanılıyorsa. Ancak sahipliği açıkça ifade etmek için değer nesneleri veya akıllı pointer'lar kullanmak çok daha iyidir.
Eğer kompozisyondaki kısım-nesne sahibi dışında oluşturulup kendisine verilirse ne olur?
Bu durumda kompozisyonun invariyantlarının ihlal edilme riski vardır: eğer dışarıda oluşturulan nesne sahibine verilirse ve o nesneyi yok ederse, başka bir yerde ona referans varsa, dangling pointer oluşur. Projede sahiplik ve yok etme sorumluluklarını net bir şekilde tanımlamak gereklidir.
Bir ekip, tüm yerleşik nesneleri raw pointer'lar aracılığıyla bir konteynerde tutmaya ve yıkıcıda manuel olarak onları yok etmeye karar verdi. Her şey çalışıyordu, ta ki sahiplik şemasını değiştirdiklerinde. Sonuç olarak, pointer iki kez serbest bırakıldı ve bir çökme meydana geldi.
Artılar:
Eksiler:
Başka bir ekip, gerçekten sahiplik ilişkileri için std::unique_ptr kullanmaya geçti ve sahip olmayanları yalnızca geçici referanslar biçiminde kullandı. Bu, mimariyi açıkça ifade etti.
Artılar:
Eksiler: