มลพิษที่ "ใช้ namespace" คืออะไร?


15

ฉันกำลังดูคู่มือการเขียนโค้ดของ Google [ที่นี่]และพวกเขาไม่แนะนำให้ใช้using namespaceหรือnamespace::function- ถ้าฉันไม่ได้ตีความผิด

สิ่งนี้ใช้ได้กับstdเช่นกันหรือไม่? cout<<ไม่ทำงานหากไม่มีมัน หนังสือเล่มนี้แนะนำเหมือนกัน ดังนั้นฉันจะไปเกี่ยวกับการใช้cout<<โดยไม่ต้องusing namespace std;หรือstd::cout<<อย่างไร

วิธีที่แนะนำคืออะไร? std::cout<<? หนังสือเรียน c ++ ส่วนใหญ่สอนผู้เริ่มต้นด้วยusing namespace std;พวกเขาเผยแพร่วิธีการเข้ารหัสที่ไม่ดีหรือไม่?

คำตอบ:


18

เมื่อฉันอ่านมาตรฐานของ Google คุณไม่สามารถใช้using namespace foo;คำสั่งได้ทุกที่ คำสั่งนี้นำทุกอย่างที่ประกาศในเนมสเปซและเป็นสาเหตุของการชนและพฤติกรรมที่ไม่คาดคิด คนอื่น ๆ อ้างถึงวิธีการทั่วไป: คุณมีวิธีสูงสุดหรือต่ำสุดของคุณเองและมันจะชนกันในไฟล์ src ที่มีคนรวมส่วนหัวกับวิธีการของคุณแล้วพูดว่าusing namespace std;

ในบางสถานที่จะได้รับอนุญาตให้มีการประกาศใช้ซึ่งเป็นของแบบฟอร์ม using ::foo::bar;

คนชอบใส่คำสั่งในรหัสของพวกเขาเพราะมันช่วยประหยัดการพิมพ์จำนวนมาก แต่มันมีความเสี่ยง หากคุณมีไฟล์ที่มีคำสั่ง cout จำนวนมากฉันสามารถเข้าใจว่าไม่ต้องการที่จะพิมพ์ std :: cout ร้อยครั้ง แต่คุณสามารถพูดโดยใช้ :: std :: cout ฉันปฏิบัติต่อสิ่งเหล่านี้เช่นการประกาศตัวแปร: กำหนดขอบเขตที่ต้องการ หากหนึ่งฟังก์ชันในไฟล์ 10 จำเป็นต้องเขียนเอาต์พุตไม่ต้องประกาศวิธี cout ที่ด้านบนวางไว้ในฟังก์ชันที่ทำเอาต์พุตจริง

#include <ostream>
//using namespace std; // NO!
//using ::std::cout;   // less bad than using namespace, but I prefer to scope it

int main(int argc, char** argv)
{
   int rc = do_some_stuff(argc, argv);
   using ::std::endl;
   if (rc) { // print the success report
      using ::std::cout;
      cout << "The test run completed. The return code was " << rc << '.' << endl;
    } else {
      using ::std::cerr;
      cerr << "Unable to complete the test run." << endl;
    }
    return 0 == rc;
}

นั่นเป็นเรื่องเล็กน้อยมากที่มีเพียงสองบรรทัดที่ทำเอาท์พุต แต่คุณจะได้แนวคิด

อีกสิ่งหนึ่งที่สามารถทำได้คือนามแฝงหรือ typedef เพื่อลดการพิมพ์ ฉันไม่พบมาตรฐาน :: สิ่งที่จะไม่ดีว่า console_gui::command_window::append("text")แต่เรามีชุดใหญ่ของแหล่งที่มากับโมดูลหลายโหลและบางครั้งเราต้องเขียนโค้ดเช่น ที่น่าเบื่อหลังจากที่ในขณะและทำให้เกิดเส้นยาวมาก ฉันทุกคนชอบอะไร

typedef console_gui::command_window cw;
cw::append("text");

ตราบใดที่นามแฝงทำในขอบเขตโลคัลและเก็บบริบทให้เพียงพอเพื่อให้โค้ดอ่านได้


1
ขอบคุณ! สิ่งนี้มีประโยชน์จริงๆ คุณไม่เพียง แต่อธิบายว่าทำไมมันถึงไม่ดีกับตัวอย่างที่ดี แต่ยังชี้ให้เห็นวิธีแก้ปัญหาด้วยตัวอย่างที่ดี :-)
ลอร์ดโลห์

1
BTW: การใช้std::endlฟลัช On on stdout/ stderrเป็นฟุ่มเฟือยค่อนข้างปกติสตรีมเหล่านี้จะเชื่อมโยงกับstdout/ stderrอย่างไรก็ตาม มันทำให้สิ่งต่าง ๆ ช้าลงเล็กน้อย
Deduplicator

8

นี่เป็นเพราะ: 1) มันเอาชนะจุดประสงค์ทั้งหมดของเนมสเปซซึ่งเป็นการลดการชนกันของชื่อ; 2) ทำให้สามารถใช้ได้กับ namespace ทั่วโลกทั้ง namespace ที่ระบุด้วยการใช้คำสั่ง

ตัวอย่างเช่นหากคุณรวมและกำหนดฟังก์ชั่น max () ของคุณเองมันจะชนกับ std :: max ()

http://en.cppreference.com/w/cpp/algorithm/max

การตั้งค่าคือการใช้ std :: member_you_wish_to_use เพราะมันระบุอย่างชัดเจนว่า namespace ที่จะใช้


ฉันเข้าใจว่านี่หมายความว่าฉันควรใช้std::max()กับคำนำหน้าชื่อพื้นที่ หรือฉันเข้าใจผิด?
ลอร์ดโลห์

3
หมายความว่าถ้าคุณใส่ "using namespace std;" ในรหัสของคุณคุณจะได้รับข้อผิดพลาดถ้าคุณกำหนดฟังก์ชั่นสูงสุดของคุณเอง (หรือชื่ออื่น ๆ ที่กำหนดไว้แล้วใน std namespace)
Chewy Gumball

1
หมายความว่าคุณควรระวังusingคำสั่งเพราะในกรณีนี้มันจะทำลายฟังก์ชั่น max () ของคุณหากคุณได้กำหนดไว้และรวม <algorithm> นี่เป็นกรณีง่าย ๆ แต่คุณไม่มีทางรู้ว่าคุณจะแตก คุณต้องรู้ทั้งห้องสมุดเพื่อให้แน่ใจว่าคุณไม่ได้ทำลายมัน แต่คุณไม่รู้ว่ารหัสของคุณจะแตก (เช่นการชนกันของชื่อ) ในอนาคตหรือไม่
ApplePie

6

อ้างถึงลิงก์ที่คุณให้:

คุณสามารถใช้การประกาศได้ทุกที่ในไฟล์. cc และในฟังก์ชั่นวิธีการหรือคลาสในไฟล์. h

// ตกลงในไฟล์. cc

// ต้องอยู่ในฟังก์ชันเมธอดหรือคลาสในไฟล์. h

ใช้ :: foo :: แถบ;

สไตล์ Google ห้ามมิให้คุณใช้การนำเข้าเนมสเปซในบริบทส่วนกลาง แต่อนุญาตให้ทำในโลคอลท้องถิ่น

ทุกที่ที่มีการประกาศจะมีผลเฉพาะส่วนที่ จำกัด และมองเห็นได้ชัดเจนซึ่งเป็นที่ยอมรับได้อย่างสมบูรณ์

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


เรามีมาตรฐานเดียวกัน คนของเราบางคนจะพิมพ์ชื่อเฉพาะในพื้นที่ เช่น typedef foolicious :: barlicious fb; fb :: ดื่ม d;
Michael Mathews

1

พวกเขาไม่แนะนำให้ใช้อย่างใดอย่างหนึ่งใช้ namespace หรือ namespace: function` - ถ้าฉันไม่ได้ตีความมันผิด

คุณทำ disrecommendation นำไปใช้กับusing namespaceคำสั่งเท่านั้น (ซึ่งโดยทั่วไปจะเรียกว่าabusing namespaceไม่ใช่ทั้งหมดอย่างขบขัน) std::coutเป็นที่ต้องการอย่างแรงกล้าว่าคุณใช้ชื่อเต็มของฟังก์ชั่นหรือวัตถุเช่น


1

แม้ว่าคำถามจะมีคำตอบที่เป็นประโยชน์อยู่แล้ว แต่รายละเอียดหนึ่งดูเหมือนจะสั้นเกินไป

โปรแกรมเมอร์ส่วนใหญ่ค่อนข้างสับสนกับusingคำหลักและคำอธิบายnamespaceการใช้งานในตอนแรกแม้ว่าพวกเขาจะพยายามเรียนรู้โดยดูที่การอ้างอิงเพราะการประกาศและคำสั่งอ่านค่อนข้างเทียบเท่าทั้งสองมีคำยาวค่อนข้างเป็นนามธรรมที่เริ่มต้นด้วยd

ตัวระบุภายในเนมสเปซสามารถเข้าถึงได้โดยการตั้งชื่อเนมสเปซอย่างชัดเจน:

myNamespace::myIdent

นี่อาจเป็นคีย์ที่ต้องพิมพ์อีกมาก แต่มันอาจลดความสำคัญของรหัสของคุณด้วยหากตัวระบุส่วนใหญ่จะนำหน้าด้วยวิธีเดียวกัน usingคำหลักที่ช่วยในการป้องกันข้อเสีย namespace เหล่านี้ ตั้งแต่usingทำงานในระดับคอมไพเลอร์ (ไม่มีมาโคร) เอฟเฟกต์จะคงอยู่ตลอดขอบเขตที่ใช้งานนั่นคือสาเหตุที่สไตล์ Google จำกัด การใช้งานกับขอบเขตที่กำหนดไว้อย่างดีเช่นคลาสในไฟล์ส่วนหัวหรือฟังก์ชั่นในไฟล์ cpp

... แน่นอนว่ามีความแตกต่างระหว่าง ใช้การประกาศ

using myNamespace::myIdent; // make myIdent an alias of myNamespace::myIdent

และ ใช้คำสั่ง

using myNamespace; // make all identifiers of myNamespace directly accessible

ถ้าใช้ในขอบเขตขนาดใหญ่นำไปสู่หลังมากความสับสนมากขึ้น


1

ไปเลย:

#include <iostream>

int main()
{
    std::endl(std::operator<<(std::cout, "Hello world!"));
}

โดยการเขียนด้วยวิธีนี้เราหลีกเลี่ยงข้อผิดพลาด ADL ได้ง่ายพร้อมกับการใช้คำสั่งและการประกาศ

นี่หมายถึงคำตอบประชดประชัน :-D

ฉันอยู่กับ Herb Sutter บน Google ในอันนี้ จากมาตรฐานการเข้ารหัส C ++:

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

คุณสามารถครอบงำเกี่ยวกับความขัดแย้ง namespace ที่มีศักยภาพที่อาจจะไม่เคยประจักษ์และอาจจะไม่ยากที่จะแก้ไขปัญหาดังกล่าวในเหตุการณ์ที่ยาก astronomically โดยระมัดระวังหลีกเลี่ยงusingคำสั่งอย่างชัดเจนและระบุแต่ละคนและทุกสิ่งที่คุณใช้ (ลงไปที่ผู้ประกอบการ) ที่มีusingการประกาศหรือ เพียงไปข้างหน้าและเริ่มusing namespace stdเพียงแค่ไปข้างหน้าและเริ่ม ฉันแนะนำหลังจากมุมมองการผลิต

หนังสือเรียน c ++ ส่วนใหญ่สอนผู้เริ่มต้นด้วยการใช้ namespace std; พวกเขาเผยแพร่การฝึกฝนการเข้ารหัสที่ไม่ดีหรือไม่?

ตรงกันข้ามถ้าคุณถามฉันและฉันเชื่อว่าซัทเทอร์ข้างบนเห็นด้วย

ตอนนี้ในอาชีพของฉันฉันได้พบกับความขัดแย้งของ namespace ประมาณ 3 รายการซึ่งเป็นผลโดยตรงจาก usingคำสั่งในฐานรหัสซึ่งครอบคลุม LOC นับสิบล้านรายการ อย่างไรก็ตามในทั้ง 3 กรณีพวกเขาอยู่ในไฟล์ต้นฉบับที่มีรหัสดั้งเดิมมากกว่า 50,000 บรรทัดซึ่งเดิมเขียนเป็นภาษา C และจากนั้นก็ถูกใช้ใน C ++ เพื่อแสดงรายการฟังก์ชั่นที่หลากหลายรวมถึงส่วนหัวจากห้องสมุดที่แตกต่างกันหลายสิบแห่ง รายการมหากาพย์ของ#includesที่ขยายไปทั่วหน้า ทั้งๆที่มีความยุ่งเหยิงพวกมันก็ไม่ยากเกินไปที่จะแก้ไขเพราะมันทำให้เกิดข้อผิดพลาดในการสร้างบน OSX (ระบบปฏิบัติการเดียวที่โค้ดไม่สามารถสร้างได้) ไม่ใช่บั๊กรันไทม์ อย่าจัดระเบียบโค้ดของคุณในแบบที่น่าหวาดเสียวและคุณควรจะสบายดี

ที่กล่าวว่าหลีกเลี่ยงทั้ง usingคำสั่งและการประกาศในไฟล์ส่วนหัว นั่นเป็นเพียงแค่ปัญญาอ่อน แต่สำหรับไฟล์ต้นฉบับและโดยเฉพาะอย่างยิ่งไฟล์ที่ไม่มีทั้งหน้าที่เต็มไปด้วย#includeคำสั่งผมจะบอกว่าไม่ต้องกังวลถ้าคุณไม่ได้ทำงานให้กับ Google

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