C ++ 'da aynı sınıf için farklı türler nasıl tanımlanır


84

Aynı uygulamayı paylaşan ancak yine de C ++ 'da farklı türde olan birkaç türe sahip olmak istiyorum.

Sorumu basit bir örnekle açıklamak için, hepsi aynı işlemlere ve aynı uygulamaya sahip olan Elma, Portakal ve Muz sınıfına sahip olmak istiyorum. Farklı tiplere sahip olmalarını isterim çünkü tip güvenliği sayesinde hataları önlemek istiyorum.

class Apple {
     int p;
public:
     Apple (int p) : p(p) {}
     int price () const {return p;}
}

class Banana {
     int p;
public:
     Banana (int p) : p(p) {}
     int price () const {return p;}
}

class Orange ...

Kodu çoğaltmamak için, bir temel sınıf Fruit kullanıp ondan miras alabilirim gibi görünüyor:

class Fruit {
     int p;
public:
     Fruit (int p) : p(p) {}
     int price () const {return p;}
}

class Apple: public Fruit {};
class Banana: public Fruit {};
class Orange: public Fruit {};

Ama sonra, kurucular miras alınmaz ve onları yeniden yazmam gerekir.

Aynı sınıfa farklı türlerle kolayca sahip olmamı sağlayacak herhangi bir mekanizma (yazım biçimleri, şablonlar, kalıtım ...) var mı?


Buna neden ihtiyaç duyduğunuzu daha detaylı açıklayabilir misiniz? İyi bir fikir bulamıyorum. Sınıflar uygulamayı paylaşıyorsa, bu aynı zamanda işlevselliği paylaştıkları anlamına gelmez mi?
jnovacho

4
Evet, ancak farklı türleri olacağından, derleme sırasında bazı programlama hataları tespit edilebilir (örneğin, Elmaları ve Portakalları birleştirme).
anumi

Yanıtlar:


119

Yaygın bir teknik, şablon bağımsız değişkeninin onu benzersiz bir tür haline getirmek için benzersiz bir simge ("etiket") işlevi gördüğü bir sınıf şablonuna sahip olmaktır:

template <typename Tag>
class Fruit {
    int p;
public:
    Fruit(int p) : p(p) { }
    int price() const { return p; }
};

using Apple = Fruit<struct AppleTag>;
using Banana = Fruit<struct BananaTag>;

Etiket sınıfları bile tanımlanması gerekmez Not, bu yeterlidir beyan benzersiz tür adı. Bu işe yarar çünkü etiket aslında şablonun herhangi bir yerinde kullanılmamaktadır . Ve şablon argüman listesinin içinde tür adını bildirebilirsiniz (şapka ipucu @Xeo'ya).

usingSözdizimi C ++ 11'dir. C ++ 03 ile takıldıysanız, bunun yerine şunu yazın:

typedef Fruit<struct AppleTag> Apple;

Ortak işlevsellik çok fazla kod gerektiriyorsa, bu ne yazık ki son çalıştırılabilir dosyada oldukça fazla yinelenen kod ortaya çıkarır. Bu, işlevselliği uygulayan ortak bir temel sınıfa sahip olmak ve ardından ondan türetilen bir uzmanlaşmaya (gerçekten başlattığınız) sahip olmak suretiyle önlenebilir.

Ne yazık ki, bu miras alınamayan tüm üyeleri (yapıcılar, atama…) yeniden uygulamanızı gerektirir ve bu da küçük bir ek yük getirir - bu nedenle bu yalnızca büyük sınıflar için mantıklıdır. Burada yukarıdaki örneğe uygulanır:

// Actual `Fruit` class remains unchanged, except for template declaration
template <typename Tag, typename = Tag>
class Fruit { /* unchanged */ };

template <typename T>
class Fruit<T, T> : public Fruit<T, void> {
public:
    // Should work but doesn’t on my compiler:
    //using Fruit<T, void>::Fruit;
    Fruit(int p) : Fruit<T, void>(p) { }
};

using Apple = Fruit<struct AppleTag>;
using Banana = Fruit<struct BananaTag>;

+1, tek tek meyveler için tanımlanmış ek özelliklerin olmasını istemiyorsan bununla giderim ...
Nim

20
Aslında sadece ben çok rahat bulmak şablon bağımsız değişken listesinin içinde bunları bildirebilirsiniz: Fruit<struct SomeTag>.
Xeo

1
@KonradRudolph, düzenlemenin kendisini + 1'leyememem çok yazık ..... Düzenleme yorumunu gördüm .
eternalmatt

1
@eternalmatt LOL - Bunu kimsenin göreceğini asla düşünmezdim. Ama kimse bakmıyorken bile komik olmalısın. ;-)
Konrad Rudolph

2
Bunun bir dezavantajı, farklı türler için şablon somutlaştırmanın çoklu emisyonudur. Bu kopyalar, yaygın olarak kullanılan bağlayıcılar tarafından elimine ediliyor mu?
boycy

19

Şablonları kullanın ve meyve başına bir özellik kullanın , örneğin:

struct AppleTraits
{
  // define apple specific traits (say, static methods, types etc)
  static int colour = 0; 
};

struct OrangeTraits
{
  // define orange specific traits (say, static methods, types etc)
  static int colour = 1; 
};

// etc

Daha sonra, Fruitörneğin bu özellik üzerine yazılan tek bir sınıfa sahip olun.

template <typename FruitTrait>
struct Fruit
{
  // All fruit methods...
  // Here return the colour from the traits class..
  int colour() const
  { return FruitTrait::colour; }
};

// Now use a few typedefs
typedef Fruit<AppleTraits> Apple;
typedef Fruit<OrangeTraits> Orange;

Biraz fazla olabilir! ;)



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.