www.4FipS.com]-      
[ FipS' NEWS ]-

 ABOUT FipS =
[ START HERE ]=
[ PROFILE ]-
[ GUESTBOOK ]-
[ FORUMS ]-

 COMPUTERS=
[ CODING ]-
[ FipS' CODE ]-
[ FSHUB ENG. ]-
[ OLD STUFF ]-

 ART & CG =
[ CG ART ]-
[ ARTWORKS ]-

 PHOTO =
[ * ABOUT ]-
[ SERIES ]-
[ SHOTS ]-
[ PANORAMAS ]-
[ MACROS ]-
[ TIMELAPSE ]-

C++ Tips (Cz)

C++ --->
[ C++ Tips, rev. 2006-05   /   ]
[ C++ Collected Wisdom, rev. 2006-09 ]
[ C++ Smart Pointers, rev. 2005-01 ]
OOP / Pragmatic UML --->
[ UML Introduction, rev. 2006-06 ]
[ UML Class Diagrams, rev. 2006-06 ]

Visitor Map      
= LINKS 
-[ CODING ]
-[ C++ ]
-[ GAME DEV ]
-[ GRAPHICS ]
-[ MATH ]
-[ LIBS ]
-[ PYTHON ]
-[ MOBILE ]
-[ FREE TOOLS ]

-[ CG ART ]
-[ PHOTO ]
-[ LEARN ENG ]

-[ E FRIENDS ]
-[ SELECTED ]
-[ MISC ]

= DUMPED 
-[ 2010 ] [ 07 ]
-[ 2009 ] [ 06 ]
-[ 2008 ] [ 05 ]


C++ Tips : rev. 2005-06-18 by FipS

Na tomto miste bych rad shromazdoval ruzne postrehy a zajimavosti tykajici se kazdodenni prace s C++.

Tip #3 - "std::for_each" a funkcni adapter "std::mem_fun", 2005-06-18

V dnesni dobe si lze jen tezko predstavit rozsahlejsi aplikaci napsanou v C++ bez vyuziti kolekci STL. Kolekce pak typicky realizuji uloziste instanci nejruznejsich objektu nebo ukazatelu na instance. Pri praci s temito kolekcemi je casto nutne vyvolat nejakou operaci u vsech ulozenych instanci, coz se typicky provadi volanim prislusne clenske funkce v cyklu.

STL vsak nabizi elegantnejsi a potencialne efektivnejsi zpusob s vyuzitim algoritmu "std::for_each" a funkcniho adapteru "std::mem_fun" nebo "std::mem_fun_ref". Algoritmus "std::for_each" vola uzivatelsky definovanou funci v urcitem rozsahu iteratoru, funkcni adapter "std::mem_fun..." prevadi funkcni volani na volani clenske funkce objektu. Rozdil mezi "std::mem_fun" a "std::mem_fun_ref" spociva v tom, ze prvni varianta pradpoklada kolekci ukazatelu na instance objektu, zatimco druha kolekci vlastnich instanci. "std::mem_fun..." umoznuje volat pouze clenske funkce bez argumentu. Pomoci dalsiho funkcniho adapteru "std::bind2nd" docilime volani clenske funkce s jednim argumentem, volani s vice argumenty neni v soucasne norme STL mozne. Priklad nize demonstruje uvedenou techniku.

Pozn. Knihovna [ Boost ] nabizi alternativu k "std::mem_fun..." umoznujici volani clenskych funkci s neomezenym poctem argumentu.

#include <stdio.h>

#include <vector>
#include <algorithm> // std::for_each
#include <functional> // std::mem_fun, std::mem_fun_ref, std::bind2nd

struct STest
{
    void Print() { printf("0x%08x\n", (size_t)this); }
    void PrintValue(int nValue) { printf("nValue = %d\n", nValue); }
};

int main()
{
    // vector of object instances
    std::vector<STest> Tests;
    Tests.push_back(STest());
    Tests.push_back(STest());
    Tests.push_back(STest());

    // vector of object instance pointers
    std::vector<STest *> TestPtrs;
    TestPtrs.push_back(&Tests[0]);
    TestPtrs.push_back(&Tests[1]);
    TestPtrs.push_back(&Tests[2]);
    
    printf("std::mem_fun_ref:\n");
    
    // no argument call
    std::for_each(Tests.begin(), Tests.end(),
     std::mem_fun_ref(&STest::Print));
     
    // 1 argument call
    std::for_each(Tests.begin(), Tests.end(),
     std::bind2nd(std::mem_fun_ref(&STest::PrintValue), 10));
     
    printf("\nstd::mem_fun:\n");
    
    // no argument call
    std::for_each(TestPtrs.begin(), TestPtrs.end(),
     std::mem_fun(&STest::Print));
     
    // 1 argument call
    std::for_each(TestPtrs.begin(), TestPtrs.end(),
     std::bind2nd(std::mem_fun(&STest::PrintValue), 10));
     
    return 0;
}

// std::mem_fun_ref:
// 0x00322c70
// 0x00322c71
// 0x00322c72
// nValue = 10
// nValue = 10
// nValue = 10
//
// std::mem_fun:
// 0x00322c70
// 0x00322c71
// 0x00322c72
// nValue = 10
// nValue = 10
// nValue = 10


[ Comments here... ]

Tip #2 - Clenske reference a "operator =", 2005-01-16, rev. 2005-01-18

Ve snaze vyprodukovat robustni kod, muzeme dospet do situace, kdy se v objektech vyskytnou clenske polozky ve forme referenci (v prikladu viz. nize "m_rnValue"). Takove polozky je mozne inicializovat pouze v konstruktoru. Problem nastava, potrebujeme-li implementovat operator prirazeni "operator =", ktery je nutny napr. pokud chceme takovy objekt ukladat do kolekci v STL. Prirazeni zpusobem "m_rnValue = RHS.m_rnValue" predstavuje pouze kopirovani hodnoty, nikoliv samotne reference! (pro konstantni reference neni mozne vubec). V tomto pripade se nabizi zajimave reseni, pri kterem je explicitne vyvolan destruktor objektu a pote na puvodni adrese zkonstruovana nova instance (pomoci placement new), jak je videt nize (tuto metodu je rovnez mozne vyuzit pro zachovani konzistence mezi kopirovacim konstruktorem a operatorem prirazeni). Nutno podotknout, ze tento zpusob prinasi urcite komplikace pri dedicnosti objektu, kdy je nutne operator prirazeni vhodne implementovat u vsech potomku.

class CTest
{
 public:
 
    CTest(const int &nValue): m_rnValue(nValue) {}
    CTest(const CTest &RHS) : m_rnValue(RHS.m_rnValue) {}
    CTest & operator = (const CTest &RHS)
    {
        if(this != &RHS)
        {
            // wrong !!! (for 'const' not possible at all)
            // m_rnValue = RHS.m_rnValue;
            
            this->CTest::~CTest();
            new (this) CTest(RHS);
        }
        return *this;
    }
 
 private:
 
    CTest();
    const int &m_rnValue;
};

int main()
{
    int nValue1 = 1;
    int nValue2 = 2;

    CTest Test1(nValue1); // Test1.m_rnValue = 1
    CTest Test2(nValue2); // Test2.m_rnValue = 2
    
    Test2 = Test1; // call 'operator ='; Test(1/2).m_rnValue = 1
    
    nValue1 = 3; // Test(1/2).m_rnValue = 3

    return 0;
}


[ Comments here... ]

Tip #1 - Uskali bitovych posunu, 2005-01-16

Kazdy jiste nekdy provadel nejruznejsi bitove operace, nejcasteji zrejme za ucelem prace s tzv. "flagy", kdy jednotlive bity nejakeho ciselneho typu predstavuji binarni informace ano/ne, nebo jinou binarni aritmetiku, pri ktere bylo nutne vyuzivat operatory bitoveho posunu "<<" nebo ">>".

Priklad nize naznacuje chovani techto operatoru. Operator bitoveho posunu vlevo "<<" vykazuje transparentni chovani: vsechny bity jsou posunuty vlevo a nove vznikle misto je doplneno, prislusnym poctem 0.

U operatoru bitoveho posunu vpravo ">>" je ovsem situace slozitejsi, coz by na prvni pohled nemuselo byt zrejme! Zalezi totiz na tom, zda-li je levy operand typ znamenkovy - signed nebo bezznamenkovy - unsigned. V pripade znamenkoveho typu je totiz znamenko reprezentovano nejvyssim bitem, proto je pri bitovem posunu vpravo (ve snaze zachovat znamenko celeho ciselneho typu) pouzit znamenkovy bit k vyplneni vznikle mezery.

Z techto duvodu se zda byt pro bitove operace vhodnejsi vyuzivat bezznamenkove - unsigned typy...

template<typename T>
void fsPrintBits(T t)
{
    for(int i = 8 * sizeof T - 1; i >= 0; --i)
        putchar((t & (1 << i)) ? '1' : '0');
        
    putchar('\n');
}

int main()
{
    unsigned char nUnsigned = 0xF0;
    char nSigned = 0xF0;
    
    fsPrintBits(nUnsigned);         // 11110000
    fsPrintBits(nSigned);           // 11110000
    
    unsigned char nUnsignedLeft = nUnsigned << 2;
    char nSignedLeft = nSigned << 2;
    
    fsPrintBits(nUnsignedLeft);     // 11000000
    fsPrintBits(nSignedLeft);       // 11000000
    
    unsigned char nUnsignedRight = nUnsigned >> 2;
    char nSignedRight = nSigned >> 2;
        
    fsPrintBits(nUnsignedRight);    // 00111100 !!!
    fsPrintBits(nSignedRight);      // 11111100 !!!
        
    return 0;
}


[ Comments here... ]
Dec, 2004 - C++ Tips (CZ) - (c) Filip STOKLAS (FipS) - [ www ][ Guest Book ]