Jste zde

Zajímavosti z C++: Proč nedávat `using namespace xxx;` do hlavičkových souborů

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í:

Přidat komentář