Mengapa "menggunakan namespace std;" dianggap praktik yang buruk?
Saya telah diberitahu oleh orang lain bahwa menulis using namespace std;
kode itu salah, dan saya harus menggunakan std::cout
dan std::cin
sebagai gantinya secara langsung.
Mengapa using namespace std;
dianggap praktik yang buruk? Apakah tidak efisien atau berisiko menyatakan variabel ambigu (variabel yang memiliki nama yang sama dengan fungsi di std
namespace)? Apakah itu memengaruhi kinerja?
Jawaban
Ini sama sekali tidak terkait dengan kinerja. Tetapi pertimbangkan ini: Anda menggunakan dua pustaka yang disebut Foo dan Bar:
using namespace foo;
using namespace bar;
Semuanya berfungsi dengan baik, dan Anda dapat menelepon Blah()
dari Foo dan Quux()
dari Bar tanpa masalah. Tetapi suatu hari Anda meningkatkan ke versi baru Foo 2.0, yang sekarang menawarkan fungsi bernama Quux()
. Sekarang Anda mengalami konflik: Baik Foo 2.0 dan Bar diimpor Quux()
ke namespace global Anda. Ini akan membutuhkan beberapa upaya untuk memperbaikinya, terutama jika parameter fungsi kebetulan cocok.
Jika Anda pernah menggunakan foo::Blah()
dan bar::Quux()
, maka pengenalan tentang foo::Quux()
akan menjadi bukan peristiwa.
Saya setuju dengan semua yang Greg tulis , tetapi saya ingin menambahkan: Ini bahkan bisa lebih buruk daripada yang dikatakan Greg!
Library Foo 2.0 dapat memperkenalkan sebuah fungsi,, Quux()
yang sangat cocok untuk beberapa panggilan Anda Quux()
daripada bar::Quux()
kode yang Anda panggil selama bertahun-tahun. Kemudian kode Anda masih dikompilasi , tetapi secara diam-diam memanggil fungsi yang salah dan tidak tahu apa-apa. Itu seburuk yang bisa didapat.
Perlu diingat bahwa std
namespace memiliki ton pengidentifikasi, banyak yang sangat yang umum (berpikir list
, sort
, string
, iterator
, dll) yang sangat mungkin muncul dalam kode lain juga.
Jika Anda menganggap ini tidak mungkin: Ada pertanyaan yang diajukan di sini di Stack Overflow di mana hampir persis seperti ini terjadi (fungsi salah dipanggil karena std::
awalan yang dihilangkan ) sekitar setengah tahun setelah saya memberikan jawaban ini. Ini adalah contoh lain yang lebih baru dari pertanyaan semacam itu. Jadi ini masalah nyata.
Berikut satu lagi poin data: Bertahun-tahun yang lalu, saya juga merasa menjengkelkan karena harus mengawali semuanya dari pustaka standar dengan std::
. Kemudian saya bekerja dalam sebuah proyek di mana diputuskan di awal bahwa baik using
direktif dan deklarasi dilarang kecuali untuk lingkup fungsi. Tebak apa? Sebagian besar dari kami membutuhkan beberapa minggu untuk membiasakan diri menulis awalan, dan setelah beberapa minggu, kebanyakan dari kami bahkan setuju bahwa itu benar-benar membuat kode lebih mudah dibaca . Ada alasan untuk itu: Apakah Anda menyukai prosa yang lebih pendek atau lebih panjang itu subjektif, tetapi awalan secara obyektif menambahkan kejelasan pada kode. Tidak hanya kompilernya, tetapi Anda juga akan lebih mudah melihat pengenal mana yang dirujuk.
Dalam satu dekade, proyek itu berkembang menjadi beberapa juta baris kode. Sejak diskusi ini muncul lagi dan lagi, saya pernah penasaran seberapa sering ruang lingkup fungsi (diperbolehkan) using
sebenarnya digunakan dalam proyek. Saya mengambil sumbernya dan hanya menemukan satu atau dua lusin tempat di mana itu digunakan. Bagi saya ini menunjukkan bahwa, sekali mencoba, pengembang tidak merasa std::
cukup menyakitkan untuk menggunakan arahan bahkan sekali setiap 100 kLoC bahkan di tempat yang diizinkan untuk digunakan.
Intinya: Mengawali segala sesuatu secara eksplisit tidak membahayakan, membutuhkan sedikit waktu untuk membiasakan diri, dan memiliki keuntungan obyektif. Secara khusus, ini membuat kode lebih mudah untuk ditafsirkan oleh kompiler dan oleh pembaca manusia - dan itu mungkin menjadi tujuan utama saat menulis kode.
Masalah dengan memasukkan using namespace
file header kelas Anda adalah bahwa hal itu memaksa siapa saja yang ingin menggunakan kelas Anda (dengan menyertakan file header Anda) untuk juga 'menggunakan' (yaitu melihat semuanya di) ruang nama lain tersebut.
Namun, Anda mungkin merasa bebas untuk meletakkan pernyataan menggunakan di file (pribadi) * .cpp Anda.
Berhati-hatilah bahwa beberapa orang tidak setuju dengan perkataan saya "merasa bebas" seperti ini - karena meskipun using
pernyataan di file cpp lebih baik daripada di header (karena tidak memengaruhi orang yang menyertakan file header Anda), mereka menganggapnya masih belum bagus (karena bergantung pada kodenya, implementasi kelas bisa lebih sulit dipertahankan). Entri C ++ Super-FAQ ini mengatakan,
Petunjuk penggunaan ada untuk kode C ++ lama dan untuk memudahkan transisi ke namespace, tetapi Anda mungkin tidak boleh menggunakannya secara teratur, setidaknya tidak di kode C ++ baru Anda.
FAQ menyarankan dua alternatif:
Deklarasi penggunaan:
using std::cout; // a using-declaration lets you use cout without qualification cout << "Values:";
Cukup ketikkan std ::
std::cout << "Values:";
Saya baru-baru ini mengalami keluhan tentang Visual Studio 2010 . Ternyata hampir semua file sumber memiliki dua baris ini:
using namespace std;
using namespace boost;
Banyak fitur Boost yang menggunakan standar C ++ 0x, dan Visual Studio 2010 memiliki banyak fitur C ++ 0x, jadi tiba-tiba program ini tidak dapat dikompilasi.
Oleh karena itu, menghindari using namespace X;
adalah bentuk pemeriksaan masa depan, cara untuk memastikan perubahan ke pustaka dan / atau file header yang digunakan tidak akan merusak program.
Versi singkat: jangan gunakan using
deklarasi atau arahan global di file header. Jangan ragu untuk menggunakannya dalam file implementasi. Inilah yang dikatakan Herb Sutter dan Andrei Alexandrescu tentang masalah ini dalam Standar Pengkodean C ++ (cetak tebal untuk penekanan adalah milik saya):
Ringkasan
Penggunaan namespace adalah untuk kenyamanan Anda, bukan untuk Anda lakukan pada orang lain: Jangan pernah menulis deklarasi using atau using sebelum arahan #include.
Akibat yang wajar: Dalam file header, jangan menulis tingkat namespace menggunakan arahan atau menggunakan deklarasi; sebagai gantinya, secara eksplisit memenuhi syarat namespace semua nama. (Aturan kedua mengikuti dari yang pertama, karena header tidak pernah bisa mengetahui header lain #includes yang mungkin muncul setelahnya.)
Diskusi
Singkatnya: Anda dapat dan harus menggunakan namespace menggunakan deklarasi dan arahan secara bebas dalam file implementasi Anda setelah arahan #include dan merasa nyaman karenanya. Meskipun pernyataan berulang yang bertentangan, namespace yang menggunakan deklarasi dan arahan tidak jahat dan tidak mengalahkan tujuan namespace. Sebaliknya, itulah yang membuat namespace dapat digunakan .
Seseorang tidak boleh menggunakan using
arahan di lingkup global, terutama di header. Namun, ada situasi yang sesuai bahkan dalam 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 kualifikasi eksplisit ( std::sin
, std::cos
...), karena lebih pendek dan memiliki kemampuan untuk bekerja dengan tipe titik mengambang yang ditentukan pengguna (melalui pencarian yang bergantung pada argumen (ADL)).
Jangan gunakan secara global
Itu dianggap "buruk" hanya jika digunakan secara global . Karena:
- Anda mengacaukan namespace tempat Anda memprogram.
- Pembaca akan kesulitan melihat dari mana pengenal tertentu berasal, saat Anda menggunakan banyak
using namespace xyz
. - Semua yang benar untuk lainnya pembaca kode sumber Anda bahkan lebih benar untuk pembaca yang paling sering itu: sendiri. Kembalilah dalam satu atau dua tahun dan lihatlah ...
- Jika Anda hanya berbicara tentang
using namespace std
Anda mungkin tidak mengetahui semua hal yang Anda ambil - dan ketika Anda menambahkan yang lain#include
atau pindah ke revisi C ++ baru Anda mungkin mendapatkan konflik nama yang tidak Anda sadari.
Anda dapat menggunakannya secara lokal
Silakan dan gunakan secara lokal (hampir) dengan bebas. Ini, tentu saja, mencegah Anda dari pengulangan std::
- dan pengulangan juga buruk.
Sebuah idiom untuk menggunakannya secara lokal
Di C ++ 03 ada idiom - kode boilerplate - untuk mengimplementasikan swap
fungsi untuk kelas Anda. Disarankan agar Anda benar-benar menggunakan lokal using namespace std
- atau setidaknya using 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 melakukan keajaiban berikut:
- Kompilator akan memilih
std::swap
untukvalue_
, yaituvoid std::swap(int, int)
. - Jika Anda memiliki kelebihan beban yang
void swap(Child&, Child&)
diimplementasikan, kompilator akan memilihnya. - Jika Anda tidak memiliki kelebihan beban, kompilator akan menggunakan
void std::swap(Child&,Child&)
dan mencoba yang terbaik untuk menukar ini.
Dengan C ++ 11 tidak ada alasan untuk menggunakan pola ini lagi. Implementasi std::swap
diubah untuk menemukan potensi kelebihan beban dan memilihnya.
Jika Anda mengimpor file header yang tepat Anda tiba-tiba memiliki nama seperti hex, left, plusatau countdalam lingkup global Anda. Hal ini mungkin akan mengejutkan jika Anda tidak sadar yang std::
mengandung nama-nama tersebut. Jika Anda juga mencoba menggunakan nama-nama ini secara lokal, ini dapat menyebabkan kebingungan.
Jika semua hal standar ada di namespace-nya sendiri, Anda tidak perlu khawatir tentang benturan nama dengan kode Anda atau pustaka lain.
Alasan lainnya adalah kejutan.
Jika saya melihat cout << blah
, alih-alih std::cout << blah
saya berpikir: Apa ini cout
? Apakah ini normal cout
? Apakah itu sesuatu yang istimewa?
Pemrogram berpengalaman menggunakan apa pun untuk memecahkan masalah mereka dan menghindari apa pun yang menciptakan masalah baru, dan mereka menghindari petunjuk penggunaan tingkat header-file untuk alasan ini.
Pemrogram berpengalaman juga mencoba menghindari kualifikasi penuh nama di dalam file sumber mereka. Alasan kecil untuk ini adalah tidak elegan untuk menulis lebih banyak kode ketika lebih sedikit kode sudah cukup kecuali ada alasan yang bagus . Alasan utama untuk ini adalah menonaktifkan pencarian yang bergantung pada argumen (ADL).
Apa alasan bagus ini ? Terkadang programmer secara eksplisit ingin menonaktifkan ADL, di lain waktu mereka ingin menghilangkan keraguan.
Jadi berikut ini OK:
- Arahan penggunaan tingkat fungsi dan deklarasi penggunaan di dalam implementasi fungsi
- Deklarasi penggunaan tingkat file sumber di dalam file sumber
- (Terkadang) arahan menggunakan tingkat file sumber
Saya setuju bahwa itu tidak boleh digunakan secara global, tetapi tidak terlalu jahat untuk digunakan secara lokal, seperti di namespace
. Berikut contoh dari "The C ++ Programming Language" :
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 menyelesaikan kemungkinan bentrokan nama dan ambiguitas yang timbul dari komposisinya.
Nama yang secara eksplisit dideklarasikan di sana (termasuk nama yang dideklarasikan dengan menggunakan-deklarasi seperti His_lib::String
) diprioritaskan di atas nama yang dibuat dapat diakses di lingkup lain dengan menggunakan direktif ( using namespace Her_lib
).
Saya juga menganggapnya sebagai praktik yang buruk. Mengapa? Hanya suatu hari saya berpikir bahwa fungsi namespace adalah untuk membagi barang, jadi saya tidak boleh merusaknya dengan membuang semuanya ke dalam satu tas global.
Namun, jika saya sering menggunakan 'cout' dan 'cin', saya menulis: using std::cout; using std::cin;
di file .cpp (jangan pernah di file header seperti yang disebarkan #include
). Saya pikir tidak ada orang waras yang akan menamai aliran cout
atau cin
. ;)
Sangat menyenangkan melihat kode dan tahu apa fungsinya. Jika saya melihat std::cout
saya tahu itu cout
aliran std
perpustakaan. Jika saya melihat cout
maka saya tidak tahu. Ini bisa menjadi cout
aliran dari std
perpustakaan. Atau mungkin ada int cout = 0;
sepuluh baris yang lebih tinggi dalam fungsi yang sama. Atau static
variabel bernama cout
dalam file itu. Bisa apa saja.
Sekarang ambil basis kode sejuta baris, yang tidak terlalu besar, dan Anda sedang mencari bug, yang berarti Anda tahu ada satu baris dalam satu juta baris ini yang tidak melakukan apa yang seharusnya dilakukannya. cout << 1;
bisa membaca sebuah static int
nama cout
, menggesernya sedikit ke kiri, dan membuang hasilnya. Mencari bug, saya harus memeriksanya. Dapatkah Anda melihat bagaimana saya benar-benar lebih suka melihat std::cout
?
Salah satu hal berikut ini yang tampaknya merupakan ide yang sangat bagus jika Anda seorang guru dan tidak pernah menulis dan memelihara kode apa pun untuk mencari nafkah. Saya suka melihat kode di mana (1) saya tahu apa fungsinya; dan, (2) Saya yakin bahwa orang yang menulisnya tahu apa yang dilakukannya.
Ini semua tentang mengelola kompleksitas. Menggunakan namespace akan menarik hal-hal yang tidak Anda inginkan, dan dengan demikian mungkin membuatnya lebih sulit untuk di-debug (saya katakan mungkin). Menggunakan std :: di semua tempat lebih sulit untuk dibaca (lebih banyak teks dan semua itu).
Kuda untuk kursus - kelola kerumitan Anda sebaik mungkin dan merasa mampu.
Mempertimbangkan
// 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 penyertaan dan impor lainnya, Anda akan memiliki banyak dependensi yang harus dilalui untuk mencari tahu masalahnya. Hal yang lebih buruk tentang itu adalah Anda bisa mendapatkan kesalahan yang tidak terkait di modul lain tergantung pada definisi yang bertentangan.
Ini tidak mengerikan, tetapi Anda akan menghindari sakit kepala dengan tidak menggunakannya di file header atau namespace global. Mungkin tidak apa-apa untuk melakukannya dalam cakupan yang sangat terbatas, tetapi saya tidak pernah mengalami masalah mengetik lima karakter tambahan untuk mengklarifikasi dari mana fungsi saya berasal.
Contoh konkret untuk memperjelas kekhawatiran. Bayangkan Anda memiliki situasi di mana Anda memiliki dua perpustakaan, foo
dan bar
, masing-masing dengan namespace mereka sendiri:
namespace foo {
void a(float) { /* Does something */ }
}
namespace bar {
...
}
Sekarang katakanlah 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, program itu 'Melakukan sesuatu'. Tapi nanti Anda update bar
dan katakanlah sudah berubah menjadi seperti:
namespace bar {
void a(float) { /* Does something completely different */ }
}
Pada titik ini Anda akan 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 perlu melakukan pemeliharaan untuk mengklarifikasi bahwa 'a' yang dimaksud foo::a
. Itu tidak diinginkan, tetapi untungnya itu cukup mudah (cukup tambahkan foo::
di depan semua panggilan ke a
kompiler yang tandai sebagai ambigu).
Tapi bayangkan skenario alternatif di mana bilah berubah menjadi seperti ini:
namespace bar {
void a(int) { /* Does something completely different */ }
}
Pada titik ini panggilan Anda untuk a(42)
tiba - tiba mengikat bar::a
alih- foo::a
alih melakukan 'sesuatu', ia melakukan 'sesuatu yang sama sekali berbeda'. Tidak ada peringatan kompiler atau apapun. Program Anda diam-diam mulai melakukan sesuatu yang berbeda dari sebelumnya.
Saat Anda menggunakan namespace, Anda mempertaruhkan skenario seperti ini, itulah sebabnya orang tidak nyaman menggunakan namespace. Semakin banyak hal di namespace, semakin besar risiko konflik, sehingga orang mungkin lebih tidak nyaman menggunakan namespace std
(karena jumlah hal di namespace itu) daripada namespace lain.
Pada akhirnya, ini adalah trade-off antara kemampuan menulis vs. keandalan / pemeliharaan. Keterbacaan juga bisa menjadi faktor, tapi saya bisa melihat argumen untuk itu. Biasanya saya akan mengatakan keandalan dan pemeliharaan lebih penting, tetapi dalam kasus ini Anda akan terus membayar biaya menulis untuk dampak keandalan / pemeliharaan yang cukup langka. Pertukaran 'terbaik' akan menentukan proyek dan prioritas Anda.
Menggunakan banyak namespace pada saat yang sama jelas merupakan resep untuk bencana, tetapi menggunakan namespace JUST std
dan hanya namespace std
bukanlah masalah besar menurut saya karena redefinisi hanya dapat terjadi oleh kode Anda sendiri ...
Jadi anggap saja mereka berfungsi sebagai nama yang dicadangkan seperti "int" atau "class" dan hanya itu.
Orang harus berhenti bersikap anal tentang hal itu. Guru Anda selama ini benar. Cukup gunakan SATU namespace; itulah inti dari penggunaan namespace di tempat pertama. Anda tidak boleh menggunakan lebih dari satu pada saat yang bersamaan. Kecuali itu milik Anda sendiri. Jadi sekali lagi, redefinisi tidak akan terjadi.
Anda harus dapat membaca kode yang ditulis oleh orang-orang yang memiliki gaya dan opini praktik terbaik yang berbeda dari Anda.
Jika Anda hanya menggunakan
cout
, tidak ada yang bingung. Tetapi ketika Anda memiliki banyak namespace berkeliaran dan Anda melihat kelas ini dan Anda tidak yakin apa fungsinya, memiliki namespace eksplisit bertindak sebagai semacam komentar. Anda dapat melihat sekilas, "oh, ini adalah operasi sistem file" atau "melakukan hal-hal jaringan".
Saya setuju dengan yang lain di sini, tetapi saya ingin membahas kekhawatiran tentang keterbacaan - Anda dapat menghindari semua itu hanya dengan menggunakan typedefs di bagian atas file, fungsi, atau deklarasi kelas Anda.
Saya biasanya menggunakannya dalam deklarasi kelas saya karena metode di kelas cenderung berurusan dengan tipe data yang serupa (anggota) dan typedef adalah kesempatan untuk menetapkan nama yang bermakna dalam konteks kelas. Ini sebenarnya membantu keterbacaan dalam definisi metode kelas.
// Header
class File
{
typedef std::vector<std::string> Lines;
Lines ReadLines();
}
dan dalam implementasinya:
// .cpp
Lines File::ReadLines()
{
Lines lines;
// Get them...
return lines;
}
sebagai lawan:
// .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;
}
Namespace adalah lingkup bernama. Namespaces digunakan untuk mengelompokkan deklarasi terkait dan untuk memisahkan item yang terpisah. Misalnya, dua pustaka yang dikembangkan secara terpisah dapat menggunakan nama yang sama untuk merujuk ke item yang berbeda, tetapi 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 bisa menjadi gangguan bagi pembaca dan penulis. Akibatnya, dimungkinkan untuk menyatakan bahwa nama dari namespace tertentu tersedia tanpa kualifikasi eksplisit. Sebagai contoh:
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 pustaka yang berbeda dan versi kode yang berbeda. Secara khusus, mereka menawarkan alternatif programmer tentang bagaimana eksplisit membuat referensi ke nama nonlokal.
Sumber: Gambaran Umum Bahasa Pemrograman C ++ oleh Bjarne Stroustrup
Contoh di mana using namespace std
melontarkan kesalahan kompilasi karena ambiguitas hitungan, yang juga merupakan fungsi dalam pustaka algoritme.
#include <iostream>
#include <algorithm>
using namespace std;
int count = 1;
int main() {
cout << count << endl;
}
Itu tidak membuat perangkat lunak atau kinerja proyek Anda menjadi lebih buruk. Dimasukkannya namespace di awal kode sumber Anda tidak buruk. Dimasukkannya using namespace std
instruksi bervariasi sesuai dengan kebutuhan Anda dan cara Anda mengembangkan perangkat lunak atau proyek.
The namespace std
berisi fungsi standar C ++ dan variabel. Namespace ini berguna jika Anda sering menggunakan fungsi standar C ++.
Seperti yang disebutkan di halaman ini :
Pernyataan menggunakan namespace std umumnya dianggap praktik yang buruk. Alternatif untuk pernyataan ini adalah menentukan namespace yang mana pengenalnya menggunakan operator scope (: :) setiap kali kita mendeklarasikan sebuah tipe.
Dan lihat pendapat ini :
Tidak ada masalah menggunakan "using namespace std" di file sumber Anda saat Anda menggunakan namespace secara berlebihan dan tahu pasti bahwa tidak ada yang akan bertabrakan.
Beberapa orang mengatakan itu adalah praktik yang buruk untuk memasukkan using namespace std
dalam file sumber Anda karena Anda memanggil semua fungsi dan variabel dari namespace itu. Ketika Anda ingin mendefinisikan sebuah fungsi baru dengan nama yang sama dengan fungsi lain yang ada di dalamnya, namespace std
Anda akan membebani fungsi tersebut dan itu bisa menimbulkan masalah karena kompilasi atau eksekusi. Itu tidak akan dikompilasi atau dijalankan seperti yang Anda harapkan.
Seperti yang disebutkan di halaman ini :
Meskipun pernyataan tersebut menyelamatkan kita dari mengetik std :: kapan pun kita ingin mengakses kelas atau tipe yang ditentukan di namespace std, ia mengimpor keseluruhan namespace std ke dalam namespace program saat ini. Mari kita ambil beberapa contoh untuk memahami mengapa ini mungkin bukan hal yang baik
...
Sekarang di tahap pengembangan selanjutnya, kami ingin menggunakan versi lain dari cout yang diimplementasikan secara kustom di beberapa library yang disebut "foo" (misalnya)
...
Perhatikan bagaimana ada ambiguitas, ke perpustakaan mana cout menunjuk? Kompilator dapat mendeteksi ini dan tidak mengkompilasi program. Dalam kasus terburuk, program mungkin masih mengkompilasi tetapi memanggil fungsi yang salah, karena kita tidak pernah menentukan namespace tempat pengenal itu berada.
Saya tidak berpikir ini adalah praktik yang buruk dalam semua kondisi, tetapi Anda harus berhati-hati saat menggunakannya. Jika Anda sedang menulis perpustakaan, Anda mungkin harus menggunakan operator resolusi cakupan dengan namespace untuk menjaga perpustakaan Anda agar tidak berselisih dengan perpustakaan lain. Untuk kode level aplikasi, saya tidak melihat ada yang salah dengannya.
Saya setuju dengan orang lain - ini meminta pertentangan nama, ambiguitas, dan kemudian faktanya kurang eksplisit. Meskipun saya dapat melihat penggunaannya using
, preferensi pribadi saya adalah membatasinya. Saya juga akan sangat mempertimbangkan apa yang beberapa orang lain tunjukkan:
Jika Anda ingin mencari nama fungsi yang mungkin merupakan nama yang cukup umum, tetapi Anda hanya ingin menemukannya di std
namespace (atau sebaliknya - Anda ingin mengubah semua panggilan yang tidak ada di namespace std
, namespace X
, ...), lalu bagaimana Anda mengusulkan untuk melakukan ini?
Anda dapat menulis program untuk melakukannya, tetapi bukankah lebih baik menghabiskan waktu mengerjakan proyek Anda sendiri daripada menulis program untuk mempertahankan proyek Anda?
Secara pribadi, saya sebenarnya tidak keberatan dengan std::
awalannya. Saya lebih suka penampilannya daripada tidak memilikinya. Saya tidak tahu apakah itu karena eksplisit dan mengatakan kepada saya "ini bukan kode saya ... Saya menggunakan pustaka standar" atau apakah itu sesuatu yang lain, tapi saya pikir itu terlihat lebih bagus. Ini mungkin aneh mengingat saya baru saja masuk ke C ++ (digunakan dan masih menggunakan C dan bahasa lain lebih lama dan C adalah bahasa favorit saya sepanjang masa, tepat di atas assembly).
Ada satu hal lain meskipun agak terkait dengan hal di atas dan apa yang ditunjukkan orang lain. Meskipun ini mungkin praktik yang buruk, terkadang saya memesan std::name
versi pustaka standar dan nama untuk implementasi khusus program. Ya, memang ini bisa menggigit Anda dan menggigit Anda dengan keras, tetapi semuanya bermuara pada itulah saya memulai proyek ini dari awal, dan saya satu-satunya programmer untuk itu. Contoh: Saya membebani std::string
dan menyebutnya string
. Saya memiliki tambahan yang bermanfaat. Saya melakukannya sebagian karena kecenderungan C dan Unix (+ Linux) saya terhadap nama huruf kecil.
Selain itu, Anda dapat memiliki alias namespace. Berikut adalah contoh di mana berguna yang mungkin belum dirujuk. Saya menggunakan standar C ++ 11 dan secara khusus dengan libstdc ++. Ya, itu tidak memiliki std::regex
dukungan lengkap . Tentu, itu mengkompilasi, tetapi itu melempar pengecualian sepanjang baris itu menjadi kesalahan di pihak pemrogram. Tapi itu kurang implementasinya.
Jadi, inilah cara saya menyelesaikannya. Instal regex Boost, dan tautkan. Kemudian, saya melakukan hal berikut sehingga ketika libstdc ++ telah menerapkannya sepenuhnya, saya hanya perlu menghapus blok ini dan kodenya tetap sama:
namespace std
{
using boost::regex;
using boost::regex_error;
using boost::regex_replace;
using boost::regex_search;
using boost::regex_match;
using boost::smatch;
namespace regex_constants = boost::regex_constants;
}
Saya tidak akan berdebat apakah itu ide yang buruk atau tidak. Namun saya akan berpendapat bahwa itu tetap bersih untuk proyek saya dan pada saat yang sama membuatnya spesifik: Benar, saya harus menggunakan Boost, tetapi saya menggunakannya seperti libstdc ++ pada akhirnya akan memilikinya. Ya, memulai proyek Anda sendiri dan memulai dengan standar (...) di awal sangat membantu dalam membantu pemeliharaan, pengembangan, dan semua yang terkait dengan proyek!
Hanya untuk memperjelas sesuatu: Saya tidak benar-benar berpikir itu adalah ide yang baik untuk menggunakan nama kelas / apapun di STL dengan sengaja dan lebih spesifik sebagai gantinya. String adalah pengecualian (abaikan yang pertama, di atas, atau kedua di sini, permainan kata-kata jika Anda harus) untuk saya karena saya tidak menyukai gagasan 'String'.
Saat ini, saya masih sangat bias terhadap C dan bias terhadap C ++. Membiarkan detail, banyak dari apa yang saya kerjakan lebih cocok untuk C (tapi itu adalah latihan yang baik dan cara yang baik untuk membuat diri saya sendiri a. Belajar bahasa lain dan b. Mencoba untuk tidak kurang bias terhadap objek / kelas / dll yang mungkin lebih baik dinyatakan sebagai kurang berpikiran tertutup, kurang sombong, dan lebih menerima.). Tetapi apa yang berguna adalah apa yang sudah disarankan beberapa: Saya memang menggunakan daftar (ini cukup umum, bukan?), Dan mengurutkan (hal yang sama) untuk menyebutkan dua yang akan menyebabkan nama bentrok jika saya melakukannya using namespace std;
, dan begitu untuk itu saya lebih suka menjadi spesifik, dalam kendali dan mengetahui bahwa jika saya bermaksud untuk menjadi penggunaan standar maka saya harus menentukannya. Sederhananya: asumsi tidak diperbolehkan.
Dan untuk membuat regex Boost menjadi bagian dari std
. Saya melakukannya untuk integrasi di masa mendatang dan - sekali lagi, saya akui sepenuhnya bahwa ini bias - menurut saya tidak seburuk itu boost::regex:: ...
. Memang, itu adalah hal lain bagi saya. Ada banyak hal di C ++ yang masih belum saya terima sepenuhnya dalam tampilan dan metode (contoh lain: template variadic versus argumen var [meskipun saya akui template variadic sangat berguna!]). Bahkan mereka yang saya terima pun sulit, dan saya masih memiliki masalah dengan mereka.
Dari pengalaman saya, jika Anda memiliki beberapa perpustakaan yang menggunakan katakanlah cout
, tetapi untuk tujuan yang berbeda Anda mungkin menggunakan yang salah cout
.
Misalnya, jika saya mengetik, using namespace std;
dan using namespace otherlib;
dan mengetik hanya cout
(yang kebetulan ada di keduanya), daripada std::cout
(atau 'otherlib::cout'
), Anda mungkin menggunakan yang salah, dan mendapatkan kesalahan. Jauh lebih efektif dan efisien untuk digunakan std::cout
.
Ini kasus per kasus. Kami ingin meminimalkan "biaya total kepemilikan" dari perangkat lunak selama masa pakainya. Menyatakan "menggunakan namespace std" memiliki beberapa biaya, tetapi tidak menggunakannya juga memiliki biaya dalam keterbacaan.
Orang-orang dengan tepat menunjukkan bahwa ketika menggunakannya, ketika pustaka standar memperkenalkan simbol dan definisi baru, kode Anda berhenti dikompilasi, dan Anda mungkin dipaksa untuk mengganti nama variabel. Namun ini mungkin bagus untuk jangka panjang, karena pengelola di masa mendatang akan bingung atau terganggu untuk sementara jika Anda menggunakan kata kunci untuk tujuan yang mengejutkan.
Anda tidak ingin memiliki template yang disebut vektor, misalnya, yang bukan vektor yang diketahui oleh orang lain. Dan jumlah definisi baru yang diperkenalkan di pustaka C ++ cukup kecil sehingga mungkin tidak muncul begitu saja. Ada adalah biaya untuk harus melakukan jenis perubahan, tetapi biaya tidak tinggi dan diimbangi dengan kejelasan yang diperoleh dengan tidak menggunakan std
nama simbol untuk tujuan lain.
Mengingat jumlah kelas, variabel, dan fungsi, menyatakan std::
pada setiap kelas mungkin akan meningkatkan kode Anda hingga 50% dan membuatnya lebih sulit untuk dipahami. Algoritme atau langkah dalam metode yang dapat diambil dalam satu layar penuh kode sekarang memerlukan pengguliran bolak-balik untuk mengikutinya. Ini adalah biaya yang nyata. Bisa dibilang ini bukan biaya tinggi, tetapi orang yang menyangkal keberadaannya tidak berpengalaman, dogmatis, atau salah.
Saya akan menawarkan aturan berikut:
std
berbeda dari semua perpustakaan lainnya. Ini adalah satu-satunya perpustakaan yang pada dasarnya perlu diketahui semua orang, dan menurut pandangan saya paling baik dianggap sebagai bagian dari bahasa. Secara umum ada kasus yang sangat baikusing namespace std
bahkan jika tidak ada perpustakaan lain.Jangan pernah memaksakan keputusan kepada pembuat unit kompilasi (file .cpp) dengan meletakkan ini
using
di header. Selalu serahkan keputusan kepada penulis unit kompilasi. Bahkan dalam proyek yang telah memutuskan untuk digunakan diusing namespace std
mana-mana mungkin baik-baik saja beberapa modul yang paling baik ditangani sebagai pengecualian untuk aturan itu.Meskipun fitur namespace memungkinkan Anda memiliki banyak modul dengan simbol yang didefinisikan sama, itu akan membingungkan untuk melakukannya. Jaga agar nama-nama itu berbeda sejauh mungkin. Bahkan jika tidak menggunakan fitur namespace, jika Anda memiliki kelas bernama
foo
danstd
memperkenalkan kelas bernamafoo
, mungkin lebih baik jangka panjang untuk mengganti nama kelas Anda.Alternatif untuk menggunakan namespace adalah dengan menggunakan simbol namespace secara manual dengan mengawalnya. Saya memiliki dua perpustakaan yang telah saya gunakan selama beberapa dekade, keduanya dimulai sebagai perpustakaan C, sebenarnya, di mana setiap simbol diawali dengan "AK" atau "SCWin". Secara umum, ini seperti menghindari konstruksi "menggunakan", tetapi Anda tidak menulis titik dua kembar.
AK::foo()
adalah sebagai gantinyaAKFoo()
. Itu membuat kode 5-10% lebih padat dan lebih sedikit bertele-tele, dan satu-satunya downside adalah Anda akan mendapat masalah besar jika Anda harus menggunakan dua pustaka yang memiliki awalan yang sama. Perhatikan pustaka X Window sangat bagus dalam hal ini, kecuali mereka lupa melakukannya dengan beberapa #defines: TRUE dan FALSE seharusnya XTRUE dan XFALSE, dan ini mengatur namespace bentrok dengan Sybase atau Oracle yang juga menggunakan TRUE dan FALSE dengan nilai yang berbeda! (ASCII 0 dan 1 dalam kasus database!) Satu keuntungan khusus dari ini adalah bahwa hal ini berlaku untuk definisi preprocessor, sedangkan C ++using
/namespace
sistem tidak menanganinya. Manfaat yang bagus dari hal ini adalah memberikan kemiringan organik dari menjadi bagian dari proyek menjadi perpustakaan. Dalam aplikasi besar saya, semua kelas jendela diawaliWin
, semua modul pemrosesan sinyal Mod, dan seterusnya. Ada sedikit kemungkinan salah satu dari ini digunakan kembali sehingga tidak ada manfaat praktis untuk menjadikan setiap kelompok menjadi perpustakaan, tetapi dalam beberapa detik akan terlihat jelas bagaimana proyek ini dibagi menjadi subproyek.
Dengan pengenal impor yang tidak memenuhi syarat, Anda memerlukan alat pencarian eksternal seperti grep untuk mengetahui di mana pengenal dideklarasikan. Hal ini membuat penalaran tentang kebenaran program menjadi lebih sulit.
Itu tergantung di mana lokasinya. Jika ini adalah tajuk umum, maka Anda mengurangi nilai namespace dengan menggabungkannya ke dalam namespace global. Perlu diingat, ini bisa menjadi cara yang rapi untuk membuat modul global.
Ini adalah praktik yang buruk, sering dikenal sebagai polusi ruang nama global. Masalah dapat terjadi ketika lebih dari satu namespace memiliki nama fungsi yang sama dengan tanda tangan, maka akan menjadi ambigu bagi compiler untuk memutuskan mana yang akan dipanggil dan ini semua dapat dihindari ketika Anda menentukan namespace dengan pemanggilan fungsi Anda seperti std::cout
. Semoga ini membantu. :)
Untuk menjawab pertanyaan Anda, saya melihatnya secara praktis: banyak programmer (tidak semua) memanggil namespace std. Oleh karena itu seseorang harus membiasakan diri untuk TIDAK menggunakan hal-hal yang melanggar atau menggunakan nama yang sama seperti yang ada di namespace std. Itu banyak yang diberikan, tetapi tidak terlalu banyak dibandingkan dengan jumlah kemungkinan kata dan nama samaran yang koheren yang dapat dihasilkan dengan berbicara secara tegas.
Maksud saya benar-benar ... mengatakan "jangan mengandalkan keberadaan ini" hanya membuat Anda mengandalkannya TIDAK hadir. Anda akan terus-menerus mengalami masalah saat meminjam cuplikan kode dan terus-menerus memperbaikinya. Simpan saja hal-hal yang ditentukan pengguna dan dipinjam dalam ruang lingkup terbatas sebagaimana mestinya dan SANGAT hemat dengan yang global (sejujurnya, global hampir selalu menjadi pilihan terakhir untuk tujuan "kompilasi sekarang, kewarasan nanti"). Sungguh saya pikir ini adalah nasihat yang buruk dari guru Anda karena menggunakan std akan bekerja untuk "cout" dan "std :: cout" tetapi TIDAK menggunakan std hanya akan berfungsi untuk "std :: cout". Anda tidak akan selalu cukup beruntung untuk menulis semua kode Anda sendiri.
CATATAN: Jangan terlalu fokus pada masalah efisiensi sampai Anda benar-benar belajar sedikit tentang cara kerja kompiler. Dengan sedikit pengalaman coding, Anda tidak perlu belajar banyak tentang mereka sebelum Anda menyadari betapa mereka mampu menggeneralisasi kode yang baik menjadi sesuatu yang sederhana. Setiap bit sesederhana jika Anda menulis semuanya dalam C. Kode yang baik hanya serumit yang seharusnya.