C ++ 'da “arayüz” terimi


11

Java, classve arasında net bir ayrım yapar interface. (C # da inanıyorum, ama onunla hiçbir deneyimim yok). Ancak C ++ yazarken, sınıf ve arabirim arasında dil tarafından zorlanan bir ayrım yoktur.

Sonuç olarak ben her zaman Java çoklu kalıtım eksikliği için bir çözüm olarak gördüm. Böyle bir ayrım yapmak C ++ 'da keyfi ve anlamsızdır.

Ben her zaman "en belirgin şekilde şeyler yazmak" yaklaşımı ile gitmek eğiliminde, bu yüzden C ++ Java, örneğin bir arayüz denir ne var?

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() = 0;
};

ve sonra uygulayıcılarının çoğunun Foomuhtemelen yazacağım bazı ortak işlevleri paylaşmak istediğine karar verdim :

class Foo {
public:
  virtual void doStuff() = 0;
  ~Foo() {}
protected:
  // If it needs this to do its thing:
  int internalHelperThing(int);
  // Or if it doesn't need the this pointer:
  static int someOtherHelper(int);
};

Bu da artık Java anlamında bir arayüz değil.

Bunun yerine C ++ 'nin aynı temel miras problemiyle ilgili iki önemli konsepti vardır:

  1. virtual inhertiance
  2. Üye değişkeni olmayan sınıflar, temel olarak kullanıldığında fazladan yer kaplayamaz

    "Temel sınıf alt nesnelerinin boyutu sıfır olabilir"

    Referans

Mümkün olan her yerde # 1'den kaçınmaya çalıştım - bunun gerçekten "en temiz" tasarım olduğu bir senaryo ile karşılaşmak nadirdir. Ancak # 2, "arayüz" terimini anlamam ile C ++ dil özellikleri arasındaki ince fakat önemli bir farktır. Bunun bir sonucu olarak, şu anda (neredeyse) hiçbir zaman C ++ 'da "arabirimler" olarak bahsetmiyorum ve temel sınıflar ve boyutları açısından konuşuyorum. C ++ "arayüz" bağlamında bir yanlış ad olduğunu söyleyebilirim.

Ancak pek çok insanın böyle bir ayrım yapmadığı dikkatimi çekti.

  1. C ++ 'ta bir "arayüz" içinde (örn. protected) Olmayan virtualfonksiyonların var olmasına izin vererek bir şey kaybetmek için stand ? (Hislerim tam tersi - paylaşılan kod için daha doğal bir yer)
  2. "Arayüz" terimi C ++ için anlamlı mı - sadece saf virtualmı yoksa üye değişkenleri olmayan C ++ sınıflarını hala bir arayüz olarak çağırmak adil olur mu?

C #, arayüzlerin aynı çoklu mirasına, Java ile uygulamanın tek mirasına sahiptir , ancak genel uzantı yöntemlerinin kullanılmasıyla , internalHelperThinghemen hemen tüm durumlarda simüle edilebilir.
Sjoerd

Sanal üyeleri ortaya çıkarmanın kötü bir uygulama olduğunu unutmayın.
Klaim

~Foo() {}soyut bir sınıftaki bir halk (neredeyse) her koşul altında bir hatadır.
Wolf

Yanıtlar:


11

C ++, dönem "arabirim" değil, sadece sahip bir çok kabul gören tanımı - Bunu kullanacağız zaman öyleyse, tam olarak ne anlama geldiğini demeliyim - sanal bir temel sınıf varsayılan uygulamaları, bir başlık dosyası, kamu üyeleri ile veya olmadan rasgele bir sınıf vb.

Örneğinizle ilgili olarak: Java'da (ve C #'da benzer), kodunuz muhtemelen endişelerin ayrılmasını ima eder:

interface IFoo {/*  ... */} // here is your interface

class FooBase implements IFoo 
{
     // make default implementations for interface methods
}

class Foo extends FooBase
{
}

C ++ 'da bunu yapabilirsiniz , ancak zorunda değilsiniz. Bir sınıfa üye değişkenleri yoksa, ancak bazı yöntemler için varsayılan uygulamalar içeriyorsa, bir arabirimi çağırmak istiyorsanız, bunu yapın, ancak konuştuğunuz herkesin ne demek istediğinizi bildiğinden emin olun.


3
Bir C ++ programcısı için "arabirim" in başka bir olası anlamı dosyanın publicparçalarıdır .h.
David Thornley

Kod örneğiniz hangi dildir? Bu C ++ olmak için bir girişim ise ağlamak üzereyim ...
Qix - MONICA

@Qix: kolay tutun, yayınımı tekrar okuyun (kodun Java'da olduğu açıkça belirtilir) ve 2000'den fazla puanınız olması durumunda, örneğimi daha fazla yapmak için eşdeğer C ++ örnek kodunu eklemek için yayınımı düzenleyebilirsiniz. açık.
Doc Brown

Bu java ise yine de yanlıştır ve hayır bunu düzenleyemiyorum; değiştirmek için yeterli karakter yok. Java, uygulama / genişletmede iki nokta üst üste
işaretini

@Qix: daha sözdizimsel sorunlar bulursanız, bunları yılbaşı hediyesi olarak tutabilirsiniz :-)
Doc Brown

7

Bir arayüzün kavramsal olarak hem bir uygulama (küçük harf 'i' arayüzü) hem de bir soyutlama (Bir arayüz - büyük harf 'I') olduğu anlamını karıştırmanın tuzağına düşmüş gibi görünüyorsunuz. ).

Örneklerinizle ilgili olarak, ilk kod bitiniz sadece bir sınıftır. Sınıfınızda , davranışına erişimi sağlamak için yöntemler sağladığı anlamında bir arabirim olsa da, sınıfların yapmasını isteyebileceğiniz bir tür davranışı temsil eden bir soyutlama katmanı sağlayan bir Arayüz bildirimi anlamında bir Arabirim değildir . uygulamak. Doc Brown'ın yayınınıza verdiği cevap tam olarak burada neden bahsettiğimi gösteriyor.

Arayüzler genellikle birden fazla mirası desteklemeyen diller için "geçici çözüm" olarak lanse edilir, ancak bunun zor bir gerçeklikten çok bir yanlış anlama olduğunu düşünüyorum (ve bunu belirtmek için alev alabileceğimi sanıyorum!). Arabirimler, sınıflar arasında işlevsel uyumluluk veya sınıflar için uygulama soyutlaması sağlamak için miras alınmasını veya bir ata olmayı gerektirmemeleri nedeniyle çoklu kalıtımla hiçbir ilgisi yoktur. Aslında, kodunuzu bu şekilde uygulamak istiyorsanız, tamamen mirastan tamamen kurtulmanıza izin verebilirler - tamamen tavsiye etmem değil, ama sadece yapabileceğinizi söylüyorumyap. Gerçek şu ki, kalıtım konularına bakılmaksızın, Arayüzler, sınıf türlerinin tanımlanabileceği bir araç sağlar, nesnelerin birbirleriyle nasıl iletişim kurabileceğini belirleyen kurallar oluşturur, böylece sınıflarınızın olmadan desteklemesi gereken davranışı belirlemenize izin verir. bu davranışı uygulamak için kullanılan belirli yöntemi dikte etmek.

C ++ 'da bir "arayüz" içinde sanal olmayan fonksiyonların var olmasına izin vererek (örn. Korumalı) bir şey kaybetmek için stand? (Hislerim tam tersi - paylaşılan kod için daha doğal bir yer)

Saf bir Arayüz tamamen soyut olmalıdır, çünkü tanımları ortak bir atadan davranışlarını miras almamış olabilecek sınıflar arasında bir uyumluluk sözleşmesine izin verir. Uygulandığında, bir alt sınıfta uygulama davranışının genişletilmesine izin verilip verilmeyeceği konusunda bir seçim yapmak istersiniz. Yöntemleriniz değilse virtual, alt sınıflar oluşturmaya karar vermeniz durumunda bu davranışı daha sonra genişletme yeteneğini kaybedersiniz. Uygulamanın sanal olup olmamasına bakılmaksızın, Arayüz kendi içinde davranışsal uyumluluğu tanımlar, sınıf ise sınıfın temsil ettiği örnekler için bu davranışın uygulanmasını sağlar.

"Arayüz" terimi C ++ için anlamlı mıdır - sadece saf sanal anlamına mı gelir yoksa üye değişkeni olmayan C ++ sınıflarını hala bir arayüz olarak çağırmak adil olur mu?

Affet beni, ama C ++ 'a gerçekten ciddi bir uygulama yazdığımdan beri uzun zaman geçti. Arayüz burada açıkladığım gibi soyutlama amaçlı bir anahtar kelime olduğunu hatırlıyorum. Herhangi bir tür C ++ sınıfları bir arabirim çağırmazsınız, bunun yerine sınıf yukarıda özetlenen anlamları içinde bir arabirim olduğunu söyleyebilirim. Bu anlamda IS terimi anlamlıdır, ancak bu gerçekten bağlama bağlıdır.


1
"Sahip olmak" ve "olmak" bir arayüz arasındaki ayrım için +1.
Doc Brown

6

Java arayüzleri bir "geçici çözüm" değildir, elmas kalıtım gibi çoklu kalıtımla ilgili bazı sorunlardan kaçınmak ve eşleşmeyi en aza indiren tasarım uygulamalarını teşvik etmek için kasıtlı bir tasarım kararıdır.

Korumalı yöntemlerle ara yüzünüz, "kompozisyonu kalıtım yerine tercih etmeniz" gereken bir ders kitabı örneğidir. Bu adla Sutter ve Alexandrescu'dan mükemmel C ++ Kodlama Standartlarından alıntı yapmak için :

Veraset vergisinden kaçının: Veraset, C ++ 'da ikinci arkadaşlık ilişkisidir. Sıkı bağlantı istenmez ve mümkün olduğunca kaçınılmalıdır. Bu nedenle, ikincisinin tasarımınıza gerçekten fayda sağladığını bilmiyorsanız kompozisyonu kalıtım olarak tercih edin.

Yardımcı işlevlerinizi arayüzünüze dahil ederek, şimdi biraz yazımdan tasarruf ediyor olabilirsiniz, ancak sizi yolda incitecek bir bağlantı sunuyoruz. Yardımcı fonksiyonlarınızı ayırmak ve geçmek için neredeyse her zaman daha uzun vadelidir Foo*.

STL bunun güzel bir örneğidir. <algorithm>Konteyner sınıflarında bulunmak yerine mümkün olduğunca çok sayıda yardımcı işlev dışarı çekilir . Örneğin, sort()işini yapmak için genel kapsayıcı API'sını kullandığından, herhangi bir STL kodunu değiştirmek zorunda kalmadan kendi sıralama algoritmanızı uygulayabileceğinizi biliyorsunuz. Bu tasarım, STL'nin yükseltme hakkında hiçbir şey bilmesine gerek kalmadan, STL'yi değiştirmek yerine zenginleştiren boost gibi kütüphanelere olanak tanır.


2

C ++ 'da bir "arayüz" içinde sanal olmayan fonksiyonların var olmasına izin vererek (örn. Korumalı) bir şey kaybetmek için stand? (Hislerim tam tersi - paylaşılan kod için daha doğal bir yer)

Bunu düşünürsünüz, ancak başka bir soyut sınıfa korumalı, sanal olmayan bir yöntem koymak, bir alt sınıf yazan herkese uygulamayı gerektirir. Bunu yapmak, bir arayüzün amacını saf anlamda yenilgiye uğratır, bu da altında olanı gizleyen bir kaplama sağlamaktır.

Bu, herkese uyan tek bir cevap bulunmayan durumlardan biridir ve bir karar vermek için deneyiminizi ve yargınızı kullanmanız gerekir. Aksi halde tamamen sanal sınıfınızın her olası alt sınıfının Fooher zaman korumalı yöntemin uygulanmasına ihtiyaç duyacağını % 100 güvenle söyleyebiliyorsanız bar(), Foobunun için doğru yerdir. Bazİhtiyacınız olmayan bir alt sınıfa sahip olduğunuzda, gerekmemesi gereken koda erişimi olacak ya da sınıf hiyerarşinizi yeniden düzenleme alıştırmasıyla bar()yaşamak Bazzorundasınız. İlki iyi bir uygulama değildir ve ikincisi, ilk etapta işleri düzgün bir şekilde düzenlemek için alacağı birkaç dakikadan fazla zaman alabilir.

"Arayüz" terimi C ++ için anlamlı mıdır - sadece saf sanal anlamına mı gelir yoksa üye değişkeni olmayan C ++ sınıflarını hala bir arayüz olarak çağırmak adil olur mu?

C ++ standardının Bölüm 10.4'ü, arabirimleri uygulamak için soyut sınıfların kullanımından geçerken bahseder, ancak bunları resmi olarak tanımlamaz. Terim, genel bir bilgisayar bilimi bağlamında anlamlıdır ve yetkili herkes " Foo(ne olursa olsun) için bir arabirimdir" ifadesinin bir tür soyutlama anlamına geldiğini anlamalıdır . Tanımlı arayüz yapılarına sahip dillere maruz kalanlar saf sanal düşünebilirler, ancak gerçekten çalışması gereken herkes Foodevam etmeden önce tanımına bakacaktır.


2
Saf bir sanal bildirim sağlama ile bir bildirim artı uygulama arasındaki fark nedir? Her iki durumda da, bir uygulama olmalıdır ve bazher zaman böyle bir uygulama olabilir return false;. Yöntem tüm alt sınıflar için geçerli değilse, hiçbir şekilde temel soyut sınıfa ait değildir.
David Thornley

1
Sanırım son cümleniz aynı sayfada olduğumuzu söylüyor: soyut sınıflarda korumalı yöntemlere sahip olmanız için bir neden yok, ancak miras ağacında kesinlikle gerekenden daha yüksek olmamalıdırlar. Sadece bir sınıfın bir uygulamayı kukla etmesi gerekiyorsa, bunun ağaçta doğru yerde olmadığını düşünüyorum.
Blrfl
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.