2016-05-12 108 views
5

OOPs konsepti üzerine Silly Sorularım var, neden polimorfizm için gidiyoruz? C++OOP'da Polimorfizm'e ihtiyaç nedir?

Basit kodu: Dikdörtgen için geç bağlama o zaman Dikdörtgen beraberlik, sivri eğer

class Shape{ 
public: 
virtual void draw(){ cout<<"Shape"<<endl;}; 
}; 

class Traingle: public Shape 
{ 
public: void draw(){cout<<"Triangle"<<endl;} 
}; 

class Rectangle: public Shape 
{ 
public: void draw(){cout<<"Rectangle"<<endl;} 
}; 

int main(){ 
Shape *ptr= new Traingle(); 
ptr->draw(); 
delete ptr; 
return 7; 
} 

İşte ptr-> çizmek() fonksiyonu, Üçgen beraberlik arayacak.

Temel sınıf gösterici oluşturmanın ve farklı sınıflara işaret etmenin gerekliliği nedir? Herhangi bir sanal işlev olmadan ayrı bir sınıf nesnesi oluşturabilir ve ihtiyaç duyulan her şeyi arayabiliriz. temelde aynı şeyi yapar

int main(){ 
    Traingle tObj; 
    tObj->draw(); 
    Rectangle rObj; 
    rObj->draw(); 
} 

gibi;

Neden polimorfizm temelde? neden sanal?

İhtiyacı nedir ya da bu özelliği kullanarak ne gibi bir fark yaratıyor? Ve gerçek vaka örneği yardımcı olacaktır!

+0

Durumunuzda yeni bir işaretçiye gerek yok. Ancak bu, "polimorfizm için ihtiyaç nedir?" Ile ilgili değildir. " – juanchopanza

+0

Sanal işlevlerin noktası, çalışma zamanında davranışı tanımlayabilir ve nesnelerin dinamik kullanımını sağlar. Polimorfizm buna verilen isimdir. – Lawrence

+1

Örneğin, şekiller koleksiyonu oluşturmak isteyebilirsiniz. Std :: vector '' seçeneğini göz önünde bulundurun. Farklı şekiller içerebilir, ancak hepsine ortak şeyler yapabilirsiniz. – isapego

cevap

3

Polimorfizm ilgili tip nesneler, aynı tedavi edilecek sağlayarak kod yeniden kullanılmasına olanak sağlar.

Eğer farklı davranır alt sınıflara onlarca ihtiyaç duyabileceği göz önünde bulundurun:

struct Shape1: public Shape { /* .. */ }; // triangle 
struct Shape2: public Shape { /* .. */ }; // rectangle 
// ... 
struct ShapeN: public Shape { /* .. */ }; // projection of rhombic triacontahedron 

Eğer Shape işaretçiler dizisi ile işaret nesneleri işlemek gerekebilir olduğunu düşünün. polimorfizm ile

, tek vektör gerekir ve sanal fonksiyonu ile tek bir döngü çağırır: polimorfizm olmadan

std::vector<Shape*> v = get_shape_vector(); 
for(Shape* s : v) 
    s->draw(); 

, her türü için ayrı dizi yönetmek olurdu ve bunları ayrı ayrı işlemek:

polimorfizm kullanarak kod
std::vector<Shape1> v1 = get_shape1_vector(); 
std::vector<Shape2> v2 = get_shape2_vector(); 
// ... 
std::vector<ShapeN> vN = get_shapeN_vector(); 

for(Shape1& s : v1) 
    s.draw(); 
for(Shape2& s : v2) 
    s.draw(); 
// ... 
for(ShapeN& s : vN) 
    s.draw(); 

3 hat polimorfizm kullanmayan kod 3 * K hatları daha sağlamanın yolu, daha kolaydır.

işlemi değiştirmeniz gerekebilir olduğunu düşünün. Belki de çizimden önce bir işlev çağrısı eklemek istersiniz.

void pre_draw(Shape*); 

for(Shape* s : v) { 
    pre_draw(s); 
    s->draw(); 
} 

polimorfizmi olmadan, düzine her fonksiyonların onlarca tanımlamak ve değiştirmek gerekir döngüler:

void pre_draw1(Shape1&); 
void pre_draw2(Shape2&); 
// ... 
void pre_drawN(ShapeN&); 

for(Shape1& s : v1) { 
    pre_draw1(s); 
    s.draw(); 
} 
for(Shape2& s : v1) { 
    pre_draw2(s); 
    s.draw(); 
} 
// ... 
for(ShapeN& s : v1) { 
    pre_drawN(s); 
    s.draw(); 
} 

daha sonra şekil ekleme olabileceğini göz önünde bulundurun yanınızda polimorfizm olduğunda bu basit . Polimorfizm ile, yeni türü ve sanal işlevi tanımlamanız yeterlidir. Dizine işaretçiler ekleyebilir ve bunlar diğer her uyumlu türdeki nesneler gibi işlenecektir.

struct ShapeN1: public Shape { /* .. */ }; // yet another shape 

polymorhpism olmadan, yeni bir tür tanımlama yanında, bunun için yeni bir dizi oluşturmak gerekir. Ve yeni bir pre_draw işlevi oluşturmak gerekir. Ve bunları işlemek için yeni bir döngü eklemeniz gerekir.

void pre_drawN1(ShapeN1&); 
// ... 
std::vector<ShapeN1> vN1 = get_shapeN1_vector(); 
// ... 
for(ShapeN1& s : vN1) { 
    pre_drawN1(s); 
    s.draw(); 
} 

Aslında, şekil türünün her işlenir yerlerde için tüm kod tabanı geçmesi ve orada yeni türü için kod eklemek gerekir.


Şimdi, N küçük veya büyük olabilir. Daha büyük N ise, daha fazla tekrarlanan polimorfizm önler. Ama ne olursa olsun kaç kaç alt sınıfları, bir yenisi büyük bir nimet olduğunu eklediğinizde tüm kod tabanına bakmak zorunda değil.

3

Temel sınıf Shape düşünün. Bir GetArea yöntemini ortaya çıkarır. Square sınıfını ve Rectangle sınıfını ve Circle sınıfını düşünün. Ayrı GetSquareArea, GetRectangleArea ve GetCircleArea yöntemlerini oluşturmak yerine, türetilen sınıfların her birinde yalnızca bir yöntem uygulayabilirsiniz. Kullandığınız Shape'un hangi alt sınıfını bilmeniz gerekmiyor, sadece GetArea'u arayın ve sonucunuzu elde edersiniz, hangi beton tipinden bağımsız olarak elde edersiniz. Eğer, sadece taban türü kullanıyorsanız sınıfın tam türünü bilmek zorunda değilsiniz ve -

#include <iostream> 
using namespace std; 

class Shape 
{ 
public: 
    virtual float GetArea() = 0; 
}; 

class Rectangle : public Shape 
{ 
public: 
    Rectangle(float a) { this->a = a; } 
    float GetArea() { return a * a; } 
private: 
    float a; 
}; 

class Circle : public Shape 
{ 
public: 
    Circle(float r) { this->r = r; } 
    float GetArea() { return 3.14f * r * r; } 
private: 
    float r; 
}; 

int main() 
{ 
    Shape *a = new Circle(1.0f); 
    Shape *b = new Rectangle(1.0f); 

    cout << a->GetArea() << endl; 
    cout << b->GetArea() << endl; 
} 

Burada dikkat edilmesi gereken önemli bir nokta olduğunu:

bu kodu göz at Doğru sonucu alacaksın. Bu daha karmaşık sistemlerde de çok kullanışlıdır. biçimlilik kullanmak için en iyi örnek Imho

+5

Zaten bir çizim yöntemiyle bir sınıf hiyerarşisi vardı. Buna göre açıklayamadın mı? –

0

bir kapsayıcıdır:

std::vector<Shape*> shapes; 
shapes.push_back(new Triangle()); 
shapes.push_back(new Rectangle()); 

Eğer nesnelerin gerçek türüdür ne zamandan beri ilgilenmeye olmadan bu kabın yineleyebilirsiniz:

for (int i=0;i<shapes.size();i++){ 
    shapes[i]->draw(); 
} 
0

Basitçe söylemek gerekirse, Polimorfizm, aynı kutuya farklı şekiller koymanızı ve kutudaki her şeyi heterojen bir şekilde ele almanızı sağlar.

seni örnek uzanan:

#include <iostream> 
#include <memory> 
#include <vector> 

class Shape{ 
public: 
    virtual void draw(){ std::cout<<"Shape"<< std::endl;}; 
}; 

class Traingle: public Shape 
{ 
public: 
    void draw() override 
    { 
     std::cout<<"Triangle"<< std::endl; 
    } 
}; 

class Rectangle: public Shape 
{ 
public: 
    void draw() override 
    { 
     std::cout<<"Rectangle"<< std::endl; 
    } 
}; 

int main(){ 

    std::vector<std::unique_ptr<Shape>> box_of_shapes; 

    box_of_shapes.emplace_back(new Traingle); 
    box_of_shapes.emplace_back(new Rectangle); 

    for (const auto& pshape : box_of_shapes) 
    { 
     pshape->draw(); 
    } 

    return 0; 
} 

Rectanglebir (o kamuya Shape türetilmiş çünkü) Shape nedenle (işaretçilere) Shape fırsatlar bir vektör tarafından tutulabilir. Ancak, draw yönteminin sanal olması nedeniyle, arayan kişi yalnızca Shape olarak düşünülse bile Rectangle doğru çizilecektir.

beklenen çıktı:

Triangle 
Rectangle 
0

Sihirli kelime ayrıştırma olduğunu. Bir süpermarketin depolanmasını yönetmek için bir uygulama oluşturduğunuzu varsayalım. Baştan beri, er ya da geç veriye ihtiyaç duyacağınız bir bilgiye ihtiyacınız olduğunu hemen hemen biliyorsunuz. Ancak, henüz kullanılacak en iyi teknoloji hangi bilmiyorum (bir ilişkisel veritabanı veya örneğin bir NoSQL biri olabilir).Sadece serialize bellek sürümünde bir uygulama uygulamanın diğer bölümlerinin gelişmesi ile devam edebilir, Şimdi

struct Serialize{ 
    virtual void save(Product product) = 0; 
    ... other method here ... 
}; 

:

Yani, bir arabirim (soyut sınıf) oluşturun.

class InMemorySerialize : public Serialize { 
    ... implement stuff here ... 
}; 

, gerçekten gereken tek yer serialize uygulamasını değiştirmek için değiştirilmesi somut hangi sınıfta işlenen umursamıyor serialize bağlıdır kod beton sınıfının yapıdır. Fakat sınıfın inşası muhtemelen tek bir yerde ve ana fonksiyona oldukça yakındır (daha fazla bilgi için strategy pattern'a bakın).

//in the main (switching these lines you will use different implementations) 
//the rest of your code will not change 
unique_ptr<Serialize> serializer(new InMemorySerialize()); 
//unique_ptr<Serialize> serializer(new OnFileSerialize(myfolder)); 

Ayrıca, bir arabirim uygulama Open/Close principle yerine getirmek için basit yöntemlerden biridir. Bellek yerine dosyaya serileştirmeyi desteklemek istiyorsanız, ayrı bir dosyada olacak yeni bir sınıf oluşturacaksınız. Bu nedenle, mevcut koda dokunmadan uygulamanızın işlevselliğini arttırmaya açıktır.

Son bir açıklama olarak, beton nesneler yerine arayüzlere bağlıysanız, test yapmak çok daha kolaydır. Testiniz için ihtiyaç duyduğunuz davranışı kolayca uygulayabilirsiniz.

İlgili konular