I've telah diberitahu oleh orang lain yang menulis using namespace std;
dalam kode yang salah, dan bahwa saya harus menggunakan std::cout
dan std::cin
langsung sebagai gantinya.
Mengapa using namespace std;
dianggap sebagai praktek yang buruk? Itu tidak efisien atau tidak itu resiko yang menyatakan ambigu variabel (variabel-variabel yang berbagi nama yang sama sebagai fungsi dalam std
namespace)? Apakah itu dampak kinerja?
Hal ini tidak terkait dengan kinerja pada semua. Tapi pertimbangkan ini: anda menggunakan dua perpustakaan yang disebut Foo dan Bar:
using namespace foo;
using namespace bar;
Semuanya bekerja dengan baik, dan anda dapat memanggil Bla()
dari Foo dan Quux()
dari Bar tanpa masalah. Tapi suatu hari anda meng-upgrade ke versi baru dari Foo 2.0, yang sekarang menawarkan fungsi yang disebut Quux()
. Sekarang anda've punya konflik: Kedua Foo 2.0 dan Bar impor Quux()
ke global namespace. Ini akan mengambil beberapa upaya untuk memperbaiki kesalahan, terutama jika parameter fungsi terjadi untuk pertandingan.
Jika anda telah menggunakan foo::Blah()
dan bar::Quux()
, maka pengenalan foo::Quux()
akan menjadi non-event.
Saya setuju dengan segala sesuatu Greg menulis, tapi saya'd ingin menambahkan: Itu bahkan bisa lebih buruk dari Greg mengatakan!
Perpustakaan Foo 2.0 bisa memperkenalkan fungsi, Quux()
, yang jelas pertandingan yang lebih baik untuk beberapa panggilan untuk Quux()
dari bar::Quux()
kode anda disebut selama bertahun-tahun. Kemudian anda kode masih mengkompilasi tapi itu diam-diam menyebut salah function dan apakah tuhan-tahu-apa. Yang's hampir sebagai buruk sebagai hal-hal yang bisa mendapatkan.
Menjaga dalam pikiran bahwa std
namespace memiliki banyak pengenal, banyak yang very yang umum (berpikir daftar
, jenis
, string
, iterator
, dll.) yang sangat mungkin muncul dalam kode lain, terlalu.
Jika anda menganggap hal ini tidak mungkin: Ada pertanyaan di sini pada Stack Overflow di mana cukup banyak persis ini terjadi (salah fungsi yang disebut karena dihilangkan std::
prefix) sekitar setengah tahun setelah saya memberikan jawaban ini. Di sini yang lain, yang lebih baru-baru ini contoh pertanyaan seperti itu.
Jadi ini adalah masalah nyata.
Berikut ini's satu lebih titik data: banyak, Banyak tahun yang lalu, saya juga pernah menemukan hal ini menjengkelkan karena untuk mengawali segala sesuatu dari perpustakaan standar dengan std::. Kemudian saya bekerja di sebuah proyek di mana ia memutuskan di awal bahwa kedua
menggunakan` arahan dan deklarasi ini adalah dilarang kecuali untuk lingkup fungsi. Tebak apa? Itu mengambil sebagian besar dari kita sangat beberapa minggu untuk membiasakan diri untuk menulis awalan, dan setelah beberapa minggu lagi sebagian besar dari kita bahkan setuju bahwa itu benar-benar membuat kode more readable. Ada's alasannya: Apakah anda ingin lebih pendek atau lebih lama prosa adalah subjektif, tapi awalan obyektif menambah kejelasan kode. Tidak hanya compiler, tetapi anda juga lebih mudah untuk melihat mana pengenal dimaksud.
Dalam satu dekade, proyek itu tumbuh untuk memiliki beberapa juta baris kode. Karena diskusi ini datang lagi dan lagi, saya pernah ingin tahu seberapa sering (diperbolehkan) fungsi-ruang lingkup menggunakan
benar-benar digunakan dalam proyek. Saya grep'd sumber-sumber itu dan hanya menemukan satu atau dua lusin tempat-tempat di mana ia digunakan. Bagi saya ini menunjukkan bahwa, sekali mencoba, pengembang don't menemukan std::
cukup menyakitkan untuk mempekerjakan menggunakan arahan bahkan sekali setiap 100 kLoC bahkan di mana ia diizinkan untuk digunakan.
Bottom line: secara Eksplisit awalan segala sesuatu yang doesn't melakukan hal yang membahayakan, mengambil sedikit membiasakan diri, dan memiliki tujuan keuntungan. Secara khusus, itu membuat kode lebih mudah untuk ditafsirkan oleh compiler dan oleh manusia pembaca — dan yang mungkin harus menjadi tujuan utama ketika menulis kode.
Masalah dengan menempatkan menggunakan namespace
di header file dari kelas anda adalah bahwa hal itu memaksa siapa saja yang ingin menggunakan kelas anda (dengan menyertakan header file) juga menjadi 'menggunakan' (yaitu melihat segala sesuatu di) orang-orang lain namespaces.
Namun, anda mungkin merasa bebas untuk menempatkan menggunakan pernyataan anda (pribadi) *.cpp file.
Berhati-hatilah bahwa beberapa orang tidak setuju dengan saya mengatakan "jangan ragu" seperti ini -- karena meskipun menggunakan
pernyataan dalam cpp file baik dari dalam header (karena itu doesn't mempengaruhi orang-orang yang menyertakan header file), mereka pikir itu's masih tidak baik (karena tergantung pada kode itu bisa membuat pelaksanaan kelas lebih sulit untuk mempertahankan). C++ Super-FAQ entry mengatakan,
menggunakan direktif ada untuk warisan kode C++ dan untuk memudahkan transisi ke namespaces, tapi anda mungkin tidak harus menggunakannya secara teratur, setidaknya tidak dalam kode C++.
FAQ menyarankan dua alternatif:
menggunakan std::cout; // menggunakan-deklarasi memungkinkan anda menggunakan cout tanpa kualifikasi cout << "Nilai";
std::cout << "Nilai";
Aku baru saja berlari ke keluhan tentang Visual Studio 2010. Ternyata cukup banyak sekali sumber file memiliki dua baris:
using namespace std;
using namespace boost;
Banyak Meningkatkan fitur-fitur yang akan ke C++0x standar, dan Visual Studio 2010 telah banyak C++0x fitur, begitu tiba-tiba program tersebut tidak menyusun.
Oleh karena itu, menghindari menggunakan namespace X;
adalah bentuk masa depan-pemeriksaan, cara membuat yakin perubahan ke perpustakaan dan/atau file header yang di gunakan tidak akan merusak program.
Versi pendek: don't menggunakan global menggunakan
deklarasi atau arahan dalam file header. Merasa bebas untuk menggunakan mereka dalam implementasi file. Berikut ini's apa yang Ramuan Sutter dan Andrei Alexandrescu telah mengatakan tentang masalah ini di C++ Standar Pengkodean (huruf tebal untuk penekanan saya):
Ringkasan
Namespace usings untuk kenyamanan anda, bukan untuk anda untuk menimbulkan pada lain-lain: tidak Pernah menulis menggunakan deklarasi atau menggunakan petunjuk sebelum #include directive.
Wajar: Dalam file header, jangan menulis namespace-tingkat menggunakan petunjuk atau menggunakan deklarasi; sebaliknya, secara eksplisit namespace-lolos semua nama-nama. (Kedua aturan berikut dari yang pertama, karena header pernah bisa tahu apa yang header lain #termasuk mungkin muncul setelah mereka).
Diskusi
singkatnya: Anda dapat dan harus menggunakan namespace menggunakan deklarasi dan arahan bebas dalam implementasi file setelah #termasuk arahan dan merasa baik tentang hal itu. Meskipun berulang-ulang pernyataan-pernyataan yang bertentangan, namespace menggunakan deklarasi dan arahan yang tidak jahat dan mereka tidak mengalahkan tujuan namespaces. Sebaliknya, mereka adalah apa yang membuat namespaces yang dapat digunakan.
Orang tidak't menggunakan menggunakan
direktif di lingkup global, terutama di header. Namun, ada situasi di mana sangat tepat bahkan di file header:
template <typename FloatType> inline
FloatType compute_something(FloatType x)
{
using namespace std; // No problem since scope is limited
return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4));
}
Ini lebih baik daripada eksplisit kualifikasi (std::dosa
, std::cos
...), karena itu lebih pendek dan memiliki kemampuan untuk bekerja dengan pengguna didefinisikan floating point jenis (via argumen tergantung lookup (ADL)).
Hal ini dianggap "buruk" hanya ketika digunakan secara global. Karena:
menggunakan namespace xyz
.using namespace std
anda mungkin tidak menyadari semua hal-hal yang anda ambil-dan ketika anda menambahkan #include
atau pindah ke new C++ revisi yang mungkin anda dapatkan konflik nama anda tidak menyadari.Pergi ke depan dan menggunakannya secara lokal (hampir) bebas. Ini, tentu saja, mencegah anda dari pengulangan std::
- dan pengulangan juga buruk.
Dalam C++03 ada sebuah idiom -- boilerplate kode-untuk menerapkan swap
fungsi untuk kelas anda. Disarankan bahwa anda benar-benar menggunakan lokal using namespace std
-- atau setidaknya menggunakan std::swap
:
class Thing {
int value_;
Child child_;
public:
// ...
friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
using namespace std; // make `std::swap` available
// swap all members
swap(a.value_, b.value_); // `std::stwap(int, int)`
swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}
Ini tidak berikut sihir:
std::swap
untuk value_
, yaitu batal std::swap(int, int)
.void swap(Anak& Anak&)
dilaksanakan compiler akan memilih itu.batal std::swap(Anak& Anak&)
dan mencoba yang terbaik swapping ini.Dengan C++11 tidak ada alasan untuk menggunakan pola ini lagi. Pelaksanaan std::swap
diubah untuk menemukan potensi yang berlebihan dan memilih.
Jika anda mengimpor kanan header file anda tiba-tiba memiliki nama-nama seperti hex
, kiri
, plus
atau count
dalam lingkup global. Ini mungkin akan mengejutkan jika anda tidak menyadari bahwa std::
berisi nama-nama ini. Jika anda juga mencoba untuk menggunakan nama-nama lokal dapat menyebabkan beberapa kebingungan.
Jika semua hal-hal yang standar dalam dirinya sendiri namespace anda don't perlu khawatir tentang nama tabrakan dengan kode anda atau perpustakaan lain.
Programmer berpengalaman menggunakan apa pun memecahkan masalah mereka dan menghindari apa pun yang menciptakan masalah-masalah baru, dan mereka menghindari header-file-menggunakan-arahan untuk alasan ini tepat.
Programmer berpengalaman juga mencoba untuk menghindari full kualifikasi nama di dalam file sumber. Kecil alasan untuk ini adalah bahwa hal itu's tidak elegan untuk menulis kode yang lebih ketika kode kurang cukup kecuali ada alasan yang baik. Alasan utama untuk ini adalah mematikan argumen tergantung lookup (ADL).
Apa ini alasan? Kadang-kadang programmer secara eksplisit ingin mematikan ADL, lain kali mereka ingin disatukan.
Jadi berikut ini adalah OK:
Saya setuju bahwa itu tidak boleh digunakan secara global, tapi itu's tidak begitu jahat untuk digunakan secara lokal, seperti dalam nama
. Berikut ini's contoh dari "Bahasa Pemrograman C++":
namespace My_lib {
using namespace His_lib; // Everything from His_lib
using namespace Her_lib; // Everything from Her_lib
using His_lib::String; // Resolve potential clash in favor of His_lib
using Her_lib::Vector; // Resolve potential clash in favor of Her_lib
}
Dalam contoh ini, kami memutuskan potensial nama bentrokan dan ambiguitas yang timbul dari komposisi mereka.
Nama-nama yang secara eksplisit menyatakan ada (termasuk nama-nama yang dinyatakan dengan menggunakan pernyataan seperti His_lib::String
) mengambil prioritas di atas nama-nama yang dibuat dapat diakses dalam lingkup lain dengan menggunakan direktif (menggunakan namespace Her_lib
).
Saya juga menganggap itu sebuah praktek yang buruk. Mengapa? Hanya satu hari saya berpikir bahwa fungsi namespace adalah untuk membagi hal-hal, jadi seharusnya aku't merusak itu dengan melemparkan semuanya ke dalam satu global bag.
Namun, jika saya sering menggunakan 'cout' dan 'cin', saya menulis: menggunakan std::cout; menggunakan std::cin;
di .cpp file (tidak pernah di header file seperti itu menyebar dengan #include
). Saya berpikir bahwa tidak ada yang waras akan pernah nama aliran cout
atau cin
. ;)
It's bagus untuk melihat kode dan tahu apa yang dilakukannya. Jika saya melihat std::cout
aku tahu bahwa's cout
aliran std
perpustakaan. Jika saya melihat cout
kemudian aku don't tahu. Itu could menjadi cout
aliran std
perpustakaan. Atau bisa jadi sebuah int cout = 0;
sepuluh garis tinggi pada fungsi yang sama. Atau statis
variabel bernama cout
dalam file tersebut. Itu bisa apa saja.
Sekarang ambil satu juta baris kode dasar, yang isn't sangat besar, dan anda'kembali mencari bug, yang berarti anda tahu ada satu baris dalam satu juta baris yang doesn't melakukan apa yang seharusnya dilakukan. cout << 1;
bisa membaca static int
bernama cout
, pergeseran ke kiri oleh satu bit, dan membuang hasil. Mencari bug, saya'a harus memeriksa itu. Anda dapat melihat bagaimana saya benar-benar benar-benar lebih memilih untuk melihat std::cout
?
It's salah satu dari hal-hal yang tampaknya benar-benar ide yang baik jika anda seorang guru dan tidak pernah harus menulis dan mempertahankan kode apapun untuk hidup. Saya suka melihat kode di mana (1) aku tahu apa yang dilakukannya; dan, (2) I'm yakin bahwa orang yang menulis itu tahu apa yang dilakukannya.
It's semua tentang mengelola kompleksitas. Dengan menggunakan namespace akan menarik hal-hal yang anda don't ingin, dan dengan demikian mungkin membuatnya sulit untuk debug (saya katakan mungkin). Menggunakan std:: seluruh tempat lebih sulit untuk membaca (teks yang lebih banyak dan semua itu).
Kuda-kuda untuk kursus - mengelola kompleksitas bagaimana anda dapat dan merasa mampu.
Anda harus mampu untuk membaca kode yang ditulis oleh orang-orang yang memiliki gaya yang berbeda dan praktek-praktek terbaik pendapat dari anda.
Jika anda're hanya menggunakan cout
, tak akan ada yang bingung. Tapi ketika anda memiliki banyak ruang nama yang terbang di sekitar dan anda melihat kelas ini dan anda tidak't benar-benar yakin apa yang dilakukannya, memiliki namespace eksplisit bertindak sebagai komentar macam. Anda dapat melihat pada pandangan pertama, "oh, ini adalah filesystem operasi" atau "yang's melakukan jaringan hal-hal".
Pertimbangkan
// myHeader.h
#include <sstream>
using namespace std;
// someoneElses.cpp/h
#include "myHeader.h"
class stringstream { // Uh oh
};
Perhatikan bahwa ini adalah contoh sederhana. Jika anda memiliki file dengan 20 termasuk dan impor lainnya, anda'll memiliki banyak dependensi untuk pergi melalui untuk mengetahui masalah. Hal buruk tentang ini adalah bahwa anda bisa mendapatkan yang tidak terkait kesalahan dalam modul-modul lainnya tergantung pada definisi-definisi konflik itu.
It's tidak mengerikan, tetapi anda'll menyelamatkan diri sakit kepala dengan tidak menggunakan itu dalam file-file header atau global namespace. It's mungkin semua hak untuk melakukan itu sangat terbatas lingkup, tapi aku've tidak pernah punya masalah mengetik tambahan lima karakter untuk memperjelas di mana fungsi-fungsi saya berasal.
Menggunakan banyak ruang nama pada saat yang sama lebih jelas resep untuk bencana, tetapi HANYA menggunakan namespace std
dan hanya namespace std
bukan masalah besar menurut saya karena redefinisi hanya dapat terjadi dengan kode anda sendiri...
Jadi hanya mempertimbangkan fungsi mereka sebagai reserved nama-nama seperti "int" atau "kelas" dan yang itu.
Orang-orang harus berhenti menjadi begitu anal tentang hal itu. Guru anda itu benar. Hanya menggunakan SATU namespace; itu adalah seluruh titik menggunakan ruang nama tempat pertama. Anda tidak seharusnya menggunakan lebih dari satu pada waktu yang sama. Kecuali jika itu adalah anda sendiri. Jadi sekali lagi, redefinisi tidak akan terjadi.
Namespace adalah nama sebuah ruang lingkup. Namespaces yang digunakan untuk kelompok yang terkait deklarasi dan untuk menjaga terpisah item yang terpisah. Misalnya, dua secara terpisah mengembangkan perpustakaan dapat menggunakan nama yang sama untuk merujuk ke berbagai item, tapi pengguna masih dapat menggunakan keduanya:
namespace Mylib{
template<class T> class Stack{ /* ... */ };
// ...
}
namespace Yourlib{
class Stack{ /* ... */ };
// ...
}
void f(int max) {
Mylib::Stack<int> s1(max); // Use my stack
Yourlib::Stack s2(max); // Use your stack
// ...
}
Mengulangi nama namespace dapat menjadi gangguan bagi para pembaca dan penulis. Akibatnya, hal ini mungkin untuk menyatakan bahwa nama-nama tertentu namespace yang tersedia tanpa eksplisit kualifikasi. Misalnya:
void f(int max) {
using namespace Mylib; // Make names from Mylib accessible
Stack<int> s1(max); // Use my stack
Yourlib::Stack s2(max); // Use your stack
// ...
}
Namespaces menyediakan alat yang ampuh untuk pengelolaan perpustakaan yang berbeda dan versi yang berbeda dari kode. Secara khusus, mereka menawarkan programmer alternatif tentang bagaimana eksplisit untuk membuat referensi untuk nonlokal nama.
Sumber: Gambaran dari Bahasa Pemrograman C++ oleh Bjarne Stroustrup
Saya setuju dengan yang lain di sini, tapi saya akan seperti untuk mengatasi kekhawatiran mengenai dibaca - anda dapat menghindari semua itu dengan hanya menggunakan typedefs di bagian atas file, fungsi atau deklarasi kelas.
Biasanya saya menggunakannya di kelas saya deklarasi sebagai metode dalam kelas cenderung untuk berurusan dengan data yang sama jenis (anggota) dan typedef adalah kesempatan untuk menetapkan nama yang bermakna dalam konteks kelas. Ini benar-benar membantu dibaca dalam definisi dari metode kelas.
// Header
class File
{
typedef std::vector<std::string> Lines;
Lines ReadLines();
}
dan dalam pelaksanaannya:
// .cpp
Lines File::ReadLines()
{
Lines lines;
// Get them...
return lines;
}
sebagai lawan untuk:
// .cpp
vector<string> File::ReadLines()
{
vector<string> lines;
// Get them...
return lines;
}
atau:
// .cpp
std::vector<std::string> File::ReadLines()
{
std::vector<std::string> lines;
// Get them...
return lines;
}
Contoh konkret untuk memperjelas kekhawatiran. Bayangkan anda memiliki situasi di mana anda memiliki dua perpustakaan, foo
dan bar
, masing-masing dengan mereka sendiri namespace:
namespace foo {
void a(float) { /* Does something */ }
}
namespace bar {
...
}
Sekarang mari's mengatakan anda menggunakan foo
dan bar
bersama-sama dalam program anda sendiri sebagai berikut:
using namespace foo;
using namespace bar;
void main() {
a(42);
}
Pada titik ini semuanya baik-baik saja. Ketika anda menjalankan program anda itu 'Melakukan sesuatu'. Tapi kemudian anda update bar
dan let's mengatakan itu telah berubah menjadi seperti:
namespace bar {
void a(float) { /* Does something completely different */ }
}
Pada titik ini anda'll mendapatkan kesalahan kompiler:
using namespace foo;
using namespace bar;
void main() {
a(42); // error: call to 'a' is ambiguous, should be foo::a(42)
}
Jadi anda'll perlu melakukan beberapa perawatan untuk memperjelas bahwa 'a' berarti foo::a
. Yang's tidak diinginkan, tapi untungnya itu sangat mudah (hanya menambahkan foo::
di depan semua panggilan ke a
yang kompilator tanda sebagai ambigu).
Tapi bayangkan skenario alternatif di mana bar berubah bukan untuk terlihat seperti ini:
namespace bar {
void a(int) { /* Does something completely different */ }
}
Pada titik ini anda panggilan untuk a(42)
tiba-tiba mengikat bar::a
bukan foo::a
dan bukannya melakukan 'sesuatu' itu 'sesuatu yang benar-benar berbeda'. Tidak ada compiler peringatan atau apa pun. Program anda hanya diam-diam mulai melakukan sesuatu yang berbeda dari sebelumnya.
Ketika anda menggunakan namespace anda're mempertaruhkan skenario seperti ini, itulah sebabnya mengapa orang-orang tidak nyaman menggunakan ruang nama. Semakin banyak hal-hal dalam sebuah namespace, semakin besar risiko konflik, sehingga orang-orang mungkin akan lebih nyaman menggunakan namespace std
(karena jumlah hal-hal yang namespace) dari namespaces.
Pada akhirnya ini adalah trade-off antara writability vs keandalan/rawatan. Pembacaan mungkin faktor dalam juga, tapi aku bisa melihat argumen untuk pergi dengan cara baik. Biasanya aku akan mengatakan keandalan dan pemeliharaan yang lebih penting, tetapi dalam kasus ini anda'll terus-menerus membayar writability biaya yang cukup langka keandalan/rawatan dampak. The 'terbaik' trade-off akan menentukan pada proyek anda dan prioritas anda.