C ++: Namespaces - Bagaimana cara menggunakan file header dan source dengan benar?

90

Pertimbangkan sepasang dua file sumber: file deklarasi antarmuka ( *.hatau *.hpp) dan file implementasinya ( *.cpp).

Biarkan *.hfilenya seperti berikut:

namespace MyNamespace {
  class MyClass {
  public:
    int foo();
  };
}

Saya telah melihat dua praktik berbeda untuk menggunakan ruang nama di file sumber:

*.cpp menunjukkan latihan # 1:

#include "MyClass.h"
using namespace MyNamespace;

int MyClass::foo() { ... }

*.cpp menunjukkan latihan # 2:

#include "MyClass.h"
namespace MyNamespace {

  int MyClass::foo() { ... }

}

Pertanyaan saya: Apakah ada perbedaan antara kedua praktik ini dan apakah yang satu dianggap lebih baik dari yang lain?

nickolay
sumber
30
Ada juga opsi 3: Hanya kami nama lengkapnya, mis int MyNamespace::MyClass::foo() ....
Benjamin Bannier
1
Kemungkinan duplikat: stackoverflow.com/questions/7789163/…
David
@Dave tidak menduplikasi. Pertanyaan-pertanyaan ini saling melengkapi. Sarankan untuk menambahkan tautan yang disediakan oleh Dave sebagai "Baca juga ..." ke pertanyaan ini. Pertanyaan saya akan membantu pemula untuk memilih gaya yang benar.
nickolay
Kemungkinan duplikat: stackoverflow.com/questions/8210935/…
Firedragon

Jawaban:

65

Dari sudut pandang keterbacaan kode, mungkin lebih baik menurut saya menggunakan metode # 2 karena alasan ini:

Anda dapat memiliki usingbeberapa ruang nama sekaligus, dan objek atau fungsi apa pun yang ditulis di bawah baris tersebut dapat termasuk dalam salah satu ruang nama tersebut (kecuali konflik penamaan). Membungkus seluruh file dalam satu namespaceblok lebih eksplisit, dan memungkinkan Anda untuk mendeklarasikan fungsi dan variabel baru yang termasuk dalam namespace tersebut dalam file .cpp juga.

Dan F
sumber
Pertanyaan yang ditautkan Dave dalam komentarnya ke pertanyaan Anda juga menguraikan beberapa poin penting dalam perbedaan (jika ada) antara kedua metode yang Anda lihat
Dan F
Teman-teman, saya benar-benar tidak tahu jawaban siapa yang harus dipilih. Mereka memiliki persimpangan sambil saling melengkapi.
nickolay
Hanya berkomentar untuk mengetahui bahwa beberapa IDE seperti CLion hanya akan mendeteksi implementasi jika Anda menggunakan opsi / praktik # 2.
pedrostanaka
@PedroTanaka apakah masih demikian? Saya tidak melihat adanya masalah seperti itu.
John McFarlane
@JMcF Saya belum memeriksanya sejak saya menerbitkan komentar. Dalam versi awal Clion, masalah terjadi.
pedrostanaka
52

Yang paling jelas adalah opsi yang tidak Anda tampilkan:

int MyNamespace::MyClass::foo()
{
    //  ...
}

Ini juga sangat bertele-tele; terlalu banyak bagi kebanyakan orang. Karena using namespaceini adalah resep untuk konflik nama, setidaknya menurut pengalaman saya, dan harus dihindari kecuali dalam cakupan dan tempat yang sangat terbatas, saya biasanya menggunakan # 2 Anda.

James Kanze
sumber
4
Terima kasih sangat jelas. Bersama-sama kami membuat halaman FAQ yang bagus untuk pengguna namespace. :)
nickolay
2
Teman-teman, saya benar-benar tidak tahu jawaban siapa yang harus dipilih. Mereka memiliki persimpangan sambil saling melengkapi.
nickolay
10

Apakah ada perbedaan antara kedua praktik ini

Iya. # 1 dan # 2 masing-masing adalah contoh dari penggunaan-direktif dan definisi namespace . Mereka secara efektif sama dalam kasus ini tetapi memiliki konsekuensi lain. Misalnya, jika Anda memperkenalkan pengenal baru di sampingnya MyClass::foo, ini akan memiliki cakupan yang berbeda:

# 1:

using namespace MyNamespace;
int x;  // defines ::x

# 2:

namespace MyNamespace {
  int x;  // defines MyNamespace::x
}

apakah yang satu dianggap lebih baik dari yang lain?

# 1 Kelebihan: sedikit lebih singkat; lebih sulit untuk secara tidak sengaja memasukkan sesuatu ke dalam MyNamespacetanpa disadari. Kekurangan: dapat menarik pengenal yang ada secara tidak sengaja.

# 2 Kelebihan: lebih jelas bahwa definisi pengenal yang ada dan deklarasi pengenal baru milik keduanya MyNamespace. Kekurangan: lebih mudah untuk secara tidak sengaja memperkenalkan pengenal MyNamespace.

Kritik dari # 1 dan # 2 adalah bahwa mereka merujuk ke seluruh namespace ketika Anda mungkin hanya peduli tentang definisi anggota MyNamespace::MyClass. Ini berat dan tidak mengkomunikasikan maksudnya dengan baik.

Alternatif yang mungkin untuk # 1 adalah menggunakan-deklarasi yang hanya menyertakan pengenal yang Anda minati:

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }
John McFarlane
sumber
5

Saya juga ingin menambahkan bahwa jika Anda memutuskan karena alasan tertentu untuk mengimplementasikan spesialisasi template dalam file cpp dan hanya mengandalkan, using namespaceAnda akan mengalami masalah berikut:

// .h file
namespace someNameSpace
{
  template<typename T>
    class Demo
    {
      void foo();
    };
}

// .cpp file
using namespace someNameSpace;

template<typename T>
void Demo<T>::foo(){}

// this will produce
// error: specialization of 'template<class T> void someNameSpace::Demo<T>::foo()' in different namespace [-fpermissive]
template<>
void Demo<int>::foo(){}

Jika tidak, jika Anda menerapkan metode # 2 ini akan baik-baik saja.

Yordania
sumber
0

Saya ingin menambahkan satu cara lagi, menggunakan using-declaration :

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }

Dengan cara ini Anda tidak perlu mengetik nama namespace berkali-kali jika kelas memiliki banyak fungsi

Joanna
sumber