Obsah
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í
- Direktivu
using
v hlavičkovém souboru v globálním jmenném prostoru psáti nesmíš. - Inicializační sekce konstruktorů psáti budeš.
- Alokaci
new (std::nothrow)
psáti nebudeš. - Copy&swap idiom všem třídám implementuješ.
- Typ
auto
zřídkakdy a s rozvahou používati budeš. - Výjimky hodnotou vyhazovati budeš a (konstantní) referencí chytati budeš.
- Výjimky zřídkakdy a s rozvahou používati budeš.
- 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.
- Konstanty klíčovým slovem
const
anotuješ. - Na místo C-style-cast přetypování
static_cast
adynamic_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; }