“ การใช้เนมสเปซ” ในส่วนหัว c ++


119

ในหลักสูตร c ++ ทั้งหมดของเราครูทุกคนจะใส่using namespace std;หลัง#includes ลงใน.hไฟล์เสมอ สิ่งนี้ดูเหมือนว่าฉันจะเป็นอันตรายตั้งแต่นั้นมาโดยการรวมส่วนหัวนั้นไว้ในโปรแกรมอื่นฉันจะได้รับเนมสเปซที่นำเข้ามาในโปรแกรมของฉันอาจจะโดยไม่รู้ตัวตั้งใจหรือต้องการ (การรวมส่วนหัวอาจซ้อนกันได้ลึกมาก)

ดังนั้นคำถามของฉันจึงเป็นสองเท่า: ฉันพูดถูกหรือusing namespaceเปล่าที่ไม่ควรใช้ในไฟล์ส่วนหัวและ / หรือมีวิธียกเลิกบางอย่างเช่น:

//header.h
using namespace std {
.
.
.
}

อีกหนึ่งคำถามในบรรทัดเดียวกัน: ส่วนหัวควรจัดเก็บส่วนหัว#includeทั้งหมดที่.cppไฟล์นั้นต้องการเฉพาะคำถามที่จำเป็นสำหรับคำจำกัดความของส่วนหัวและปล่อยให้.cppไฟล์#includeเหลือหรือไม่มีเลยและประกาศทุกสิ่งที่ต้องการเป็นextern?
เหตุผลเบื้องหลังคำถามก็เหมือนกับข้างบน: ฉันไม่ต้องการความประหลาดใจเมื่อรวม.hไฟล์

นอกจากนี้ถ้าฉันพูดถูกนี่เป็นข้อผิดพลาดทั่วไปหรือไม่ ฉันหมายถึงการเขียนโปรแกรมในโลกแห่งความเป็นจริงและในโครงการ "จริง" ที่นั่น

ขอบคุณ.



3
เป็นหมายเหตุด้านข้างหากคุณได้รับการชนกันของชื่อเนื่องจากusing namespaceข้อความคุณสามารถใช้ชื่อที่มีคุณสมบัติครบถ้วนเพื่อแก้ปัญหา
Marius Bancila

คำตอบ:


115

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

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

ฉันไม่แน่ใจว่าฉันจะเรียกมันว่าธรรมดา แต่มันจะปรากฏขึ้นเป็นครั้งคราวซึ่งมักจะเขียนโดยโปรแกรมเมอร์มือใหม่ที่ไม่ตระหนักถึงผลเสีย โดยทั่วไปแล้วการศึกษาเพียงเล็กน้อยเกี่ยวกับความเสี่ยงจะดูแลปัญหาต่างๆได้เนื่องจากการแก้ไขค่อนข้างง่าย


2
เรามีอิสระในการใช้usingงบใน.cppไฟล์ของเราหรือไม่? 3rdPartyLib::BigClassName<3rdPartyLib::AnotherBigName,3rdPartyLib::AnotherBigName>::Iterators เป็นความตายมาสู่ปลายนิ้ว
Christopher

1
และเราควรปรับปรุงtemplateฟังก์ชันอย่างไร - ซึ่งควรจะอยู่ในส่วนหัว? typedefs?
Christopher

1
@donlan ดูเหมือนว่าคุณจะไม่ได้รับการตอบสนองมาระยะหนึ่งแล้ว ... ใช่คุณสามารถใช้usingคำสั่งภายใน.cppไฟล์ได้โดยไม่ต้องกังวลมากนักเนื่องจากขอบเขตจะถูก จำกัด ไว้ที่ไฟล์นั้นเท่านั้น แต่ไม่เคยทำก่อนที่จะมี#includeคำสั่ง สำหรับฟังก์ชันเทมเพลตที่กำหนดไว้ในส่วนหัวน่าเสียดายที่ฉันไม่รู้วิธีแก้ปัญหาที่ดีนอกเหนือจากการเขียนเนมสเปซ ... บางทีคุณอาจวางการusingประกาศภายในขอบเขตที่แยกต่างหาก{ /* using statement in between brackets */ }ซึ่งอย่างน้อยก็จะป้องกันไม่ให้ไฟล์ปัจจุบันหนี .
tjwrona1992

26

รายการ 59 ใน"มาตรฐานการเข้ารหัส C ++ของ Sutter and Alexandrescu : 101 กฎแนวทางปฏิบัติและแนวทางปฏิบัติที่ดีที่สุด" :

59. อย่าเขียนการใช้งานเนมสเปซในไฟล์ส่วนหัวหรือก่อน #include

เนมสเปซusingมีไว้เพื่อความสะดวกของคุณไม่ใช่สำหรับคุณที่จะสร้างความเสียหายให้กับผู้อื่นห้ามเขียนusingคำประกาศหรือusingคำสั่งก่อน#includeคำสั่ง

Corollary: ในไฟล์ส่วนหัวอย่าเขียนusingคำสั่งหรือusingการประกาศระดับเนมสเปซ แทนเนมสเปซอย่างชัดเจน - รับรองชื่อทั้งหมด

ไฟล์ส่วนหัวคือแขกในไฟล์ต้นฉบับอย่างน้อยหนึ่งไฟล์ ไฟล์ส่วนหัวที่มีusingคำสั่งและคำประกาศก็นำเพื่อนนักเลงมาด้วยเช่นกัน

using ประกาศนำในหนึ่งเพื่อน using สั่งนำในเพื่อนทั้งหมดในการ namespace การใช้ครูของคุณusing namespace std;เป็นการใช้คำสั่ง

อย่างจริงจังเรามีเนมสเปซเพื่อหลีกเลี่ยงการปะทะกันของชื่อ ไฟล์ส่วนหัวมีไว้เพื่อจัดเตรียมอินเทอร์เฟซ ส่วนหัวส่วนใหญ่ไม่เข้าใจว่าโค้ดใดอาจรวมไว้ในตอนนี้หรือในอนาคต การเพิ่มusingคำสั่งเพื่อความสะดวกภายในส่วนหัวจะทำให้ชื่อที่ใช้สะดวกเหล่านั้นกับผู้มีโอกาสเป็นลูกค้าทั้งหมดของส่วนหัวนั้น ที่สามารถนำไปสู่การปะทะกันของชื่อ และมันก็หยาบคายธรรมดา


12

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

คุณควรรวมส่วนหัวไว้ในส่วนหัวเมื่อจำเป็นเท่านั้น (เมื่อใดก็ตามที่จำเป็นต้องใช้คำจำกัดความทั้งหมดของคลาส) และใช้การประกาศไปข้างหน้าทุกที่ที่ทำได้ (เมื่อจำเป็นต้องใช้คลาสเป็นตัวชี้หรือการอ้างอิง)

สำหรับเนมสเปซฉันมักจะใช้การกำหนดขอบเขตเนมสเปซที่ชัดเจนในไฟล์ส่วนหัวของฉันและใส่เฉพาะusing namespaceในไฟล์ cpp ของฉัน


1
คุณจะปรับปรุงtemplateการประกาศฟังก์ชันได้อย่างไร? ที่ต้องเกิดขึ้นในส่วนหัวไม่ใช่เหรอ?
Christopher

6

ตรวจสอบมาตรฐานการเข้ารหัสของ Goddard Space Flight Center (สำหรับ C และ C ++) นั่นกลายเป็นเรื่องยากกว่าที่เคยเป็นมาเล็กน้อย - ดูคำตอบที่อัปเดตสำหรับคำถาม SO:

มาตรฐานการเข้ารหัส GSFC C ++ กล่าวว่า:

§3.3.7ไฟล์ส่วนหัวแต่ละไฟล์จะต้อง#includeเป็นไฟล์ที่ต้องการในการคอมไพล์แทนที่จะบังคับให้ผู้ใช้ไป#includeยังไฟล์ที่ต้องการ #includesจะ จำกัด เฉพาะสิ่งที่ส่วนหัวต้องการ อื่น ๆ#includesควรอยู่ในไฟล์ต้นฉบับ

คำถามแรกที่อ้างอิงโยงกันตอนนี้มีคำพูดจากมาตรฐานการเข้ารหัส GSFC C และเหตุผล แต่เนื้อหาก็เหมือนกัน


5

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

ในโครงการจริงผู้คนพยายามลดจำนวนไฟล์ที่รวมไว้ให้น้อยที่สุดเพราะยิ่งคุณรวมน้อยเท่าไหร่ก็ยิ่งรวบรวมได้เร็วเท่านั้น ที่ช่วยประหยัดเวลาของทุกคน อย่างไรก็ตามหากไฟล์ส่วนหัวสันนิษฐานว่าควรรวมบางสิ่งไว้ก่อนหน้านั้นก็ควรรวมไว้ด้วยตัวเอง มิฉะนั้นจะทำให้ส่วนหัวไม่อยู่ในตัว


4

คุณพูดถูก และไฟล์ใด ๆ ควรมีเฉพาะส่วนหัวที่จำเป็นสำหรับไฟล์นั้น สำหรับ "การทำสิ่งที่ผิดปกติในโครงการจริงหรือไม่" - โอ้ใช่!


4

เช่นเดียวกับทุกสิ่งในการเขียนโปรแกรมลัทธิปฏิบัตินิยมควรมีชัยเหนือความเชื่องมงาย IMO

ตราบเท่าที่คุณทำการตัดสินใจทั้งโครงการ ("โครงการของเราใช้ STL อย่างกว้างขวางและเราไม่ต้องการที่จะต้องนำหน้าทุกอย่างด้วย std ::.") ฉันไม่เห็นปัญหา สิ่งเดียวที่คุณเสี่ยงคือการชนกันของชื่อและด้วยความแพร่หลายของ STL มันไม่น่าจะเป็นปัญหา

ในทางกลับกันหากเป็นการตัดสินใจของนักพัฒนารายหนึ่งในไฟล์ส่วนหัวเดียว (ที่ไม่เป็นส่วนตัว) ฉันสามารถดูได้ว่าจะสร้างความสับสนในทีมได้อย่างไรและควรหลีกเลี่ยง


4

เกี่ยวกับ "มีวิธียกเลิก [ usingประกาศ] บ้างไหม"

ฉันคิดว่ามันมีประโยชน์ที่จะชี้ให้เห็นว่าusingการประกาศได้รับผลกระทบจากขอบเขต

#include <vector>

{   // begin a new scope with {
    using namespace std;
    vector myVector;  // std::vector is used
}   // end the scope with }

vector myOtherVector;   // error vector undefined
std::vector mySTDVector // no error std::vector is fully qualified

ใช่อย่างมีประสิทธิภาพ โดย จำกัด ขอบเขตของusingประกาศผลจะมีผลอยู่ภายในขอบเขตนั้นเท่านั้น จะถูก 'ยกเลิก' เมื่อขอบเขตนั้นสิ้นสุดลง

เมื่อการusingประกาศถูกประกาศในไฟล์นอกขอบเขตอื่น ๆ จะมีขอบเขตของไฟล์และมีผลต่อทุกสิ่งในไฟล์นั้น

ในกรณีของไฟล์ส่วนหัวหากการusingประกาศอยู่ที่ขอบเขตไฟล์สิ่งนี้จะขยายไปถึงขอบเขตของไฟล์ใด ๆ ที่มีส่วนหัวอยู่


2
ดูเหมือนคุณจะเป็นคนเดียวที่เข้าใจคำถามที่แท้จริง ... อย่างไรก็ตามการคอมไพล์ของฉันไม่ค่อยพอใจที่ฉันใช้ในการชะลอตัวของคลาส
rustypaper

คำตอบนี้สามารถทำให้ดีขึ้นได้ด้วยการอธิบายปัญหาด้วยแนวคิดของ OP ว่าขอบเขตควรทำงานอย่างไร (เช่นnamespaceสิ่งที่ประกาศ) เทียบกับวิธีการทำงานจริง (เช่นตัวแปร) {}การปิดล้อมจะ จำกัด ขอบเขตของมัน{}หลังจากที่ไม่ได้ทำอะไรเกี่ยวข้องกับมัน นั่นเป็นวิธีที่ไม่ได้ตั้งใจที่using namespaceจะนำไปใช้ทั่วโลก
TafT

2

ฉันเชื่อว่าคุณสามารถใช้ 'ใช้' ในส่วนหัว C ++ ได้อย่างปลอดภัยหากคุณเขียนการประกาศของคุณในเนมสเปซที่ซ้อนกันเช่นนี้:

namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED
{
    /*using statements*/

    namespace DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED
    {
        /*declarations*/
    }
}

using namespace DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED::DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED;

ซึ่งควรรวมเฉพาะสิ่งที่ประกาศใน "DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED" โดยไม่มีเนมสเปซที่ใช้ ฉันได้ทดสอบกับคอมไพเลอร์ mingw64 แล้ว


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

แม้ว่าจะเป็นผลข้างเคียงที่โชคร้ายของรูปแบบนี้คือการเรียนใด ๆ ประกาศภายใน namespace error: ... DECLARATIONS_WITH_NAMESPACES_USED_INCLUDED:: DECLARATIONS_WITH_NO_NAMESPACES_USED_INCLUDED::ClassName ...ด้านในสุดจะแสดงในข้อความผิดพลาดคอมไพเลอร์ที่มีชื่อที่มีคุณสมบัติครบถ้วน: อย่างน้อยนั่นคือสิ่งที่เกิดขึ้นสำหรับฉันใน g ++
Anthony Hall
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.