Sök…


Introduktion

Ett loop-uttalande kör en grupp uttalanden upprepade gånger tills ett villkor är uppfyllt. Det finns 3 typer av primitiva öglor i C ++: för, medan och gör ... medan.

Syntax

  • medan ( villkor ) uttalande ;
  • göra uttalande medan ( uttryck );
  • för ( för-init-uttalande ; villkor ; uttryck ) uttalande ;
  • för ( för-intervall-deklaration : för-intervall-initialisering ) uttalande ;
  • ha sönder ;
  • Fortsätta ;

Anmärkningar

algorithm är generellt att föredra framför handskrivna slingor.

Om du vill ha något som en algoritm redan gör (eller något mycket liknande) är algoritmsamtalet tydligare, ofta mer effektivt och mindre fel benägna.

Om du behöver en slinga som gör något ganska enkelt (men som kräver en förvirrande fläns av bindemedel och adaptrar om du använder en algoritm), skriv bara slingan.

Range-baserad för

C ++ 11

for slingor kan användas för att iterera över elementen i ett iteratorbaserat intervall, utan att använda ett numeriskt index eller direkt komma åt iteratorerna:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(auto val: v)
{
    std::cout << val << " ";
}

std::cout << std::endl;

Detta upprepas över varje element i v , med val får värdet på det aktuella elementet. Följande uttalande:

for (for-range-declaration : for-range-initializer ) statement

är ekvivalent med:

{
    auto&& __range = for-range-initializer;
    auto __begin = begin-expr, __end = end-expr;
    for (; __begin != __end; ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}
C ++ 17
{
    auto&& __range = for-range-initializer;
    auto __begin = begin-expr;
    auto __end = end-expr; // end is allowed to be a different type than begin in C++17
    for (; __begin != __end; ++__begin) {
        for-range-declaration = *__begin;
        statement
    }
}

Denna förändring infördes för det planerade stödet för Ranges TS i C ++ 20.

I detta fall motsvarar vår slinga:

{
    auto&& __range = v;
    auto __begin = v.begin(), __end = v.end();
    for (; __begin != __end; ++__begin) {
        auto val = *__begin;
        std::cout << val << " ";
    }
}

Observera att auto val deklarerar en värdetyp, som kommer att vara en kopia av ett värde lagrat i intervallet (vi kopierar initiera det från iteratorn när vi går). Om värdena som lagras i intervallet är dyra att kopiera, kanske du vill använda const auto &val . Du behöver inte heller använda auto ; Du kan använda ett lämpligt typnamn, så länge det implicit är konvertibelt från områdets värdetyp.

Om du behöver åtkomst till iteratorn, kan inte-baserad för hjälpa dig (inte utan någon ansträngning, åtminstone).

Om du vill hänvisa till det kan du göra det:

vector<float> v = {0.4f, 12.5f, 16.234f};

for(float &val: v)
{
    std::cout << val << " ";
}

Du kan iterera på const referens om du har const container:

const vector<float> v = {0.4f, 12.5f, 16.234f};

for(const float &val: v)
{
    std::cout << val << " ";
}

Man skulle använda vidarebefordringsreferenser när sekvens iteratorn returnerar ett proxyobjekt och du måste använda det objektet på ett icke- const sätt. Obs! Det kommer troligen att förvirra läsarna av din kod.

vector<bool> v(10);

for(auto&& val: v)
{
    val = true;
}

Typen "intervall" som tillhandahålls för intervallbaserad for kan vara en av följande:

  • Språkuppsättningar:

    float arr[] = {0.4f, 12.5f, 16.234f};
    
    for(auto val: arr)
    {
        std::cout << val << " ";
    }
    

    Observera att tilldelning av en dynamisk grupp inte räknas:

    float *arr = new float[3]{0.4f, 12.5f, 16.234f};
    
    for(auto val: arr) //Compile error.
    {
        std::cout << val << " ";
    }
    
  • Alla typer som har medlemsfunktioner begin() och end() , som returnerar iteratorer till elementen i typen. Standardbibliotekets behållare är kvalificerade, men användardefinierade typer kan också användas:

    struct Rng
    {
        float arr[3];
    
        // pointers are iterators
        const float* begin() const {return &arr[0];}
        const float* end() const   {return &arr[3];}
        float* begin() {return &arr[0];}
        float* end()   {return &arr[3];}
    };
    
    int main()
    {
        Rng rng = {{0.4f, 12.5f, 16.234f}};
    
        for(auto val: rng)
        {
            std::cout << val << " ";
        }
    }
    
  • Varje typ som har icke-medlemmar begin(type) och end(type) funktioner som kan hittas via argumentberoende uppslag, baserat på type . Detta är användbart för att skapa en intervalltyp utan att behöva ändra själva klasstypen:

    namespace Mine
    {
        struct Rng {float arr[3];};
    
        // pointers are iterators
        const float* begin(const Rng &rng) {return &rng.arr[0];}
        const float* end(const Rng &rng) {return &rng.arr[3];}
        float* begin(Rng &rng) {return &rng.arr[0];}
        float* end(Rng &rng) {return &rng.arr[3];}
    }
    
    int main()
    {
        Mine::Rng rng = {{0.4f, 12.5f, 16.234f}};
    
        for(auto val: rng)
        {
            std::cout << val << " ";
        }
    }
    

För slinga

A for loop exekverar uttalanden i loop body , medan slingan condition är sant. Innan initialization statement exekveras en gång. Efter varje cykel körs iteration execution .

En for slinga definieras enligt följande:

for (/*initialization statement*/; /*condition*/; /*iteration execution*/)
{
    // body of the loop
}

Förklaring av uttalanden om platshållare:

  • initialization statement : Detta uttalande körs endast en gång i början av for loopen. Du kan ange en deklaration av flera variabler av en typ, till exempel int i = 0, a = 2, b = 3 . Dessa variabler är endast giltiga inom loopens omfattning. Variabler definierade före slingan med samma namn döljs under körningen av slingan.
  • condition : Detta uttalande får utvärderas inför varje exekverings slingan och avbryter loopen om det beräknas till false .
  • iteration execution : Detta påstående får exekveras efter slingan, inför nästa tillstånd utvärdering, såvida inte den for slingan avbryts i kroppen (genom break , goto , return eller ett undantag som kastas). Du kan ange flera uttalanden i iteration execution , till exempel a++, b+=10, c=b+a .

Den grova ekvivalent av en for slinga, skrivas om som en while slinga är:

/*initialization*/
while (/*condition*/)
{
    // body of the loop; using 'continue' will skip to increment part below
    /*iteration execution*/
}

Det vanligaste fallet för att använda en for loop är att köra uttalanden ett visst antal gånger. Tänk till exempel på följande:

for(int i = 0; i < 10; i++) {
    std::cout << i << std::endl;
}

En giltig slinga är också:

for(int a = 0, b = 10, c = 20; (a+b+c < 100); c--, b++, a+=c) {
    std::cout << a << " " << b << " " << c << std::endl; 
}

Ett exempel på att dölja deklarerade variabler före en slinga är:

int i = 99; //i = 99
for(int i = 0; i < 10; i++) { //we declare a new variable i
    //some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 99

Men om du vill använda den redan deklarerade variabeln och inte dölja den, utelämna deklarationsdelen:

int i = 99; //i = 99
for(i = 0; i < 10; i++) { //we are using already declared variable i
    //some operations, the value of i ranges from 0 to 9 during loop execution
}
//after the loop is executed, we can access i with value of 10

Anmärkningar:

  • Initierings- och steguppgörelserna kan utföra operationer som inte är relaterade till tillståndsdeklarationen, eller ingenting alls - om du vill göra det. Men av läsbarhetsskäl är det bästa sättet att bara utföra operationer som är direkt relevanta för slingan.
  • En variabel som deklareras i initialiseringsförklaringen är synlig endast inom ramen för for loopen och släpps vid avslutningen av slingan.
  • Glöm inte att variabeln som deklarerades i initialization statement kan ändras under slingan, liksom variabeln kontrollerad i condition .

Exempel på en slinga som räknas från 0 till 10:

for (int counter = 0; counter <= 10; ++counter)
{
    std::cout << counter << '\n';
}
// counter is not accessible here (had value 11 at the end)

Förklaring av kodfragmenten:

  • int counter = 0 initialiserar den variabla counter till 0. (Denna variabel kan endast användas insidan av for slingan.)
  • counter <= 10 är ett booleskt tillstånd som kontrollerar om counter är mindre än eller lika med 10. Om det är true , kör slingan. Om den är false slutar slingan.
  • ++counter är en stegvis operation som ökar värdet på counter med 1 före nästa tillståndskontroll.

Genom att lämna alla uttalanden tomma kan du skapa en oändlig slinga:

// infinite loop
for (;;)
    std::cout << "Never ending!\n";

Ekvivalent med while slingan för ovanstående är:

// infinite loop
while (true)
    std::cout << "Never ending!\n";

Emellertid kan en oändlig slinga fortfarande lämnas genom att använda uttalanden break , goto eller return eller genom att kasta ett undantag.

Nästa vanliga exempel på att iterera över alla element från en STL-samling (t.ex. en vector ) utan att använda <algorithm> -huvudet är:

std::vector<std::string> names = {"Albert Einstein", "Stephen Hawking", "Michael Ellis"};
for(std::vector<std::string>::iterator it = names.begin(); it != names.end(); ++it) {
    std::cout << *it << std::endl;
}

Medan slinga

A while loop exekverar uttalanden tills de givna villkoret utvärderas till false . Detta kontrolluttalande används när det inte är känt i förväg hur många gånger ett kodblock ska utföras.

Till exempel, för att skriva ut alla siffror från 0 till 9, kan följande kod användas:

int i = 0;
while (i < 10)
{
    std::cout << i << " ";
    ++i; // Increment counter
}
std::cout << std::endl; // End of line; "0 1 2 3 4 5 6 7 8 9" is printed to the console
C ++ 17

Observera att sedan C ++ 17 kan de första två påståendena kombineras

while (int i = 0; i < 10)
//... The rest is the same

För att skapa en oändlig slinga kan följande konstruktion användas:

while (true)
{
    // Do something forever (however, you can exit the loop by calling 'break'
}

Det finns en annan variant av while slingor, nämligen do...while konstruera. Se exempel på do-while-loop för mer information.

Förklaring av variabler i förhållanden

I tillståndet for och while slingor är det också tillåtet att förklara ett objekt. Detta objekt kommer att anses vara i omfattning till slutet av slingan och kommer att fortsätta genom varje iteration av slingan:

for (int i = 0; i < 5; ++i) {
    do_something(i);
}
// i is no longer in scope.

for (auto& a : some_container) {
    a.do_something();
}
// a is no longer in scope.

while(std::shared_ptr<Object> p = get_object()) {
   p->do_something();
}
// p is no longer in scope.

Det är emellertid inte tillåtet att göra samma sak med en do...while loop; istället, förklara variabeln innan slingan, och (valfritt) bifoga både variabeln och slingan inom ett lokalt omfång om du vill att variabeln ska gå ut ur räckvidden efter att slingan är slut:

//This doesn't compile
do {
    s = do_something();
} while (short s > 0);

// Good
short s;
do {
    s = do_something();
} while (s > 0);

Detta beror på att uttalandets del av en do...while slingan (slingans kropp) utvärderas innan uttrycksdelen ( while ) nås, och alltså kommer alla deklarationer i uttrycket inte att synas under den första iterationen av slinga.

Do-while-loop

En do-while- loop liknar en while- loop, förutom att villkoret kontrolleras i slutet av varje cykel, inte i början. Därför garanteras slingan att köras minst en gång.

Följande kod kommer att skriva ut 0 , eftersom villkoret kommer att utvärderas till false i slutet av den första iterationen:

int i =0;
do
{
    std::cout << i;
    ++i; // Increment counter
}
while (i < 0);
std::cout << std::endl; // End of line; 0 is printed to the console

Obs: Glöm inte semikolon i slutet av while(condition); , som behövs i do-while- konstruktionen.

Till skillnad från do-while- slingan kommer följande inte att skriva ut något, eftersom villkoret utvärderas till false i början av den första iterationen:

int i =0;
while (i < 0)
{
    std::cout << i;
    ++i; // Increment counter
}    
std::cout << std::endl; // End of line; nothing is printed to the console

Obs: A while-slinga kan avslutas utan att villkoret blir falskt genom att använda en break , goto eller return uttalande.

int i = 0;
do
{
    std::cout << i;
    ++i; // Increment counter
    if (i > 5) 
    {
        break;
    }
}
while (true);
std::cout << std::endl; // End of line; 0 1 2 3 4 5 is printed to the console

En trivial do-while- slinga används också ibland för att skriva makroer som kräver sitt eget räckvidd (i vilket fall släpande semikolon utelämnas från makrodefinitionen och som måste tillhandahållas av användaren):

#define BAD_MACRO(x) f1(x); f2(x); f3(x);

// Only the call to f1 is protected by the condition here
if (cond) BAD_MACRO(var);

#define GOOD_MACRO(x) do { f1(x); f2(x); f3(x); } while(0)

// All calls are protected here
if (cond) GOOD_MACRO(var);

Loop Control-uttalanden: Bryt och fortsätt

Loop-kontrollsatser används för att ändra exekveringsflödet från dess normala sekvens. När exekveringen lämnar ett omfattning förstörs alla automatiska objekt som skapades i det omfånget. break och continue är slingkontrolluttalanden.

Den break uttalande avslutar en loop utan ytterligare behandling.

for (int i = 0; i < 10; i++)
{
    if (i == 4)
        break; // this will immediately exit our loop
    std::cout << i << '\n';
}

Ovanstående kod kommer att skriva ut:

1
2
3

Den continue uttalandet inte omedelbart avsluta slingan, utan hoppar resten av slingan och går till toppen av slingan (inklusive kontroll av tillstånd).

for (int i = 0; i < 6; i++)
{
    if (i % 2 == 0) // evaluates to true if i is even
        continue; // this will immediately go back to the start of the loop
    /* the next line will only be reached if the above "continue" statement 
       does not execute  */
    std::cout << i << " is an odd number\n";
}

Ovanstående kod kommer att skriva ut:

1 is an odd number
3 is an odd number
5 is an odd number

Eftersom sådana kontrollflödesförändringar ibland är svåra för människor att lätt förstå, används break och continue sparsamt. Mer enkel implementering är vanligtvis lättare att läsa och förstå. Till exempel kan den första for slingan med break ovan skrivas om som:

for (int i = 0; i < 4; i++)
{
    std::cout << i << '\n';
}

Det andra exemplet med continue kan skrivas om som:

for (int i = 0; i < 6; i++)
{
    if (i % 2 != 0) {
        std::cout << i << " is an odd number\n";
    }
}

Räckvidd för över ett underområde

Med hjälp av områdebasslingor kan du slinga över en underdel av en given behållare eller annat intervall genom att generera ett proxyobjekt som kvalificerar sig för områdebaserade för slingor.

template<class Iterator, class Sentinel=Iterator>
struct range_t {
  Iterator b;
  Sentinel e;
  Iterator begin() const { return b; }
  Sentinel end() const { return e; }
  bool empty() const { return begin()==end(); }
  range_t without_front( std::size_t count=1 ) const {
    if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
      count = (std::min)(std::size_t(std::distance(b,e)), count);
    }
    return {std::next(b, count), e};
  }
  range_t without_back( std::size_t count=1 ) const {
    if (std::is_same< std::random_access_iterator_tag, typename std::iterator_traits<Iterator>::iterator_category >{} ) {
      count = (std::min)(std::size_t(std::distance(b,e)), count);
    }
    return {b, std::prev(e, count)};
  }
};

template<class Iterator, class Sentinel>
range_t<Iterator, Sentinel> range( Iterator b, Sentinal e ) {
  return {b,e};
}
template<class Iterable>
auto range( Iterable& r ) {
  using std::begin; using std::end;
  return range(begin(r),end(r));
}

template<class C>
auto except_first( C& c ) {
  auto r = range(c);
  if (r.empty()) return r;
  return r.without_front();
}

nu kan vi göra:

std::vector<int> v = {1,2,3,4};

for (auto i : except_first(v))
  std::cout << i << '\n';

och skriva ut

2
3
4

Var medveten om att mellanliggande objekt som genereras i for(:range_expression) av for loopen har löpt ut när tiden for loopen startar.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow