Seznam nešvarů

V níže uvedeném seznamu jsou popsány různé problémové konstrukty jazyka C++, které studenti sice s oblibou používají, ale které není vhodné používat.

Přikázání

  1. Direktivu using v hlavičkovém souboru v globálním jmenném prostoru psáti nesmíš.
  2. Inicializační sekce konstruktorů psáti budeš.
  3. Alokaci new (std::nothrow) psáti nebudeš.
  4. Copy&swap idiom všem třídám implementuješ.
  5. Typ auto zřídkakdy a s rozvahou používati budeš.
  6. Výjimky hodnotou vyhazovati budeš a (konstantní) referencí chytati budeš.
  7. Výjimky zřídkakdy a s rozvahou používati budeš.
  8. K parametrům do funkcí a metod předávaných takto se chovati budeš:
    • Základní typy hodnotou předávati budeš.
    • Třídy konstantní referencí předávati budeš, pokud je číst stačiti bude.
    • Třídy referencí předávati budeš, pokud je měnit potřeba bude.
    • Třídy hodnotou předávati budeš, pokud je zkopírovat potřeba bude.
  9. Konstanty klíčovým slovem const anotuješ.
  10. Na místo C-style-cast přetypování static_cast a dynamic_cast použiješ.

using namespace std

Nikdy nedávejte using namespace std do globálního prostoru v hlavičkovém souboru.

Špatný příklad

header.h
#include <string>
 
using namespace std;
 
struct MyClass {
    string data;
};

Správný příklad

header.h
#include <string>
 
struct MyClass {
    std::string data;
};
 
struct MyClass2 {
    using string = std::string;
    // stejne jako:
    // typedef std::string string;
    string data;
};

Inicializační sekce

Studenti rádi vynechávají inicializační sekce. V C++ je toto obecně nevhodné chování, protože samotná inicializace se (téměř) vždy děje ještě předtím, než se spustí kód konstruktoru. A to, co se děje, máte možnost ovlivnit právě v inicializační sekci.

Špatný příklad

header.h
struct MyClass {
    MyClass() {
        _data = 0;
    }
private:
    int _data;
};

Správný příklad

header.h
struct MyClass {
    MyClass() :
        _data( 0 )
    {}
private:
    int _data;
};

new (std::nothrow) ...

Pokud k tomu nemáte dobrý důvod (a to v předmětu PB161 nemáte), tak to nepoužívejte.

Špatný příklad

main.cpp
#include <new>
int main() {
    int *numbers = new (std::nothrow) int[6];
    delete[] numbers;
}

Správný příklad

main.cpp
int main() {
    int *numbers = new int[6];
    delete[] numbers;
}

Metoda at(index)

Když znáte rozsah, proč ho znovu kontrolovat při každém přístupu?

Špatný příklad

main.cpp
#include <vector>
int main() {
    std::vector< int > numbers( 6 );
    for ( int i = 0; i < numbers.size(); ++i )
        numbers.at( i ) = i;
}

Správný příklad

main.cpp
#include <vector>
int main() {
    std::vector< int > numbers( 6 );
    for ( int i = 0; i < numbers.size(); ++i )
        numbers[ i ] = i;
}

Copy&swap idiom

Proč používat něco těžkopádné a nebezpečné, když můžete použít něco jednoduché a elegantní?

Špatný příklad

header.h
struct Data {
    Data( int s ) :
        _data( new int[ s ] ),
        _size( s )
    {}
    Data( const Data &other ) :
        _data( new int[ other._size ] ),
        _size( other._size )
    {
        std::copy( other._data, other._data + _size, _data );
    }
    ~Data() {
        delete[] _data;
    }
    Data &operator=( const Data &other ) {
        if ( this == &other )
            return *this;
 
        delete[] _data;
        _size = other._size;
        _data = new int[ _size ];
        std::copy( other._data, other._data + _size, _data );
 
        return *this;
    }
    size_t size() const {
        return _size;
    }
    int &operator[]( size_t i ) {
        return _data[ i ];
    }
    int operator[]( size_t i ) const {
        return _data[ i ];
    }
private:
    int *_data;
    size_t _size;
};

Správný příklad

header.h
struct Data {
    Data( int s ) :
        _data( new int[ s ] ),
        _size( s )
    {}
    Data( const Data &other ) :
        _data( new int[ other._size ] ),
        _size( other._size )
    {
        std::copy( other._data, other._data + _size, _data );
    }
    ~Data() {
        delete[] _data;
    }
    Data &operator=( Data other ) {
        swap( other );
        return *this;
    }
    void swap( Data &other ) {
        using std::swap;
 
        swap( _data, other._data );
        swap( _size, other._size );
    }
    size_t size() const {
        return _size;
    }
    int &operator[]( size_t i ) {
        return _data[ i ];
    }
    int operator[]( size_t i ) const {
        return _data[ i ];
    }
private:
    int *_data;
    size_t _size;
};
inline void swap( Data &lhs, Data &rhs ) {
    lhs.swap( rhs );
}

auto

Kdy použít auto a kdy explicitně typ proměnné? Téměř vždy pište celý typ, pouze v ojedinělých případech lze svolit k tomu, že použijete auto namísto typu. Typicky v případech, kdy používáte iterátory datových kontejnerů.

Výjimky

Jak na výjimky? Házet hodnotou, chytat (konstantní) referencí, ovšem nikdy používat k řízení toku programu – a už vůbec ne k tomu, abyste si z funkcí mohli vracet tuhle číslo a onehdy zase řetězec. Obsloužení výjimky je totiž poměrně nákladná záležitost.

main.c
#include <stdexcept>
#include <iostream>
void foo( int param ) {
    if ( param < 0 )
        throw std::runtime_exception( "out of bounds" );
    // do things...
}
int main() {
    try {
        foo( -1 );
    } catch ( const std::exception &e ) {
        std::cerr << "exception: " << e.what() << std::endl;
        return 1;
    }
    return 0;
}
QR Code
QR Code public:pb161:doporuceni (generated for current page)