2010-10-29 7 views
8

Nasıl C++ bir özyinelemeli işlevi içinde mevcut derinliği bulabilirsiniz derinliğini bulabilirim? yani, fonksiyonun her çağrılmasında, seviye takip etmek ve bu sayıyı parametre olarak iletmek için bir parametre kullanılmadan fonksiyonun kaç defa çağrıldığını bilmek mümkün mü?nasıl önceki düzeyde geçmeden C bir özyinelemeli fonksiyonu ++

Örneğin benim özyinelemeli fonksiyon şöyle görünür: Eğer iş parçacığı güvenliği hakkında önemli değilse

DoSomething(int level) 
{ 
    print level; 
    if (level > 10) 
    return; 
    DoSomething(++level); 
} 

main 
{ 
    DoSomething(0); 
} 
+1

[bu tartışmaya] bir göz atmak isteyebilirsiniz (http://stackoverflow.com/questions/582673/is-there-a-cheaper-way-to-find-the-depth-of-the- geçirici yığını-daha-kullanan-BackTrace). Sonuç olarak, backtrace'i elde etmek için bir derleyici/platforma özel bir yol olabileceği ve bunu kullanabileceği ... ama açıkça derleyici/platforma özel ve muhtemelen inline gibi şeylere duyarlı. Her durumda, bir göz atmaya değer olabilir. –

cevap

11

Bina JoshD tarafından verilen: özyinelemeli fonksiyon tamamlandı, ancak yine de ya yinelenen maksimum derinliği izler sonra

void recursive() 
{ 
    static int calls = 0; 
    static int max_calls = 0; 
    calls++; 
    if (calls > max_calls) 
     max_calls = calls; 

    recursive(); 

    calls--; 
} 

Bu sayacı sıfırlar.

Yakında sonra silinecek, hızlı bir test için bir şey ama böyle statik değişkenleri kullanmak ister. Bunu sürekli olarak izlemeniz gerekiyorsa daha iyi yöntemler vardır.

3

Sen, yerel bir statik değişkeni kullanabilirsiniz.

bu sadece uygun bir alınma sayısı size özyinelemeli rutin ilk kez çalıştırdığınızda verecektir rağmen. Daha iyi bir teknik, bir iç statik değişken içeren bir RAII koruyucu tipi sınıf olurdu. Yinelemeli rutinin başlangıcında, koruma sınıfını inşa edin. Yapıcı içsel statik değişkeni artıracak ve yıkıcı bunu azaltacaktır. Bu şekilde, yeni bir yığın-çerçeve oluşturduğunuzda, sayaç birer birer artar ve her bir istif çerçevesinden döndüğünüzde, sayaç birer birer azalır.

struct recursion_guard 
{ 
    recursion_guard() { ++counter; } 

    ~recursion_guard() { --counter; } 

    static int counter; 
}; 

int recursion_guard::counter = 0; 

void recurse(int x) 
{ 
    recursion_guard rg; 
    if (x > 10) return; 
    recurse(x + 1); 
} 

int main() 
{ 
    recurse(0); 
    recurse(0); 
} 

Ancak, bunun hala vida dişi olmadığını unutmayın. Eğer iş parçacığı güvenliği gerekiyorsa, boost::thread_specific_ptr kullanarak veya C++ 0x parçacığı yerel tesisler, ya bir iş parçacığı yerel depolama değişkenle statik depolama değişkeni yerini alabilir. Yeni bir kaynak görüşmesi başlattığınızda

Elbette
void recursive() 
{ 
static int calls = 0; 
calls++; 
recursive(); 
} 

, bu sayım tutacak Fonksiyondaki statik değişkenini kullanabilirsiniz

+0

Böyle bir şey düşünüyordum, ama sen beni dövdün. Muhafız sınıfının hala iş parçacığı güvenli olmamasının bir problemi olduğuna dikkat etmelisiniz. –

+0

@Mark Ransom, doğru. Bir fool-prova iyileştirme, statik-depolama değişkenini, 'boost :: thread_specific_ptr' veya C++ 0x iş parçacığı yerel olanaklarını kullanarak bir iş parçacığı yerel depolama değişkeni ile değiştirmektir. –

5

... .... zaten Yanıta

+0

Evet, sorun bu. Çağrıları nasıl sıfırlayabilirim? – Arizona1911

+1

Bu ayrıca tekrar giriş veya iplik güvenli olmaz. –

0

bağımsız değişkenleri ve (muhtemelen) fonksiyonu içeren edebilen yeni bir nesne (tipik olarak bir şablon) bir örneği değişken dönüştürmek level. Daha sonra yineleme akümülatör arayüzünü tekrar kullanabilirsiniz.

0

Ayrıca derinliğini oturum açmak için bir global değişken kullanmayı deneyebilirsiniz.

+1

Global, daha gevşek bir kapsamı olan bir statiktir. –

+1

Statik bir global olduğunu söyleyebilirim <. < – Blindy

1

Derleme zamanında belirlenebiliyorsa, seviyeyi şablon parametresi olarak da iletebilirsiniz. Bir işlev nesnesini de kullanabilirsiniz. Bu, uzak ve en iyi seçeneğidir - daha az güçlük ve mümkün olduğunda statik değişkenlerden kaçınılmalıdır. İsterseniz

struct DoSomething { 
    DoSomething() { 
     calls = 0; 
    } 
    void operator()() { 
     std::cout << calls; 
     calls++; 
     if (calls < 10) 
      return operator()(); 
     return; 
    } 
    int calls; 
}; 

int main() { 
    DoSomething()(); // note the double(). 
    std::cin.get(); 
} 
2

o kadar olmak yeniden katılımcıya ve evreli, neden olmasın:

void rec(int &level) // reference to your level var 
{ 
    // do work 

    rec(++level); // go down one level 
} 

main() 
{ 
    //and you call it like 
    int level=0; 
    rec(level); 

    cout<<level<<" levels."<<endl; 
} 

pisliği parçacığı için hiçbir statik/küresel değişkenler ve farklı farklı değişkenler kullanabilirsiniz yeniden girinti sorunları için özyinelemeli zincirler.

+0

Bunu beğendim – Fihop

+0

Evet Normal yığın tabanlı bir değişken yapacağı zaman global değişkenler hakkında bir şey var (ki bu statik bir çeşittir). – Blindy

İlgili konular