Derleme zamanı polimorfizminin ve çalışma zamanı polimorfizminin gerçek sınırlamalarını anladığımı düşünüyorum. Ancak açık arayüzler (çalışma zamanı polimorfizmi, yani sanal fonksiyonlar ve işaretçiler / referanslar) ve örtülü arayüzler (derleme zamanı polimorfizmi, yani şablonlar) arasındaki kavramsal farklar nelerdir .
Düşüncelerim, aynı açık arabirimi sunan iki nesnenin aynı türde nesne (veya ortak bir ataya sahip) olması gerektiğinden, aynı örtülü arabirimi sunan iki nesnenin aynı tür nesne olması ve örtük hariç tutulması gerektiğidir. Her ikisinin de sunduğu arayüz, oldukça farklı bir işleve sahip olabilir.
Bunun hakkında bir fikrin var mı?
Ve eğer iki nesne aynı örtük arayüzü sunuyorsa, hangi nedenlerin (sanal işlev arama tablosu olmadan dinamik gönderime ihtiyaç duyulmamasının teknik faydasının yanında), bu nesnelerin o arayüzü bildiren bir temel nesneden miras almaması nedeniyle vardır, onu açık bir arayüz yapıyor mu? Bunu söylemenin başka bir yolu: bana, aynı örtük arabirimi sunan iki nesnenin (ve bu nedenle örnek şablon sınıfına tür olarak kullanılabileceği), bu arabirimi açık yapan bir temel sınıftan miras almaması gerektiğini söyleyebilir misiniz?
İlgili bazı yayınlar:
- https://stackoverflow.com/a/7264550/635125
- https://stackoverflow.com/a/7264689/635125
- https://stackoverflow.com/a/8009872/635125
İşte bu soruyu daha somut hale getirecek bir örnek:
Örtülü Arayüz:
class Class1
{
public:
void interfaceFunc();
void otherFunc1();
};
class Class2
{
public:
void interfaceFunc();
void otherFunc2();
};
template <typename T>
class UseClass
{
public:
void run(T & obj)
{
obj.interfaceFunc();
}
};
Açık Arayüz:
class InterfaceClass
{
public:
virtual void interfaceFunc() = 0;
};
class Class1 : public InterfaceClass
{
public:
virtual void interfaceFunc();
void otherFunc1();
};
class Class2 : public InterfaceClass
{
public:
virtual void interfaceFunc();
void otherFunc2();
};
class UseClass
{
public:
void run(InterfaceClass & obj)
{
obj.interfaceFunc();
}
};
Daha derinlemesine, somut bir örnek:
Bazı C ++ sorunları şunlardan biri ile çözülebilir:
- şablon türü örtük bir arabirim sağlayan şablonlu bir sınıf
- müstehcen bir arabirim sağlayan temel sınıf işaretçisini alan şablonsuz bir sınıf
Değişmeyen kod:
class CoolClass
{
public:
virtual void doSomethingCool() = 0;
virtual void worthless() = 0;
};
class CoolA : public CoolClass
{
public:
virtual void doSomethingCool()
{ /* Do cool stuff that an A would do */ }
virtual void worthless()
{ /* Worthless, but must be implemented */ }
};
class CoolB : public CoolClass
{
public:
virtual void doSomethingCool()
{ /* Do cool stuff that a B would do */ }
virtual void worthless()
{ /* Worthless, but must be implemented */ }
};
Durum 1 . Açık bir arabirim sağlayan temel sınıf işaretçisini alan şablonsuz bir sınıf:
class CoolClassUser
{
public:
void useCoolClass(CoolClass * coolClass)
{ coolClass.doSomethingCool(); }
};
int main()
{
CoolA * c1 = new CoolClass;
CoolB * c2 = new CoolClass;
CoolClassUser user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
Durum 2 . Şablon türü örtük bir arabirim sağlayan şablonlanmış bir sınıf:
template <typename T>
class CoolClassUser
{
public:
void useCoolClass(T * coolClass)
{ coolClass->doSomethingCool(); }
};
int main()
{
CoolA * c1 = new CoolClass;
CoolB * c2 = new CoolClass;
CoolClassUser<CoolClass> user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
Durum 3 . Şablon türü örtük bir arabirim sağlayan şablonlanmış bir sınıf (bu sefer, şu kaynaktan türetilmez CoolClass
:
class RandomClass
{
public:
void doSomethingCool()
{ /* Do cool stuff that a RandomClass would do */ }
// I don't have to implement worthless()! Na na na na na!
}
template <typename T>
class CoolClassUser
{
public:
void useCoolClass(T * coolClass)
{ coolClass->doSomethingCool(); }
};
int main()
{
RandomClass * c1 = new RandomClass;
RandomClass * c2 = new RandomClass;
CoolClassUser<RandomClass> user;
user.useCoolClass(c1);
user.useCoolClass(c2);
return 0;
}
Durum 1, aktarılan nesnenin useCoolClass()
bir alt öğesi CoolClass
(ve uygulama worthless()
) olmasını gerektirir. Olgu 2 ve 3 ise işlevi olan herhangi bir sınıfı alacaktır doSomethingCool()
.
Kod kullanıcıları her zaman iyi alt sınıflar olsaydı CoolClass
, Durum 1 sezgisel mantıklıdır, çünkü CoolClassUser
her zaman bir a uygulamasını beklerdi CoolClass
. Ancak bu kodun bir API çerçevesinin parçası olacağını varsayalım, bu yüzden kullanıcıların CoolClass
bir doSomethingCool()
işlevi olan kendi sınıflarını alt sınıflara mı yoksa yuvarlamak isteyip istemediklerini tahmin edemem .