C ++ Yineleyici, Neden tüm yineleyiciler devralınan yineleyici temel sınıfı yok


11

Bir sınav için öğreniyorum ve vermek ve cevaplamak için mücadele ettiğim bir sorum var.

Neden yineleyici temel sınıfı diğer tüm yineleyiciler devralınmaz?

Sanırım öğretmenim hiyerarşik yapıya " http://prntscr.com/mgj542 " cpp referansından bahsediyor ve neden olması gerektiğinden başka bir neden sunmalıyız?

Yineleyicilerin ne olduğunu (konteynırlarda) kullanıldıklarını biliyorum. Altta yatan farklı veri yapıları nedeniyle anladığım kadarıyla, farklı kapsayıcılar farklı yineleyicilere sahiptir, çünkü bir diziye rasgele erişebilirsiniz, ancak bağlantılı bir listeye değil ve farklı kapsayıcılar bunlar arasında hareket etmenin farklı yollarını gerektirir.

Kapsayıcıya bağlı olarak muhtemelen özel şablonlardır, değil mi?


2
Araştırmanızı paylaşmak herkese yardımcı olur . Neyi denediğinizi ve neden ihtiyaçlarınızı karşılamadığını bize bildirin. Bu, kendinize yardım etmeye zaman ayırdığınızı gösterir, bizi açık cevapları tekrar etmekten kurtarır ve en önemlisi daha spesifik ve ilgili bir cevap almanıza yardımcı olur. Ayrıca bkz. Nasıl
gnat

5
Neden yineleyici temel sınıfı diğer tüm yineleyiciler mirastan yok? ” Ee ... neden bir tane olmalı ?
Nicol Bolas

Yanıtlar:


14

Zaten tüm yineleyicilerin tek bir Iterator temel sınıfından miras almasının neden gerekli olmadığını gösteren yanıtlar aldınız. Yine de biraz daha ilerlerdim. C ++ hedeflerinden biri, sıfır çalışma zamanı maliyeti ile soyutlamadır.

Yineleyiciler, ortak bir temel sınıftan miras alıp hepsi tarafından çalıştıysa ve arabirimi tanımlamak için temel sınıfta sanal işlevler kullandıysa ve türetilmiş sınıflar, bu sanal işlevlerin uygulamalarını sağladıysa, önemli çalışma süresi ekleyebilir (ve çoğu zaman) ilgili operasyonlara ek yük.

Örneğin, kalıtım ve sanal işlevleri kullanan basit bir yineleyici hiyerarşisini düşünelim:

template <class T>
class iterator_base { 
public:
    virtual T &operator*() = 0;
    virtual iterator_base &operator++() = 0;
    virtual bool operator==(iterator_base const &other) { return pos == other.pos; }
    virtual bool operator!=(iterator_base const &other) { return pos != other.pos; }
    iterator_base(T *pos) : pos(pos) {}
protected:
    T *pos;
};

template <class T>
class array_iterator : public iterator_base<T> {
public: 
    virtual T &operator*() override { return *pos; }
    virtual array_iterator &operator++() override { ++pos; return *this; }
    array_iterator(T *pos) : iterator_base(pos) {}
};

O zaman hızlı bir test yapalım:

int main() { 
    char input[] = "asdfasdfasdfasdfasdfasdfasdfadsfasdqwerqwerqwerqrwertytyuiyuoiiuoThis is a stringy to search for something";
    using namespace std::chrono;

    auto start1 = high_resolution_clock::now();
    auto pos = std::find(std::begin(input), std::end(input), 'g');
    auto stop1 = high_resolution_clock::now();

    std::cout << *++pos << "\n";

    auto start2 = high_resolution_clock::now();
    auto pos2 = std::find(array_iterator(input), array_iterator(input+sizeof(input)), 'g');
    auto stop2 = high_resolution_clock::now();

    std::cout << *++pos2 << "\n";

    std::cout << "time1: " << duration_cast<nanoseconds>(stop1 - start1).count() << "ns\n";
    std::cout << "time2: " << duration_cast<nanoseconds>(stop2 - start2).count() << "ns\n";
}

[not: derleyicinize bağlı olarak, derleyicinin yineleyiciyi kabul etmesi için iterator_category, fark_tipi, başvuru vb. tanımlamak gibi biraz daha fazla şey yapmanız gerekebilir.]

Ve çıktı:

y
y
time1: 1833ns
time2: 2933ns

[Elbette, kodu çalıştırırsanız sonuçlarınız bunlarla tam olarak eşleşmez.]

Bu nedenle, bu basit durum için (ve yalnızca yaklaşık 80 artış ve karşılaştırma yaptıkça) basit bir doğrusal aramaya yaklaşık% 60 ek yük ekledik. Özellikle yineleyiciler başlangıçta C ++ 'a eklendiğinde, çok az insan bu kadar genel giderleri olan bir tasarımı kabul etmezdi. Muhtemelen standardize olmazlardı ve olsaydı bile, neredeyse hiç kimse onları kullanmazdı.


7

Aradaki fark, bir şeyin ne olduğu ve Bir şeyin nasıl davrandığı arasındaki.

Pek çok dil ikisini bir araya getirmeye çalışır, ancak bunlar oldukça farklı şeylerdir.

Nasıl ve Nasıl?

Her şey miras alırsa objecto zaman bazı faydalar meydana gelir: nesnenin herhangi bir değişkeni herhangi bir değeri tutabilir. Ama ovmak da olduğu, her şey olmalıdır (davranırlar how bir benzeri) object(gibi ve göz neyi ) bir object.

Fakat:

  • Nesnenizde anlamlı bir eşitlik tanımı yoksa ne olur?
  • Anlamlı bir karması yoksa ne olur?
  • Nesneniz klonlanamıyor, ancak nesneler olabilirse ne olur?

Her iki objecttür arasında da ortak bir özellik sağlamayan nesne nedeniyle, ya tür temel olarak işe yaramaz hale gelir. Ya da bazı objectgotikler dışında neredeyse evrensel davranışı kanıtlayan bazı evrensel özelliklerin kırılmış / ayakkabı boynuzlu / saçma bir tanımına sahip nesneler olacaktır .

Ne ile ilişkili değildir Nasıl

Alternatif olarak, Ne ve Nasıl Ayrı tutabilirsiniz . Sonra birkaç farklı türleri (en ortak noktası hiçbir şey tüm neyi ) can işbirlikçisi bakıldığı zaman aynı şekilde tüm davranan nasıl . Bu anlamda bir fikri Iteratorbelirli değildir Ne , ama nasıl . Özellikle Nasıl bir şeyle sizi etkileşim yapmak Henüz bilmiyorum ne sen etkileşimde bulunur.

Java (ve benzeri), arayüzler kullanarak buna yaklaşımlara izin verir. Bu bağlamdaki bir arayüz iletişim araçlarını ve dolaylı olarak izlenen bir iletişim ve eylem protokolünü açıklar. Kendini belirli bir Nasıl olarak ilan eden herhangi bir şey , bunun protokol tarafından belirtilen ilgili iletişimi ve eylemi desteklediğini belirtir. Bu, herhangi bir ortak çalışan güvenmek sağlayan How ve tam olarak hangi belirterek saplanıp değil ne 'ler kullanılabilir.

C ++ (ve benzeri) ördek yazarak buna yaklaşım sağlar. Ortak çalışma türü, belirli bir derleme bağlamında, nesnenin belirli bir şekilde etkileşime girebileceğini belirten bir davranışı izlediğini bildirirse umursamaz. Bu, C ++ işaretçileri ve belirli işleçleri aşan nesnelerin aynı kod tarafından kullanılmasına izin verir. Çünkü eşdeğer sayılmak için kontrol listesine uyuyorlar.

  • * a, a->, ++ a ve a ++ -> giriş / ileri yineleyiciyi destekler
  • * a, a->, ++ a, a ++, --a ve a-- -> çift yönlü yineleyiciyi destekler

Altta yatan tür bir kabı yinelemek zorunda bile değil, herhangi bir şey olabilir . Buna ek olarak, bazı ortak çalışanların daha genel olmasına, sadece bir işlevin ihtiyaç duyduğunu hayal etmesine izin verir a++, bir yineleyici bunu tatmin edebilir, böylece bir işaretçi de olabilir, bir tamsayı da olabilir, herhangi bir nesne uygulayabilir operator++.

Alt ve Üst Özellikler

Her iki yaklaşımın da problemi az ve fazladır.

Bir arabirim kullanmak, nesnenin belirli bir davranışı desteklediğini beyan etmesini gerektirir, bu da yaratıcının bunu başından itibaren emmek zorunda olduğu anlamına gelir. Bu, bazı nedenleri nedir bunu beyan etmedi olarak, kesim yapmamaya s'. Hiç Ayrıca araç ne de ortak bir atası var, arayüz temsil How . Bu, ilk sorununa geri döner object. Bu, ortak çalışanların gereksinimlerini gereğinden fazla belirtmelerine neden olurken, aynı zamanda bazı nesnelerin bildirim eksikliği nedeniyle kullanılamaz olmasına veya beklenen bir davranış kötü tanımlandığı için gizli yakalamalar olmasına neden olur.

Bir şablon kullanmak, ortak çalışanın tamamen bilinmeyen bir Ne ile çalışmasını gerektirir ve etkileşimleri aracılığıyla bir Nasıl tanımlar . O analiz etmeliyiz gibi bazı ölçüde bu markaları, sert işbirlikçileri yazma Ne dışına iletişim ilkel için (işlevleri / alanlar / vs) derleme hataları kaçınırken, ya da en azından nokta nasıl belirli bir Ne için gereksinimleri ile eşleşmiyor How . Bu herhangi bir mutlak minimum gerektirecek ortak çalışan veriyor Ne en geniş yelpazede sağlayan neyi 'kullanılacak s. Ne yazık ki, bu teknik için belirli bir iletişim ilkelerini sağlayan nesnelerin anlamsız kullanımına izin verme dezavantajı vardır.Nasıl , ama her türlü kötü şeylerin ortaya çıkmasına izin veren zımni protokolü takip etmeyin.

yineleyiciler

Bu durumda bir Iteratorbir olduğunu nasıl o etkileşim açıklaması için kısaltmadır. Bu açıklamaya uyan her şey tanım gereği an Iterator. Nasıl bilmek genel algoritmalar yazmamızı ve algoritmanın çalışması için sağlanması gereken ' Belirli bir Neyin nasıl verildiğini ' içeren kısa bir listeye sahip olmamızı sağlar . Bu liste fonksiyonu / özellikler / vb bunların uygulanması dikkate özgü alır vardır ne algoritması tarafından ele ediliyor.


6

C ++ olmadığından ihtiyaç polimorfizmi yapmak (soyut) baz sınıfları için. Bu sahip yapısal alt tiplerinin yanı sıra yalın alt tiplerinin .

Belirli yineleyiciler durumunda kafa karıştırıcı olarak, önceki standartlar std::iterator(yaklaşık)

template <class Category, class T, class Distance = std::ptrdiff_t, class Pointer = T*, class Reference = T&>
struct iterator {
    using iterator_category = Category;
    using value_type = T;
    using difference_type = Distance;
    using pointer = Pointer;
    using reference = Reference;
}

Yani sadece gerekli üye türlerinin bir sağlayıcısı olarak. Herhangi bir çalışma zamanı davranışı yoktu ve C ++ 17'de kullanımdan kaldırıldı

Sınıf şablonu bir sınıf olmadığından, bu ortak bir taban olamayacağına dikkat edin, her örnekleme diğerlerinden bağımsızdır.



5

Bunun bir nedeni, yineleyicilerin bir sınıf örneği olmak zorunda olmamasıdır. İşaretçiler, örneğin birçok durumda mükemmel şekilde yineleyicilerdir ve bunlar ilkel olduklarından hiçbir şeyden miras alamazlar.

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.