Více než často vídávám, že se do hlavičkových souborů dává tzv. using direktiva ve tvaru using namespace xxx;
. Nejčastější je using namespace std;
. V následujícím příspěvku se dozvíte, proč je to špatný nápad a důvody, proč to nedělat.
Co je to ta using direktiva?
Direktiva using
slouží k přímému zpřístupnění identifikátorů v daném prostoru jmen. Zapisuje se ve tvaru using namespace xxx;
, kde xxx
je prostor jmen, jež chcete přímo zpřístupnit. Troufám si tvrdit, že mnoho programátorů v C++ chápe tuto direktivu tak, že je to jakási magická formule, která jim zjednoduší práci, a to tak, že např. v případě jmenného prostoru jmen std
nemusí psát pět znaků navíc (jedná se o znaky std::
).
Příklad použití. Mějme následující kód:
#include <string> std::string hello = "Hello World!"; std::cout << hello << std::endl;
Použitím zmiňované direktivy using
lze kód zkrátit:
#include <string> using namespace std; string hello = "Hello World!"; cout << hello << endl;
V čem je problém při použití v hlavičkových souborech?
Může to uživatelům takových hlavičkových souborů způsobit bolesti hlavy. Buď se jim jinak přeložitelný kód odmítne přeložit či se přeloží, ale bude fungovat jinak, než bez použití onoho hlavičkového souboru.
Podívejme se nyní na příklad druhého problému, který je zajímavější. Mějme následující úryvek kódu:
#include <iostream> class MyInt { public: MyInt(int value): value(value) {} // ... operator int *() { return &value; } // ... private: int value; }; void swap(int *a, int *b) { std::cout << "swap(" << *a << ", " << *b << ")\n"; int c = *a; *a = *b; *b = c; } int main() { MyInt mi1(1), mi2(2); swap(mi1, mi2); }
Příklad je to vykonstruovaný, ale bude dobře ilustrovat problém. Pokud kód přeložíme a spustíme, vypíše swap(1, 2)
. Dejme nyní tomu, že potřebujeme nějakou funkcionalitu z knihovny, která má rozhraní v hlavičkovém souboru library.h
:
#ifndef LIBRARY_H #define LIBRARY_H using namespace std; // ... (samotná funkcionalita) #endif
Proto dáme na začátek našeho souboru toto:
#include "library.h"
Pokud nyní kód přeložíme a spustíme, nic se nevypíše. Důvod je ten, že místo zavolání naší funkce swap()
se zavolá ta ze standardní knihovny. Překladač ji totiž vyhodnotí jako vhodnější, protože při jejím použití není třeba konverze na int *
, což je potřeba u naší varianty. Kdyby v onom hlavičkovém souboru nebyla použita using
direktiva, toto by se nestalo. Co je nejhorší, tak je, že se chování našeho programu změnilo jen vložením hlavičkového souboru, který s naším kódem neměl co do činění. To je velmi nebezpečné a mnohdy těžko odladitelné, protože vás obvykle nenapadne, že by mohl být problém v tomto.
Jak to tedy řešit?
V hlavičkových souborech prefixujte identifikátory ze standardní knihovny pomocí std::
. Tedy místo
using namespace std; // ... string s;
pište
// ... std::string s;
Těch pět znaků navíc vás nezabije. Vyhnete se tak potenciálním problémům, které jsem zmiňoval výše. V opačném případě svým kódem říkáte, že je vám jedno, jaké problémy vzniknou uživatelům vašich hlavičkových souborů, a že důležitější je, že se vám snadněji píše kód...
Závěrem bych rád upozornil, že použití tzv. deklarace using
ve tvaru using std::string;
tento problém neřeší. Skutečně, když si někdo nadefinuje vlastní string
, tak tím, že v hlavičkovém souboru budete mít using std::string;
, mu přivodíte podobné problémy, jako při použití using
direktivy.
Další čtení:
- C++ FAQ: Should I use using namespace std in my code?
- stackoverflow.com - why using directive in C++ is not encouraged?
- stackoverflow.com - Is it wrong to use C++ 'using' keyword in a header file?
- stackoverflow.com - Why is including “using namespace” into a header file a bad idea in C++?
- stackoverflow.com - Why is std:: used by experienced coders rather than using namespace std;?
- stackoverflow.com - distance calculation error in c++
- stackoverflow.com - why swap() can work well when I don't call it with two pointer?
- mariusbancila.ro - Avoid using directives in header files
- gotw.ca - Migrating to Namespaces