คุณใช้เนมสเปซใน C ++ อย่างไร


231

ฉันมาจากพื้นหลังของจาวาที่ซึ่งมีการใช้งานแพ็คเกจไม่ใช่เนมสเปซ ฉันเคยใส่ชั้นเรียนที่ทำงานร่วมกันเพื่อสร้างวัตถุที่สมบูรณ์ลงในแพคเกจแล้วนำมาใช้ใหม่ในภายหลังจากแพ็คเกจนั้น แต่ตอนนี้ฉันทำงานใน C ++

คุณใช้เนมสเปซใน C ++ ได้อย่างไร คุณสร้างเนมสเปซเดียวสำหรับแอปพลิเคชันทั้งหมดหรือคุณสร้างเนมสเปซสำหรับส่วนประกอบหลักหรือไม่ ถ้าเป็นเช่นนั้นคุณจะสร้างวัตถุจากคลาสในเนมสเปซอื่น ๆ ได้อย่างไร?

คำตอบ:


167

เนมสเปซเป็นแพ็คเกจหลัก พวกเขาสามารถใช้เช่นนี้

namespace MyNamespace
{
  class MyClass
  {
  };
}

จากนั้นในรหัส:

MyNamespace::MyClass* pClass = new MyNamespace::MyClass();

หรือถ้าคุณต้องการใช้เนมสเปซที่เจาะจงเสมอคุณสามารถทำได้:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

แก้ไข: ตามสิ่งที่bernhardruschพูดฉันมักจะไม่ใช้ไวยากรณ์ "using namespace x" เลยฉันมักจะระบุ namespace อย่างชัดเจนเมื่อสร้างวัตถุของฉัน (เช่นตัวอย่างแรกที่ฉันแสดง)

และตามที่คุณถามด้านล่างคุณสามารถใช้เนมสเปซได้มากเท่าที่คุณต้องการ


25
IMO จะเป็นการดีกว่าที่จะคุ้นเคยกับการใช้คำนำหน้าstdเนมสเปซกับสัญลักษณ์แทนการใช้usingเลย ดังนั้นฉันมักจะเขียนstd::coutหรือstd::stringตอนนี้เพราะนั่นคือสิ่งที่ฉันเรียกพวกเขาตอนนี้ coutฉันจะไม่เพียงแค่เขียน
Tom Savage

5
แม้ว่านี่จะเป็นเรื่องจริงสำหรับstdฉันพบว่าสิ่งนี้มีความสำคัญน้อยกว่ามากเมื่อคุณจัดการกับห้องสมุดขนาดเล็ก บ่อยครั้งที่คุณสามารถใช้งานusing namespace FooBario;ได้โดยเฉพาะอย่างยิ่งหากคุณใช้งานจำนวนมากจากไลบรารี
jkerian

4
@ jkerian ฉันเห็นประเด็นของคุณ แต่ฉันไม่เห็นด้วยเพราะการชนกันของชื่อ (ในใจของฉัน) มีแนวโน้มที่จะมาจากห้องสมุดขนาดเล็กเช่นนั้นอย่างแม่นยำ คนส่วนใหญ่ระวังอย่าตั้งชื่อคลาส / ฟังก์ชั่นเหมือนกับใน STL ที่กล่าวว่าฉันยอมรับว่าusing namespace X;ควรหลีกเลี่ยงในไฟล์ส่วนหัวถ้าเป็นไปได้
Alan Turing

12
@LexFridman "คนส่วนใหญ่ระวังที่จะไม่ตั้งชื่อคลาส / ฟังก์ชั่นเหมือนกับใน STL" - นั่นไม่ใช่เรื่องจริง ตัวอย่างเช่นถ้าฉันจะเขียนรหัส I / O ที่พิเศษมากสำหรับฮาร์ดแวร์แปลก ๆ ฉันจะไม่ใช้สิ่งอื่นนอกจากmylibrary::endlแทนการแสดงลำดับบรรทัดใหม่พิเศษของตัวเอง ฉันหมายถึงทำไมชื่อประดิษฐ์?

คอมไพเลอร์ของฉันยังคงไม่รู้จัก namespace แม้ว่าฉันต้องการระบุอย่างชัดเจนและฉันรวมไฟล์ที่มีการประกาศ
bgenchel

116

เพื่อหลีกเลี่ยงการพูดทุกอย่าง Mark Ingram พูดถึงเคล็ดลับเล็กน้อยสำหรับการใช้เนมสเปซ:

หลีกเลี่ยงคำสั่ง "using namespace" ในไฟล์ส่วนหัว - นี่เป็นการเปิดเนมสเปซสำหรับทุกส่วนของโปรแกรมที่นำเข้าไฟล์ส่วนหัวนี้ ในไฟล์การนำไปใช้ (* .cpp) โดยปกติจะไม่มีปัญหาใหญ่ - โดยทั่วไปฉันต้องการใช้คำสั่ง "using namespace" ในระดับฟังก์ชั่น

ฉันคิดว่าส่วนใหญ่ใช้เนมสเปซเพื่อหลีกเลี่ยงความขัดแย้งในการตั้งชื่อ - ไม่จำเป็นต้องจัดระเบียบโครงสร้างรหัสของคุณ ฉันจะจัดระเบียบโปรแกรม C ++ เป็นหลักด้วยไฟล์ส่วนหัว / โครงสร้างไฟล์

บางครั้งเนมสเปซถูกใช้ในโครงการ C ++ ที่ใหญ่กว่าเพื่อซ่อนรายละเอียดการใช้งาน

หมายเหตุเพิ่มเติมเกี่ยวกับการใช้คำสั่ง: บางคนชอบใช้ "การใช้" สำหรับองค์ประกอบเดียว:

using std::cout;  
using std::endl;

2
ข้อดีอย่างหนึ่งของ "การใช้เนมสเปซ" ที่ระดับฟังก์ชั่นตามที่คุณแนะนำมากกว่าที่ระดับไฟล์. cpp หรือระดับบล็อกเนมสเปซ {} ภายใน. cpp คือช่วยได้อย่างมากกับการสร้างหน่วยการรวบรวมเดี่ยว "การใช้เนมสเปซ" เป็นสกรรมกริยาและใช้กับเนมสเปซ A ในเนมสเปซที่ไม่ต่อเนื่อง A {} ในหน่วยเดียวกันดังนั้นสำหรับการสร้างหน่วยคอมไพล์เดียวคุณจะจบลงด้วยการใช้ทุกอย่างอย่างรวดเร็ว
idij

using std::cout; เป็นการประกาศที่ใช้
Konstantin

3
เป็นไปได้หรือไม่ที่จะใช้หลายชื่อจากเนมสเปซเดียวในคำสั่งเดียว? สิ่งที่ต้องการหรือแม้กระทั่งusing std::cout, std::endl; using std::cout, endl;
AlQuemist

มันสามารถตกลงที่จะใช้using namespace xในส่วนหัวถ้ามันอยู่ใน namespace อื่น ไม่ใช่สิ่งที่ฉันอยากจะแนะนำโดยทั่วไป แต่มันไม่สร้างมลภาวะเนมสเปซส่วนกลาง
Praxeolitic

79

Vincent Robert พูดถูกในความคิดเห็นของเขาคุณใช้เนมสเปซใน C ++ อย่างไร .

ใช้เนมสเปซ

Namespaces ถูกใช้อย่างน้อยที่สุดเพื่อช่วยหลีกเลี่ยงการชนกันของชื่อ ใน Java สิ่งนี้ถูกบังคับใช้ผ่าน "org.domain" สำนวน (เพราะมันควรจะไม่ใช้ชื่ออื่นนอกเหนือจากชื่อโดเมนของเขา / เธอ)

ใน C ++ คุณสามารถกำหนด namespace ให้กับรหัสทั้งหมดในโมดูลของคุณ ตัวอย่างเช่นสำหรับโมดูล MyModule.dll คุณสามารถให้รหัส MyModule กับ namespace ได้ ฉันเห็นคนอื่นที่ใช้ MyCompany :: MyProject :: MyModule ฉันเดาว่ามันเกินความจริง แต่โดยรวมแล้วดูเหมือนว่าถูกต้องสำหรับฉัน

ใช้ "ใช้"

การใช้ควรใช้ด้วยความระมัดระวังอย่างยิ่งเนื่องจากจะนำเข้าสัญลักษณ์ (หรือทั้งหมด) อย่างมีประสิทธิภาพจากเนมสเปซไปยังเนมสเปซปัจจุบันของคุณ

นี่เป็นสิ่งที่ผิดในไฟล์ส่วนหัวเนื่องจากส่วนหัวของคุณจะทำให้เกิดมลพิษทุกแหล่งรวมถึงมัน (มันทำให้ฉันนึกถึงมาโคร ... ) และแม้กระทั่งในไฟล์ต้นฉบับสไตล์ที่ไม่ดีนอกขอบเขตของฟังก์ชั่นเพราะมันจะนำเข้าที่ขอบเขตทั่วโลก สัญลักษณ์จากเนมสเปซ

วิธีที่ปลอดภัยที่สุดในการใช้ "การใช้" คือการนำเข้าสัญลักษณ์ที่เลือก:

void doSomething()
{
   using std::string ; // string is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   std::cout << a << std::endl ;
}

void doSomethingElse()
{
   using namespace std ; // everything from std is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   cout << a << endl ;
}

คุณจะเห็นจำนวนมาก "ใช้ namespace std;" ในการกวดวิชาหรือรหัสตัวอย่าง เหตุผลคือลดจำนวนสัญลักษณ์เพื่อให้การอ่านง่ายขึ้นไม่ใช่เพราะมันเป็นความคิดที่ดี

"ใช้ namespace std;" Scott Meyers ท้อใจ (ฉันจำไม่ได้ว่าหนังสือเล่มไหน แต่ฉันสามารถหาได้ถ้าจำเป็น)

องค์ประกอบเนมสเปซ

เนมสเปซเป็นมากกว่าแพ็คเกจ อีกตัวอย่างหนึ่งสามารถพบได้ใน "ภาษาโปรแกรม C ++" ของ Bjarne Stroustrup

ใน "รุ่นพิเศษ" ที่8.2.8 องค์ประกอบของเนมสเปซเขาจะอธิบายวิธีที่คุณสามารถผสานสองเนมสเปซ AAA และ BBB เข้ากับอีกหนึ่งชื่อที่เรียกว่า CCC ดังนั้น CCC จะกลายเป็นนามแฝงสำหรับทั้ง AAA และ BBB:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

namespace CCC
{
   using namespace AAA ;
   using namespace BBB ;
}

void doSomethingAgain()
{
   CCC::doSomething() ;
   CCC::doSomethingElse() ;
}

คุณสามารถนำเข้าสัญลักษณ์ที่เลือกจากเนมสเปซที่แตกต่างกันเพื่อสร้างอินเทอร์เฟซเนมสเปซที่กำหนดเองของคุณเอง ฉันยังไม่พบการใช้สิ่งนี้ในทางปฏิบัติ แต่ในทางทฤษฎีแล้วมันยอดเยี่ยม


คุณช่วยอธิบายได้โปรด "ให้เนมสเปซกับรหัสทั้งหมดในโมดูลของคุณ" แนวปฏิบัติที่ดีในการห่อหุ้มโมดูล ตัวอย่างเช่นฉันมีคลาสของจำนวนเชิงซ้อนและฟังก์ชันภายนอกที่เกี่ยวข้องกับจำนวนเชิงซ้อน คลาสนี้และฟังก์ชันทั้งสองนั้นควรอยู่ในเนมสเปซเดียวหรือไม่
yanpas

74

ฉันไม่เห็นการกล่าวถึงในคำตอบอื่น ๆ ดังนั้นนี่คือ 2 เซนต์แคนาดาของฉัน:

ในหัวข้อ "using namespace" คำสั่งที่เป็นประโยชน์คือนามแฝง namespace ช่วยให้คุณสามารถ "เปลี่ยนชื่อ" namespace ได้ตามปกติเพื่อตั้งชื่อให้สั้นลง ตัวอย่างเช่นแทนที่จะเป็น:

Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::TheClassName foo;
Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::AnotherClassName bar;

คุณสามารถเขียน:

namespace Shorter = Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally;
Shorter::TheClassName foo;
Shorter::AnotherClassName bar;

55

อย่าฟังทุกคนที่บอกคุณว่าเนมสเปซเป็นเพียงการเว้นวรรค

มีความสำคัญเนื่องจากคอมไพเลอร์ได้รับการพิจารณาให้ใช้หลักการอินเทอร์เฟซ โดยทั่วไปสามารถอธิบายได้ด้วยตัวอย่าง:

namespace ns {

class A
{
};

void print(A a)
{
}

}

หากคุณต้องการพิมพ์วัตถุ A รหัสจะเป็นรหัสนี้:

ns::A a;
print(a);

โปรดทราบว่าเราไม่ได้กล่าวถึงเนมสเปซอย่างชัดเจนเมื่อเรียกใช้ฟังก์ชัน นี่คือหลักการของอินเตอร์เฟส: C ++ พิจารณาว่าฟังก์ชันที่พิมพ์เป็นอาร์กิวเมนต์เป็นส่วนหนึ่งของอินเตอร์เฟสสำหรับประเภทนั้นดังนั้นไม่จำเป็นต้องระบุเนมสเปซเพราะพารามิเตอร์บ่งบอกถึงเนมสเปซแล้ว

แล้วทำไมหลักการนี้จึงสำคัญ ลองจินตนาการว่าผู้เขียนคลาส A ไม่ได้จัดเตรียมฟังก์ชัน print () สำหรับคลาสนี้ คุณจะต้องให้หนึ่งตัวเอง ในขณะที่คุณเป็นโปรแกรมเมอร์ที่ดีคุณจะกำหนดฟังก์ชันนี้ในเนมสเปซของคุณเองหรืออาจจะอยู่ในเนมสเปซส่วนกลาง

namespace ns {

class A
{
};

}

void print(A a)
{
}

และรหัสของคุณสามารถเริ่มเรียกใช้ฟังก์ชัน print (a) ได้ทุกที่ที่คุณต้องการ ตอนนี้ลองนึกภาพว่าหลายปีต่อมาผู้เขียนตัดสินใจที่จะจัดพิมพ์ () ฟังก์ชั่นให้ดีกว่าของคุณเพราะเขารู้ว่าภายในของชั้นเรียนของเขาและสามารถสร้างเวอร์ชั่นที่ดีกว่าของคุณ

ดังนั้นผู้เขียน C ++ จึงตัดสินใจว่าควรใช้ฟังก์ชั่น print () แทนเวอร์ชันที่ให้ไว้ในเนมสเปซอื่นเพื่อเคารพหลักการของอินเตอร์เฟส และฟังก์ชั่น "upgrade" ของฟังก์ชั่น print () ควรจะง่ายที่สุดเท่าที่จะเป็นไปได้ซึ่งหมายความว่าคุณไม่จำเป็นต้องเปลี่ยนทุกครั้งที่เรียกใช้ฟังก์ชัน print () นั่นเป็นเหตุผลที่สามารถเรียกใช้ "ฟังก์ชันอินเตอร์เฟส" (ฟังก์ชันในเนมสเปซเดียวกันกับคลาส) ได้โดยไม่ต้องระบุเนมสเปซใน C ++

และนั่นคือสาเหตุที่คุณควรพิจารณา namespace ของ C ++ เป็น "ส่วนต่อประสาน" เมื่อคุณใช้และจำไว้ว่าหลักการของส่วนต่อประสาน

หากคุณต้องการคำอธิบายที่ดีขึ้นเกี่ยวกับพฤติกรรมนี้คุณสามารถอ้างถึงหนังสือเจ๋ง C ++ จาก Herb Sutter


23
คุณต้องเปลี่ยนการเรียกทุกครั้งเป็น print () หากเพิ่ม ns :: Print แต่คอมไพเลอร์จะตั้งค่าสถานะการโทรแต่ละครั้งว่าไม่ชัดเจน การเปลี่ยนไปใช้ฟังก์ชั่นใหม่แบบเงียบ ๆ เป็นความคิดที่แย่มาก
Eclipse

ฉันสงสัยว่าตอนนี้การที่ @Vincent พูดว่าคุณจะต้องเปลี่ยนการโทรทั้งหมดเพื่อพิมพ์ถ้า autor จะให้ฟังก์ชั่น ns :: Print () คุณพยายามจะพูดอะไร? เมื่อผู้เขียนเพิ่มฟังก์ชั่น ns :: Print () คุณสามารถลบการใช้งานของคุณเองได้หรือไม่? หรือว่าคุณเพียงแค่เพิ่มการใช้ ns :: print () using-declaration? หรือเป็นอย่างอื่น? ขอบคุณ
Vaska el gato

36

โครงการ C ++ ที่ใหญ่กว่าที่ฉันเห็นแทบจะไม่ได้ใช้ namespace มากกว่าหนึ่งรายการ (เช่น boost library)

การเพิ่มประสิทธิภาพอย่างแท้จริงใช้เนมสเปซจำนวนมากโดยปกติแล้วส่วนเสริมทั้งหมดจะมีเนมสเปซของตนเองสำหรับการทำงานด้านในและจากนั้นอาจวางเฉพาะส่วนติดต่อสาธารณะในการเพิ่มเนมสเปซระดับบนสุด

โดยส่วนตัวแล้วฉันคิดว่ายิ่งรหัสฐานใหญ่ขึ้นเนมสเปซที่มีความสำคัญยิ่งจะยิ่งมีมากขึ้นแม้แต่ในแอปพลิเคชันเดียว (หรือไลบรารี) ที่ทำงานเราวางแต่ละโมดูลของแอปพลิเคชันของเราในเนมสเปซของตัวเอง

การใช้เนมสเปซที่ฉันใช้บ่อยๆเป็นเนมสเปซนิรนาม

namespace {
  const int CONSTANT = 42;
}

นี่เป็นพื้นเหมือนกับ:

static const int CONSTANT = 42;

การใช้เนมสเปซที่ไม่ระบุชื่อ (แทนที่จะเป็นสแตติก) เป็นวิธีที่แนะนำสำหรับรหัสและข้อมูลที่จะมองเห็นได้เฉพาะภายในหน่วยรวบรวมปัจจุบันใน C ++


13
ทั้งสองตัวอย่างของคุณเทียบเท่าconst int CONSTANT = 42;เพราะ const ระดับบนสุดในขอบเขตเนมสเปซแสดงถึงการเชื่อมโยงภายในแล้ว ดังนั้นคุณไม่จำเป็นต้องใช้เนมสเปซที่ไม่ระบุชื่อในกรณีนี้
sellibitze

19

นอกจากนี้โปรดทราบว่าคุณสามารถเพิ่มเนมสเปซได้ นี่คือตัวอย่างที่ชัดเจนยิ่งขึ้นสิ่งที่ฉันหมายถึงคือคุณสามารถมี:

namespace MyNamespace
{
    double square(double x) { return x * x; }
}

ในไฟล์ square.hและ

namespace MyNamespace
{
    double cube(double x) { return x * x * x; }
}

cube.hในแฟ้ม สิ่งนี้กำหนดเนมสเปซเดียวMyNamespace(นั่นคือคุณสามารถกำหนดเนมสเปซเดียวข้ามหลายไฟล์)


11

ใน Java:

package somepackage;
class SomeClass {}

ใน C ++:

namespace somenamespace {
    class SomeClass {}
}

และใช้พวกเขา, Java:

import somepackage;

และ C ++:

using namespace somenamespace;

นอกจากนี้ชื่อเต็มคือ "somepackge.SomeClass" สำหรับ Java และ "somenamespace :: SomeClass" สำหรับ C ++ ด้วยการใช้ระเบียบเหล่านั้นคุณสามารถจัดระเบียบเช่นเดียวกับที่คุณใช้ใน Java รวมถึงการสร้างชื่อโฟลเดอร์ที่ตรงกันสำหรับเนมสเปซ ความต้องการของโฟลเดอร์ -> แพ็คเกจและไฟล์ -> ไม่ได้มีดังนั้นคุณจึงสามารถตั้งชื่อโฟลเดอร์และคลาสของคุณแยกจากแพ็คเกจและเนมสเปซได้อย่างอิสระ


6

@ marius

ใช่คุณสามารถใช้เนมสเปซได้หลายรายการพร้อมกันเช่น:

using namespace boost;   
using namespace std;  

shared_ptr<int> p(new int(1));   // shared_ptr belongs to boost   
cout << "cout belongs to std::" << endl;   // cout and endl are in std

[กุมภาพันธ์ 2014 - (มันนานขนาดนั้นจริง ๆ หรือเปล่า?): ตอนนี้ตัวอย่างที่ชัดเจนไม่ชัดเจนเนื่องจาก Joey ชี้ให้เห็นด้านล่าง Boost และ std :: ตอนนี้แต่ละอันมี shared_ptr]


2
ทราบว่าstdยังมีshared_ptrโดยขณะนี้ดังนั้นการใช้ทั้งสองboostและstdnamespaces shared_ptrจะปะทะกันเมื่อคุณพยายามที่จะใช้
Joey

2
นี่เป็นตัวอย่างที่ดีว่าทำไมบ้านซอฟต์แวร์จำนวนมากจึงไม่สนับสนุนการนำเข้าเนมสเปซทั้งหมดด้วยวิธีนี้ ไม่เจ็บที่จะระบุเนมสเปซเสมอและถ้ามันยาวเกินไปให้สร้างนามแฝงหรือคลาสเฉพาะที่สำคัญจากเนมสเปซ
ข้าวเปลือก

5

นอกจากนี้คุณยังสามารถมี "กำลังใช้ namespace ... " ภายในฟังก์ชันตัวอย่างเช่น:

void test(const std::string& s) {
    using namespace std;
    cout << s;
}

3

โดยทั่วไปฉันจะสร้างเนมสเปซสำหรับเนื้อความของรหัสหากฉันเชื่อว่าอาจมีฟังก์ชั่นหรือชื่อประเภทขัดแย้งกับไลบรารีอื่น ๆ นอกจากนี้ยังช่วยในการสร้างรหัสแบรนด์ ala boost :: .


3

ฉันชอบที่จะใช้เนมสเปซระดับบนสุดสำหรับแอปพลิเคชันและเนมสเปซย่อยสำหรับส่วนประกอบ

วิธีที่คุณสามารถใช้คลาสจากเนมสเปซอื่น ๆ นั้นคล้ายกับจาวามาก คุณสามารถใช้ "use NAMESPACE" ซึ่งคล้ายกับคำสั่ง "import PACKAGE" เช่นใช้ std หรือคุณระบุแพ็คเกจเป็นคำนำหน้าของคลาสที่คั่นด้วย "::" เช่น std :: string สิ่งนี้คล้ายกับ "java.lang.String" ใน Java


3

โปรดทราบว่าเนมสเปซใน C ++ เป็นเพียงแค่พื้นที่ชื่อ พวกเขาไม่มีการห่อหุ้มที่แพ็คเกจทำใน Java ดังนั้นคุณอาจจะไม่ใช้มันมากนัก


2

ฉันใช้เนมสเปซ C ++ เหมือนกับที่ฉันทำใน C #, Perl ฯลฯ มันเป็นเพียงการแยกความหมายของสัญลักษณ์ระหว่างสิ่งต่าง ๆ ของไลบรารีมาตรฐานสิ่งต่าง ๆ ของบุคคลที่สามและรหัสของฉันเอง ฉันจะวางแอพของฉันเองในเนมสเปซหนึ่งจากนั้นเป็นส่วนประกอบของไลบรารีที่สามารถใช้ซ้ำได้ในเนมสเปซอื่นเพื่อแยก


2

ความแตกต่างอีกอย่างหนึ่งระหว่าง java และ C ++ คือใน C ++ ลำดับชั้นของเนมสเปซไม่จำเป็นต้องควบคุมเค้าโครงของระบบไฟล์ ดังนั้นฉันมักจะใส่ทั้งไลบรารีที่ใช้ซ้ำได้ในเนมสเปซเดียวและระบบย่อยภายในไลบรารีในไดเรกทอรีย่อย:

#include "lib/module1.h"
#include "lib/module2.h"

lib::class1 *v = new lib::class1();

ฉันจะวางระบบย่อยในเนมสเปซที่ซ้อนกันหากมีความเป็นไปได้ของความขัดแย้งของชื่อ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.