"Menggunakan namespace" di header c ++

119

Di semua kursus c ++ kami, semua pengajar selalu meletakkan using namespace std;tepat setelah #includes di .hfile mereka . Bagi saya ini menjadi berbahaya sejak saat itu dengan memasukkan header itu ke dalam program lain, saya akan mendapatkan namespace yang diimpor ke program saya, mungkin tanpa disadari, disengaja atau diinginkan (penyertaan header bisa sangat bersarang).

Jadi pertanyaan saya adalah dua kali lipat: Apakah saya benar yang using namespacetidak boleh digunakan dalam file header, dan / atau adakah cara untuk membatalkannya, seperti:

//header.h
using namespace std {
.
.
.
}

Satu pertanyaan lagi di sepanjang baris yang sama: Haruskah file header #includesemua header yang dibutuhkan .cppfile yang sesuai, hanya yang diperlukan untuk definisi header dan membiarkan .cppfile #includesisanya, atau tidak ada dan menyatakan semua yang dibutuhkannya sebagai extern?
Alasan di balik pertanyaan tersebut sama seperti di atas: Saya tidak ingin kejutan ketika menyertakan .hfile.

Juga, jika saya benar, apakah ini kesalahan umum? Maksud saya dalam pemrograman dunia nyata dan dalam proyek "nyata" di luar sana.

Terima kasih.

Baruch
sumber
3
Sebagai catatan tambahan, jika Anda mendapatkan nama yang bertabrakan karena using namespacepernyataan maka Anda dapat menggunakan nama yang memenuhi syarat untuk menyelesaikan masalah.
Marius Bancila

Jawaban:

115

Anda sebaiknya TIDAK menggunakan using namespaceheader karena alasan yang Anda katakan, bahwa hal itu dapat secara tidak terduga mengubah arti kode di file lain yang menyertakan header itu. Tidak ada cara untuk membatalkan using namespaceyang merupakan alasan lain mengapa itu sangat berbahaya. Saya biasanya hanya menggunakan grepatau sejenisnya untuk memastikan bahwa using namespacetidak disebutkan di header daripada mencoba sesuatu yang lebih rumit. Mungkin pemeriksa kode statis menandai ini juga.

Header harus menyertakan hanya header yang perlu dikompilasi. Cara mudah untuk menerapkannya adalah dengan selalu menyertakan header setiap file sumber sebagai hal pertama, sebelum header lainnya. Kemudian file sumber akan gagal untuk dikompilasi jika header tidak berdiri sendiri. Dalam beberapa kasus, misalnya merujuk ke kelas detail implementasi dalam perpustakaan, Anda dapat menggunakan deklarasi maju daripada #includekarena Anda memiliki kontrol penuh atas definisi kelas yang dideklarasikan maju tersebut.

Saya tidak yakin saya akan menyebutnya biasa, tetapi pasti muncul sesekali, biasanya ditulis oleh programmer baru yang tidak menyadari konsekuensi negatifnya. Biasanya hanya sedikit pendidikan tentang risiko yang menangani masalah apa pun karena relatif mudah untuk diperbaiki.

Mark B
sumber
2
apakah kita bebas menggunakan usingpernyataan dalam .cppfile kita ? yang 3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterators adalah kematian ke ujung jari.
Christopher
1
dan bagaimana kita merampingkan templatefungsi - yang seharusnya ada di header? typedefs?
Christopher
1
@donlan, sepertinya Anda tidak mendapat respon cukup lama ... Ya, Anda dapat menggunakan usingpernyataan di dalam .cppfile tanpa banyak perhatian karena cakupan akan terbatas hanya pada file itu, tetapi jangan pernah melakukannya sebelum #includepernyataan. Adapun fungsi template yang didefinisikan dalam header, sayangnya saya tidak tahu solusi yang baik selain hanya menulis namespace ... Mungkin Anda dapat meletakkan usingdeklarasi dalam ruang lingkup terpisah { /* using statement in between brackets */ }, yang setidaknya akan mencegahnya keluar dari file saat ini .
tjwrona1992
26

Butir 59 dalam Sutter dan Alexandrescu's "C ++ Coding Standards: 101 Rules, Guidelines, and Best Practices" :

59. Jangan menulis penggunaan namespace di file header atau sebelum #include.

Namespace usingadalah untuk kenyamanan Anda, bukan untuk Anda lakukan pada orang lain: Jangan pernah menulis usingdeklarasi atau usingarahan sebelum #includearahan.

Akibat yang wajar: Dalam file header, jangan menulis usingperintah atau usingdeklarasi tingkat namespace ; sebagai gantinya, secara eksplisit memenuhi syarat namespace semua nama.

File header adalah tamu di satu atau beberapa file sumber. File header yang menyertakan usingarahan dan deklarasi membawa teman-temannya yang gaduh juga.

Sebuah using deklarasi membawa satu teman. Sebuah using direktif membawa semua teman di namespace. Penggunaan guru Anda using namespace std;adalah menggunakan direktif.

Lebih serius lagi, kami memiliki ruang nama untuk menghindari benturan nama. File header dimaksudkan untuk menyediakan antarmuka. Sebagian besar header bersifat agnostik terhadap kode apa yang mungkin menyertakannya, sekarang atau di masa mendatang. Menambahkan usingpernyataan untuk kenyamanan internal dalam tajuk memasukkan nama-nama yang mudah digunakan itu pada semua klien potensial tajuk itu. Itu bisa menyebabkan bentrokan nama. Dan itu tidak sopan.

Andy Thomas
sumber
12

Anda harus berhati-hati saat menyertakan header di dalam header. Dalam proyek besar, ini dapat membuat rantai ketergantungan yang sangat kusut yang memicu pembangunan kembali yang lebih besar / lebih lama dari yang sebenarnya diperlukan. Lihat artikel ini dan tindak lanjutnya untuk mempelajari lebih lanjut tentang pentingnya struktur fisik yang baik dalam proyek C ++.

Anda sebaiknya hanya menyertakan header di dalam header saat benar-benar dibutuhkan (kapan pun definisi lengkap kelas diperlukan), dan gunakan deklarasi maju di mana pun Anda bisa (saat kelas yang dibutuhkan adalah pointer atau referensi).

Sedangkan untuk ruang nama, saya cenderung menggunakan cakupan ruang nama eksplisit di file header saya, dan hanya meletakkan a using namespacedi file cpp saya.

Mike O'Connor
sumber
1
bagaimana Anda menyederhanakan templatedeklarasi fungsi? yang harus terjadi di header, bukan?
Christopher
6

Lihat standar pengkodean Goddard Space Flight Center (untuk C dan C ++). Itu ternyata sedikit lebih sulit dari sebelumnya - lihat jawaban terbaru untuk pertanyaan SO:

Standar pengkodean GSFC C ++ mengatakan:

§3.3.7 Setiap file header adalah #includefile yang perlu dikompilasi, daripada memaksa pengguna ke #includefile yang dibutuhkan. #includesharus dibatasi pada apa yang dibutuhkan header; lainnya #includesharus ditempatkan di file sumber.

Pertanyaan pertama dari pertanyaan referensi silang sekarang menyertakan kutipan dari standar pengkodean GSFC C, dan alasannya, tetapi substansinya akhirnya sama.

Jonathan Leffler
sumber
5

Anda benar bahwa using namespacesundulan berbahaya. Saya tidak tahu bagaimana cara membatalkannya. Sangat mudah untuk mendeteksinya namun hanya mencari using namespacedi file header. Karena alasan terakhir itu, hal ini jarang terjadi dalam proyek nyata. Rekan kerja yang lebih berpengalaman akan segera mengeluh jika seseorang melakukan hal seperti itu.

Dalam proyek nyata orang mencoba meminimalkan jumlah file yang disertakan, karena semakin sedikit Anda memasukkan, semakin cepat kompilasi. Itu menghemat waktu semua orang. Namun jika file header mengasumsikan bahwa sesuatu harus disertakan sebelumnya, maka file itu harus menyertakannya sendiri. Jika tidak, itu membuat header tidak mandiri.

Öö Tiib
sumber
4

Kamu benar. Dan file apa pun hanya boleh menyertakan header yang dibutuhkan oleh file itu. Adapun "apakah melakukan sesuatu yang salah adalah hal biasa dalam proyek dunia nyata?" - Oh ya!


sumber
4

Seperti semua hal dalam pemrograman, pragmatisme harus menang atas dogmatisme, IMO.

Selama Anda membuat keputusan di seluruh proyek ("Proyek kami menggunakan STL secara ekstensif, dan kami tidak ingin harus menambahkan semuanya dengan std ::."), Saya tidak melihat ada masalah dengan itu. Satu-satunya hal yang Anda pertaruhkan adalah bentrokan nama, dan dengan keberadaan STL di mana-mana itu tidak mungkin menjadi masalah.

Di sisi lain, jika itu adalah keputusan oleh satu pengembang dalam satu file header (non-pribadi), saya dapat melihat bagaimana hal itu akan menimbulkan kebingungan di antara tim dan harus dihindari.

ijprest
sumber
4

Berkenaan dengan "Apakah ada cara untuk membatalkan [ usingpernyataan]?"

Saya pikir akan berguna untuk menunjukkan bahwa usingdeklarasi dipengaruhi oleh ruang lingkup.

#include <vector>

{   // begin a new scope with {
    using namespace std;
    vector myVector;  // std::vector is used
}   // end the scope with }

vector myOtherVector;   // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified

Sangat efektif ya. Dengan membatasi ruang lingkup usingdeklarasi, efeknya hanya bertahan dalam ruang lingkup itu; itu 'dibatalkan' saat cakupan itu berakhir.

Ketika usingdeklarasi dideklarasikan dalam file di luar cakupan lain, ia memiliki cakupan file dan memengaruhi semua yang ada di file itu.

Dalam kasus file header, jika usingdeklarasi berada pada cakupan file, ini akan meluas ke cakupan file apa pun yang termasuk dalam header.

YoungJohn
sumber
2
Anda tampaknya menjadi satu-satunya yang memahami pertanyaan yang sebenarnya ... namun, kompilasi saya tidak terlalu senang saya menggunakan perlambatan dalam kelas.
Rustypaper
Jawaban ini dapat dibuat lebih baik dengan menjelaskan masalah dengan gagasan OP tentang bagaimana ruang lingkup harus bekerja (seperti namespacehal-hal deklarasi) vs bagaimana sebenarnya bekerja (seperti variabel). {}melampirkannya membatasi ruang lingkupnya, {}setelah ia tidak melakukan apa pun yang berhubungan dengannya. Itu adalah cara yang tidak disengaja using namespacediterapkan secara global.
TafT
2

Saya yakin Anda dapat menggunakan 'using' di header C ++ dengan aman jika Anda menulis deklarasi di namespace bersarang seperti ini:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
    /*using statements*/

    namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
    {
        /*declarations*/
    }
}

using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;

Ini harus mencakup hanya hal-hal yang dideklarasikan dalam 'DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED' tanpa ruang nama yang digunakan. Saya telah mengujinya pada kompiler mingw64.

AnArrayOfFunctions
sumber
Ini adalah teknik berguna yang belum pernah saya lihat sebelumnya; Terima kasih. Biasanya saya baik-baik saja dengan menggunakan kualifikasi lingkup penuh, dan meletakkan usingdeklarasi di dalam definisi fungsi di mana saya bisa sehingga mereka tidak akan mencemari ruang nama di luar fungsi. Tapi sekarang saya ingin menggunakan literal yang ditentukan pengguna C ++ 11 dalam file header, dan sesuai dengan konvensi biasa, operator literal dilindungi oleh namespace; tetapi saya tidak ingin menggunakannya dalam daftar penginisialisasi konstruktor yang tidak berada dalam cakupan yang dapat saya gunakan dengan usingdeklarasi non-polusi . Jadi ini bagus untuk memecahkan masalah itu.
Anthony Hall
Meskipun efek samping yang menguntungkan dari pola ini adalah bahwa setiap kelas dideklarasikan di dalam terdalam namespace akan muncul di compiler pesan kesalahan dengan nama yang memenuhi syarat: error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName .... Setidaknya, itulah yang terjadi pada saya di g ++.
Anthony Hall