Öncelikle değere ve referansa göre geçmenin ne anlama geldiğine geri dönmeliyiz.
Java ve SML gibi diller için, değere göre geçiş basittir (ve referans ile geçiş yoktur), tıpkı bir değişken değerin kopyalanması gibi, tüm değişkenler sadece skaler olduğu ve yerleşik kopya semantiğine sahip oldukları için: ya aritmetik olarak sayılan şeydir C ++ veya "referanslar" (farklı ad ve sözdizimine sahip işaretçiler) yazın.
C'de skaler ve kullanıcı tanımlı tiplerimiz vardır:
- Skalerlerin kopyalanan sayısal veya soyut bir değeri vardır (işaretçiler sayı değildir, soyut bir değere sahiptir).
- Toplama türlerinde başlatılmış tüm üyeleri kopyalanır:
- ürün türleri (diziler ve yapılar) için: özyinelemeli olarak, dizilerin yapılarının ve öğelerinin tüm üyeleri kopyalanır (C işlevi sözdizimi dizileri doğrudan değere göre geçirmeyi mümkün kılmaz, yalnızca bir yapının üyelerini diziler, ancak bu bir ayrıntı ).
- toplam türleri (birlikler) için: "aktif üye" nin değeri korunur; açıkçası, tüm üyeler başlatılamayacağından üye kopyasına göre üye sıralaması yapılamaz.
C ++ 'da kullanıcı tanımlı tipler, kaynaklarına ve "derin kopya" işlemlerine sahip nesnelerle gerçekten "nesne yönelimli" programlamayı sağlayan kullanıcı tanımlı kopya semantiğine sahip olabilir. Böyle bir durumda, bir kopyalama işlemi gerçekten neredeyse keyfi işlemler yapabilen bir işleve çağrıdır.
C ++ olarak derlenen C yapıları için "kopyalama", derleyici tarafından örtük olarak oluşturulan kullanıcı tanımlı kopyalama işlemini (yapıcı veya atama operatörü) çağırmak olarak tanımlanır. Bu, bir C / C ++ ortak altkümesi programının semantiğinin C ve C ++ 'da farklı olduğu anlamına gelir: C'de bütün bir toplu tip kopyalanır, C ++' da her üyeyi kopyalamak için örtük olarak oluşturulan bir kopyalama işlevi çağrılır; sonuç, her iki durumda da her üyenin kopyalanmasıdır.
(Bence, bir birlik içindeki bir yapı kopyalandığında bir istisna vardır.)
Bir sınıf türü için, yeni bir örnek oluşturmanın tek yolu (sendika kopyaları dışında) bir kurucu aracılığıyladır (önemsiz derleyici oluşturucu kurucular için bile).
Bir değerlik adresini tekli operatör aracılığıyla &
alamazsınız, ancak bu bir değerleme nesnesi olmadığı anlamına gelmez; ve tanım olarak bir nesnenin bir adresi vardır ; ve bu adres bir sözdizimi yapısı ile temsil edilir: sınıf türünde bir nesne yalnızca bir kurucu tarafından oluşturulabilir ve bir this
işaretçisi vardır; ancak önemsiz türler için, kullanıcı tarafından yazılmış bir kurucu yoktur, bu nedenle this
kopyanın oluşturulup adlandırılmasından sonraya kadar bir yer yoktur .
Skaler tip için, bir nesnenin değeri, nesnenin depolanan saf matematiksel değer olan nesnenin değeridir.
Bir sınıf türü için, nesnenin bir değerinin tek nüshası, nesnenin başka bir kopyasıdır, bu sadece bir kopya oluşturucu, gerçek bir işlev tarafından yapılabilir (bu işlev çok özel önemsiz olsa da, bunlar bazen yapıcı çağrılmadan oluşturulur). Bu , nesnenin değerinin, bir yürütme tarafından genel program durumunun değişmesinin sonucu olduğu anlamına gelir . Matematiksel olarak erişmez.
Yani değere göre geçmek gerçekten bir şey değildir: daha az güzel olan kopya yapıcı çağrısı ile geçer . Kopya yapıcısının, dahili değişmezlerine (içsel C ++ özellikleri değil soyut kullanıcı özellikleri olan) saygı duyarak, nesne türünün uygun semantiğine göre mantıklı bir "kopya" işlemi gerçekleştirmesi beklenir.
Bir sınıf nesnesinin değerine göre geçiş şu anlama gelir:
- başka bir örnek oluştur
- sonra çağrılan işlevin bu örnek üzerinde hareket etmesini sağlayın.
Sorunun, kopyanın kendisinin bir adrese sahip bir nesne olup olmadığıyla bir ilgisi olmadığını unutmayın: tüm işlev parametreleri nesnelerdir ve bir adrese sahiptir (dil anlamsal düzeyinde).
Sorun şudur:
- kopya, skalerlerde olduğu gibi orijinal nesnenin saf matematiksel değeriyle (gerçek saf değer) başlatılan yeni bir nesnedir ;
- veya kopya, sınıflarda olduğu gibi orijinal nesnenin değeridir .
Önemsiz bir sınıf türü söz konusu olduğunda, orijinalin üye kopyasının üyesini yine de tanımlayabilirsiniz, böylece kopyalama işlemlerinin önemsizliği nedeniyle orijinalin saf değerini tanımlayabilirsiniz (kopya oluşturucu ve atama). Rasgele özel kullanıcı işlevlerinde böyle değildir: orijinalin bir değeri oluşturulmuş bir kopya olmalıdır.
Sınıf nesneleri arayan tarafından oluşturulmalıdır; bir kurucu resmi olarak bir işaretleyiciye sahiptir, this
ancak biçimselcilik burada alakalı değildir: tüm nesnelerin resmi olarak bir adresi vardır, ancak yalnızca adreslerini tamamen yerel olmayan yollarla *&i = 1;
kullananların (tamamen yerel adres kullanımı aksine ) iyi tanımlanmış olması gerekir adres.
Bir nesne, bu iki ayrı derlenmiş işlevde bir adrese sahip olması gerekiyorsa, kesinlikle adrese göre iletilmelidir:
void callee(int &i) {
something(&i);
}
void caller() {
int i;
callee(i);
something(&i);
}
Burada something(address)
saf bir işlev veya makro veya printf("%p",arg)
adresi depolayamayan veya başka bir varlıkla iletişim kuramayan herhangi bir şey (gibi ) olsa bile , adresin benzersiz int
bir özelliğe sahip benzersiz bir nesne için iyi tanımlanması gerektiğinden adrese geçme gereksinimimiz vardır. Kimlik.
Harici bir fonksiyonun kendisine iletilen adresler açısından "saf" olup olmayacağını bilmiyoruz.
İşte potansiyeli olmayan önemsiz yapıcısı veya yıkıcıdaki birinde adresin gerçek kullanım için arayan tarafında olduğu gibi, güvenli, basit yol alarak ve adresini arayan içinde nesne bir kimlik vermek ve pas nedeni muhtemelen yapar adresinin kurucudaki, inşaattan sonra ve yıkıcıdaki önemsiz olmayan kullanımlarının tutarlı olduğundan emin olun : this
nesne varlığı üzerinde aynı görünmelidir.
Diğer herhangi bir işlev gibi önemsiz bir kurucu veya yıkıcı, this
önemsiz şeyler içeren bazı nesneler yapmasa da işaretçiyi değeri üzerinde tutarlılık gerektiren bir şekilde kullanabilir :
struct file_handler { // don't use that class!
file_handler () { this->fileno = -1; }
file_handler (int f) { this->fileno = f; }
file_handler (const file_handler& rhs) {
if (this->fileno != -1)
this->fileno = dup(rhs.fileno);
else
this->fileno = -1;
}
~file_handler () {
if (this->fileno != -1)
close(this->fileno);
}
file_handler &operator= (const file_handler& rhs);
};
Bu durumda, bir işaretçinin (açık sözdizimi this->
) açıkça kullanılmasına rağmen , nesne kimliğinin alakasız olduğuna dikkat edin: derleyici, nesneyi taşımak ve "kopya elizyonu" yapmak için nesneyi bitsel olarak kopyalayabilir. Bu, this
özel üye işlevlerinde (adres kaçmaz) "saflık" düzeyine dayanır .
Ancak saflık, standart bildirim düzeyinde kullanılabilen bir özellik değildir (satıriçi olmayan işlev bildirimine saflık açıklaması ekleyen derleyici uzantıları vardır), bu nedenle kullanılamayan kodun saflığına dayalı bir ABI tanımlayamazsınız (kod veya satır içi ve analiz için uygun olmayabilir).
Saflık "kesinlikle saf" veya "saf olmayan veya bilinmeyen" olarak ölçülür. Anlamsal zemin veya anlambilimin üst sınırı (aslında maksimum) veya LCM (En Küçük Ortak Çoklu) "bilinmiyor" dur. Böylece ABI bilinmeyene karar verir.
Özet:
- Bazı yapılar derleyicinin nesne kimliğini tanımlamasını gerektirir.
- ABI, optimize edilebilecek belirli durumlar değil, program sınıfları açısından tanımlanır.
Gelecekteki olası çalışmalar:
Saflık açıklaması genelleştirilecek ve standartlaştırılacak kadar yararlı mı?