Namespace (C++)
Namespace adalah wilayah deklaratif yang menyediakan cakupan untuk pengidentifikasi (nama jenis, fungsi, variabel, dll) di dalamnya. Namespace digunakan untuk menata kode ke dalam grup logis dan untuk mencegah tabrakan nama yang dapat terjadi terutama ketika basis kode Anda menyertakan beberapa pustaka. Semua pengidentifikasi di cakupan namespace terlihat satu sama lain tanpa kualifikasi. Pengidentifikasi di luar namespace layanan dapat mengakses anggota dengan menggunakan nama yang sepenuhnya memenuhi syarat untuk setiap pengidentifikasi, misalnya std::vector<std::string> vec;
, atau dengan menggunakan Deklarasi untuk satu pengidentifikasi (using std::string
), atau menggunakan Direktif untuk semua pengidentifikasi di namespace (using namespace std;
). Kode dalam file header harus selalu menggunakan nama namespace yang sepenuhnya memenuhi syarat.
Contoh berikut menunjukkan deklarasi namespace layanan dan tiga cara kode di luar namespace layanan dapat mengakses anggotanya.
namespace ContosoData
{
class ObjectManager
{
public:
void DoSomething() {}
};
void Func(ObjectManager) {}
}
Gunakan nama yang sepenuhnya memenuhi syarat:
ContosoData::ObjectManager mgr;
mgr.DoSomething();
ContosoData::Func(mgr);
Gunakan deklarasi penggunaan untuk membawa satu pengidentifikasi ke dalam cakupan:
using ContosoData::ObjectManager;
ObjectManager mgr;
mgr.DoSomething();
Gunakan direktif menggunakan untuk membawa semuanya dalam namespace ke dalam cakupan:
using namespace ContosoData;
ObjectManager mgr;
mgr.DoSomething();
Func(mgr);
menggunakan direktif
Direktif using
memungkinkan semua nama dalam digunakan namespace
tanpa namespace-name sebagai kualifikasi eksplisit. Gunakan direktif penggunaan dalam file implementasi (yaitu *.cpp) jika Anda menggunakan beberapa pengidentifikasi yang berbeda di namespace layanan; jika Anda hanya menggunakan satu atau dua pengidentifikasi, maka pertimbangkan untuk menggunakan deklarasi untuk hanya membawa pengidentifikasi tersebut ke dalam cakupan dan tidak semua pengidentifikasi di namespace. Jika variabel lokal memiliki nama yang sama dengan variabel namespace, variabel namespace disembunyikan. Ini adalah kesalahan untuk memiliki variabel namespace dengan nama yang sama dengan variabel global.
Catatan
Direktif menggunakan dapat ditempatkan di bagian atas file .cpp (pada cakupan file), atau di dalam definisi kelas atau fungsi.
Secara umum, hindari menggunakan direktif dalam file header (*.h) karena file apa pun yang menyertakan header tersebut akan membawa semuanya dalam namespace ke dalam cakupan, yang dapat menyebabkan persembunyian nama dan masalah tabrakan nama yang sangat sulit untuk di-debug. Selalu gunakan nama yang sepenuhnya memenuhi syarat dalam file header. Jika nama-nama tersebut terlalu panjang, Anda dapat menggunakan alias namespace untuk mempersingkatnya. (Lihat di bawah ini.)
Mendeklarasikan namespace layanan dan anggota namespace
Biasanya, Anda mendeklarasikan namespace dalam file header. Jika implementasi fungsi Anda berada dalam file terpisah, maka memenuhi syarat nama fungsi, seperti dalam contoh ini.
// contosoData.h
#pragma once
namespace ContosoDataServer
{
void Foo();
int Bar();
}
Implementasi fungsi dalam contosodata.cpp harus menggunakan nama yang sepenuhnya memenuhi syarat, bahkan jika Anda menempatkan using
direktif di bagian atas file:
#include "contosodata.h"
using namespace ContosoDataServer;
void ContosoDataServer::Foo() // use fully-qualified name here
{
// no qualification needed for Bar()
Bar();
}
int ContosoDataServer::Bar(){return 0;}
Namespace dapat dideklarasikan dalam beberapa blok dalam satu file, dan dalam beberapa file. Kompiler menggabungkan bagian-bagian bersama-sama selama praproscessing dan namespace yang dihasilkan berisi semua anggota yang dideklarasikan di semua bagian. Contohnya adalah namespace std yang dideklarasikan dalam setiap file header di pustaka standar.
Anggota namespace bernama dapat didefinisikan di luar namespace layanan tempat mereka dideklarasikan oleh kualifikasi eksplisit nama yang ditentukan. Namun, definisi harus muncul setelah titik deklarasi di namespace yang menyertakan namespace deklarasi. Contohnya:
// defining_namespace_members.cpp
// C2039 expected
namespace V {
void f();
}
void V::f() { } // ok
void V::g() { } // C2039, g() is not yet a member of V
namespace V {
void g();
}
Kesalahan ini dapat terjadi ketika anggota namespace dideklarasikan di beberapa file header, dan Anda belum menyertakan header tersebut dalam urutan yang benar.
Namespace global
Jika pengidentifikasi tidak dideklarasikan dalam namespace eksplisit, itu adalah bagian dari namespace layanan global implisit. Secara umum, cobalah untuk menghindari membuat deklarasi pada cakupan global jika memungkinkan, kecuali untuk Fungsi utama titik masuk, yang diperlukan untuk berada di namespace global. Untuk secara eksplisit memenuhi syarat pengidentifikasi global, gunakan operator resolusi cakupan tanpa nama, seperti dalam ::SomeFunction(x);
. Ini akan membedakan pengidentifikasi dari apa pun dengan nama yang sama di namespace layanan lain, dan juga akan membantu membuat kode Anda lebih mudah dipahami orang lain.
Namespace std
Semua jenis dan fungsi pustaka standar C++ dideklarasikan di std
namespace layanan atau namespace layanan yang disarangkan di dalam std
.
Namespace berlapis
Namespace mungkin ditumpuk. Namespace berlapis biasa memiliki akses yang tidak memenuhi syarat ke anggota induknya, tetapi anggota induk tidak memiliki akses yang tidak memenuhi syarat ke namespace berlapis (kecuali dinyatakan sebagai sebaris), seperti yang ditunjukkan dalam contoh berikut:
namespace ContosoDataServer
{
void Foo();
namespace Details
{
int CountImpl;
void Ban() { return Foo(); }
}
int Bar(){...};
int Baz(int i) { return Details::CountImpl; }
}
Namespace berlapis biasa dapat digunakan untuk merangkum detail implementasi internal yang bukan bagian dari antarmuka publik namespace induk.
Namespace sebaris (C++11)
Berbeda dengan namespace layanan berlapis biasa, anggota namespace sebaris diperlakukan sebagai anggota namespace induk. Karakteristik ini memungkinkan pencarian dependen argumen pada fungsi yang kelebihan beban untuk bekerja pada fungsi yang memiliki kelebihan beban dalam induk dan namespace sebaris berlapis. Ini juga memungkinkan Anda untuk mendeklarasikan spesialisasi di namespace induk untuk templat yang dideklarasikan dalam namespace sebaris. Contoh berikut menunjukkan bagaimana kode eksternal mengikat ke namespace sebaris secara default:
// Header.h
#include <string>
namespace Test
{
namespace old_ns
{
std::string Func() { return std::string("Hello from old"); }
}
inline namespace new_ns
{
std::string Func() { return std::string("Hello from new"); }
}
}
// main.cpp
#include "header.h"
#include <string>
#include <iostream>
int main()
{
using namespace Test;
using namespace std;
string s = Func();
std::cout << s << std::endl; // "Hello from new"
return 0;
}
Contoh berikut menunjukkan bagaimana Anda bisa mendeklarasikan spesialisasi dalam induk templat yang dideklarasikan dalam namespace sebaris:
namespace Parent
{
inline namespace new_ns
{
template <typename T>
struct C
{
T member;
};
}
template<>
class C<int> {};
}
Anda dapat menggunakan namespace sebaris sebagai mekanisme penerapan versi untuk mengelola perubahan pada antarmuka publik pustaka. Misalnya, Anda dapat membuat namespace induk tunggal, dan merangkum setiap versi antarmuka di namespace layanannya sendiri yang disarangkan di dalam induk. Namespace layanan yang menyimpan versi terbaru atau pilihan memenuhi syarat sebagai sebaris, dan karenanya diekspos seolah-olah itu adalah anggota langsung dari namespace induk. Kode klien yang memanggil Parent::Class akan secara otomatis mengikat kode baru. Klien yang lebih suka menggunakan versi lama masih dapat mengaksesnya dengan menggunakan jalur yang sepenuhnya memenuhi syarat ke namespace berlapis yang memiliki kode tersebut.
Kata kunci sebaris harus diterapkan ke deklarasi pertama namespace dalam unit kompilasi.
Contoh berikut menunjukkan dua versi antarmuka, masing-masing di namespace berlapis. Namespace v_20
memiliki beberapa modifikasi dari v_10
antarmuka dan ditandai sebagai sebaris. Kode klien yang menggunakan pustaka dan panggilan Contoso::Funcs::Add
baru akan memanggil versi v_20. Kode yang mencoba memanggil Contoso::Funcs::Divide
sekarang akan mendapatkan kesalahan waktu kompilasi. Jika mereka benar-benar membutuhkan fungsi itu v_10
, mereka masih dapat mengakses versi dengan secara eksplisit memanggil Contoso::v_10::Funcs::Divide
.
namespace Contoso
{
namespace v_10
{
template <typename T>
class Funcs
{
public:
Funcs(void);
T Add(T a, T b);
T Subtract(T a, T b);
T Multiply(T a, T b);
T Divide(T a, T b);
};
}
inline namespace v_20
{
template <typename T>
class Funcs
{
public:
Funcs(void);
T Add(T a, T b);
T Subtract(T a, T b);
T Multiply(T a, T b);
std::vector<double> Log(double);
T Accumulate(std::vector<T> nums);
};
}
}
Alias namespace
Nama namespace harus unik, yang berarti seringkali nama tersebut tidak boleh terlalu pendek. Jika panjang nama membuat kode sulit dibaca, atau melelahkan untuk mengetik dalam file header di mana menggunakan direktif tidak dapat digunakan, maka Anda dapat membuat alias namespace layanan yang berfungsi sebagai singkatan untuk nama yang sebenarnya. Contohnya:
namespace a_very_long_namespace_name { class Foo {}; }
namespace AVLNN = a_very_long_namespace_name;
void Bar(AVLNN::Foo foo){ }
namespace anonim atau tidak bernama
Anda dapat membuat namespace eksplisit tetapi tidak memberinya nama:
namespace
{
int MyFunc(){}
}
Ini disebut namespace yang tidak disebut nama atau anonim dan berguna ketika Anda ingin membuat deklarasi variabel yang tidak terlihat oleh kode dalam file lain (yaitu memberi mereka tautan internal) tanpa harus membuat namespace bernama. Semua kode dalam file yang sama dapat melihat pengidentifikasi di namespace yang tidak disebutkan namanya tetapi pengidentifikasi, bersama dengan namespace itu sendiri, tidak terlihat di luar file tersebut—atau lebih tepatnya di luar unit terjemahan.