เมื่อใช้ส่วนหัว C ใน C ++ เราควรใช้ฟังก์ชันจาก std :: หรือ global namespace หรือไม่?


113

C ค่อนข้างไม่ตรงบางส่วนของ C ++ ดังนั้นเราจึงสามารถใช้ฟังก์ชัน / ส่วนหัว C ส่วนใหญ่ใน C ++ ได้โดยเปลี่ยนชื่อเล็กน้อย ( stdio.hto cstdio, stdlib.hto cstdlib)

คำถามของฉันเป็นความหมายจริงๆ ในรหัส C ++ (โดยใช้คอมไพเลอร์ GCC เวอร์ชันใหม่ล่าสุด) ฉันสามารถโทรได้printf("Hello world!");และstd::printf("Hello world!");ทำงานได้เหมือนกันทุกประการ std::printf("Hello world!");และในการอ้างอิงฉันใช้มันก็จะปรากฏเป็น

คำถามของฉันคือต้องการใช้std::printf();ใน C ++ หรือไม่? มีความแตกต่างหรือไม่?


17
ในกรณีที่วันหนึ่งพวกเขาสั่งให้ทิ้งCสัญลักษณ์ไลบรารีลงในเนมสเปซส่วนกลางฉันชอบใช้std::เวอร์ชันที่มีคุณสมบัติ (ฉันหวังว่าพวกเขาจะทำผิดกฎหมาย)
Galik

3
@ กาลิก: เห็นด้วย นั่นจะปลอดภัยคำถามโง่ ๆ เกี่ยวกับปัญหา C โดยใช้คอมไพเลอร์ C ++
ซื่อสัตย์เกินไปสำหรับเว็บไซต์นี้

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

2
"เกือบทั้งหมด" เป็นมาตรการที่ไร้ประโยชน์เมื่อพูดถึงชุดขององค์ประกอบมากมายที่นับไม่ได้ โดยอาร์กิวเมนต์เดียวกันคุณอาจเกี่ยวข้องกับ C และ Java
Daniel Jour

9
@sasauke ไม่ไม่ใช่ส่วนย่อย C และ C ++ แบ่งปันชุดย่อยอย่างแน่นอนแต่ C เองไม่ใช่ส่วนย่อยของ C ++
ครัวซองต์ Paramagnetic

คำตอบ:


106

จาก C ++ 11 Standard (เน้นของฉัน):

ส่วนหัวของไลบรารีมาตรฐาน D.5 C [depr.c.headers]

  1. สำหรับความเข้ากันได้กับไลบรารีมาตรฐาน C ...
  2. ทุกส่วนหัวของ C ซึ่งแต่ละส่วนมีชื่อของฟอร์มname.hจะทำงานเหมือนกับว่าแต่ละชื่อที่อยู่ในเนมสเปซไลบรารีมาตรฐานโดยส่วนหัวcname ที่เกี่ยวข้องจะถูกวางไว้ภายในขอบเขตเนมสเปซส่วนกลาง มันเป็นไม่ได้ระบุว่าชื่อเหล่านี้ได้รับการประกาศครั้งแรกหรือที่กำหนดไว้ภายในขอบเขต namespace (3.3.6) ของการ namespace มาตรฐานและได้รับการฉีดเข้าไปในขอบเขต namespace โลกโดยชัดเจนโดยใช้-ประกาศ (7.3.3)
  3. ตัวอย่าง:ส่วนหัว<cstdlib> แน่นอนให้ประกาศและคำจำกัดความของมันที่อยู่ในการ stdnamespace นอกจากนี้ยังอาจระบุชื่อเหล่านี้ภายในเนมสเปซส่วนกลาง <stdlib.h> แน่นอนว่าส่วนหัวจะให้การประกาศและคำจำกัดความเดียวกันภายในเนมสเปซส่วนกลางเช่นเดียวกับในมาตรฐาน C นอกจากนี้ยังอาจระบุชื่อเหล่านี้ภายในการ stdnamespace

การใช้ส่วนหัว« name.h »เลิกใช้งานแล้วพวกเขาถูกระบุว่าเป็นผู้สมัครที่จะนำออกจากการแก้ไขในอนาคต

ดังนั้นฉันขอแนะนำให้รวมส่วนหัว« cname »และใช้การประกาศและคำจำกัดความจากstdเนมสเปซ

หากคุณต้องใช้ส่วนหัว« name.h »ด้วยเหตุผลบางประการ (เลิกใช้แล้วดูด้านบน) ฉันขอแนะนำให้ใช้การประกาศและคำจำกัดความจากเนมสเปซส่วนกลาง

กล่าวอีกนัยหนึ่ง: ชอบ

#include <cstdio>

int main() {
    std::printf("Hello world\n");
}

เกิน

#include <stdio.h>

int main() {
    printf("Hello world\n");
}

1
N3242 ไม่ใช่มาตรฐาน C ++ ใด ๆ N3337 ร่างที่มีความแตกต่างน้อยที่สุดจาก C ++ 11
เอ็มเอ็ม

3
นอกจากนี้ดูว่าทำไม <cstdlib>ของ Jonathan Wakely ซับซ้อนกว่าที่คุณคิดจากบล็อก Red hat เขาให้รายละเอียดปัญหาหลายประการจากมุมมองของผู้ติดตั้งไลบรารีมาตรฐาน C ++ นอกจากนี้เขายังให้ประวัติย้อนกลับไปที่ C ++ 98
jww

@sergej - คุณจะรู้จักการรักษา C ++ 03 ในหัวข้อนี้หรือไม่? หรือโดนหรือพลาดจะเกิดอะไรขึ้น?
jww

5
<name.h> อาจเลิกใช้งานแล้วไม่มีโอกาสที่จะถูกลบออกในเร็ว ๆ นี้ ค่อนข้างตรงกันข้ามในความเป็นจริง มีข้อเสนอที่จะเอาป้ายที่เลิกคือดูopen-std.org/JTC1/SC22/WG21/docs/papers/2017/p0619r0.html#3.5 "ในที่สุดดูเหมือนชัดเจนว่าส่วนหัว C จะถูกคงไว้เป็นหลักตลอดไปในฐานะเลเยอร์ที่เข้ากันได้ที่สำคัญกับ C และ POSIX มันอาจจะคุ้มค่าที่จะไม่เข้าใจส่วนหัว [.. ]"
Sjoerd

82

<cmeow>ให้เสมอ::std::purrและอาจให้หรือไม่::purrก็ได้

<meow.h>ให้เสมอ::purrและอาจให้หรือไม่::std::purrก็ได้

ใช้แบบฟอร์มที่รับประกันโดยส่วนหัวที่คุณรวมไว้


7
STL ปลอมตัวไม่ดี?
nwp

@nwp ไม่. (15 ตัวอักษร)
TC

@TC แต่น่าเสียดายที่ผมพยายามคอมไพเลอร์ของฉันค่า<cmeow>มิได้<meow.h>ให้ค่า::std::purrมิได้::purrแต่ข้อผิดพลาดก่อนประมวลผล เท่านั้น<cstdio>และ / หรือ<stdio.h>ให้และ::std::printf / หรือ ::printf: P
LF

4
@LF คุณอาจจำเป็นที่จะผลิตstrcat ::purr
Lundin

8

ไม่คุณสบายดีทั้งสองทาง

ความตั้งใจเดิมคือ<___.h>ส่วนหัวจะเป็นเวอร์ชัน C ซึ่งใส่ทุกอย่างไว้ในเนมสเปซส่วนกลางและ<c___>ส่วนหัวจะเป็นเวอร์ชัน C ++ - ified ซึ่งวางทุกอย่างไว้ในstdเนมสเปซ

อย่างไรก็ตามในทางปฏิบัติเวอร์ชัน C ++ ยังรวมทุกอย่างไว้ในเนมสเปซส่วนกลาง และไม่มีความชัดเจนที่ชัดเจนว่าการใช้std::เวอร์ชันเป็น "สิ่งที่ควรทำ"

โดยพื้นฐานแล้วใช้สิ่งที่คุณต้องการ สิ่งที่พบบ่อยที่สุดคือการใช้ฟังก์ชันไลบรารีมาตรฐาน C ในเนมสเปซส่วนกลาง ( printfแทนที่จะเป็นstd::printf) แต่ไม่มีเหตุผลมากนักที่จะพิจารณาว่าหนึ่ง "ดีกว่า" อื่น ๆ


2
"และไม่มีความเห็นที่ชัดเจนว่าการใช้เวอร์ชัน std ::" เป็นสิ่งที่ถูกต้อง "" เอ่อใช่มีความเห็นตรงกันว่านั่นคือสิ่งที่ถูกต้องที่จะทำ
เส้นทางไมล์

4
วิธีการหนึ่งจะตัดสินอย่างเป็นกลางว่าได้รับฉันทามติหรือไม่?
Jeremy Friesner

9
@JeremyFriesner คุณโพสต์เกี่ยวกับเรื่องนี้ใน SO และดูว่าคุณมีความคิดเห็นที่ไม่เห็นด้วยหรือไม่ :)
jalf

1
@JeremyFriesner: มาตรฐานไม่รับประกันว่าเวอร์ชันส่วนหัว C ++ จะใส่ตัวระบุในเนมสเปซส่วนกลาง มาตรฐานยังเลิกใช้เวอร์ชันส่วนหัว C นั่นดูเป็นฉันทามติสำหรับฉัน ;-)
DevSolar

2
@DevSolar ดูคำว่า "ฉันทามติ" ในพจนานุกรมแล้ว มันไม่ได้เกี่ยวกับสิ่งที่มาตรฐานกล่าวว่า แต่สิ่งที่ C ++ โปรแกรมเมอร์พูด - และโดยเฉพาะอย่างยิ่งสิ่งที่พวกเขาทำ มีเหตุผลที่การใช้งานไลบรารีมาตรฐานทุกตัวจะมีส่วนหัว C และมีส่วนหัว C ++ ใส่ทุกอย่างไว้ในเนมสเปซส่วนกลางเช่นกัน :)
jalf

3

ความแตกต่างเพียงอย่างเดียวก็คือstd::printf()การเพิ่มstd::ความละเอียดขอบเขตคุณจะปลอดภัยจากคนที่เขียนฟังก์ชันที่มีชื่อเดียวกันในอนาคตซึ่งจะนำไปสู่ความขัดแย้งของเนมสเปซ การใช้งานทั้งสองจะนำไปสู่การเรียกใช้ OS API เหมือนกันstrace your_programทุกประการ(คุณสามารถตรวจสอบได้ใน Linux โดยเรียกใช้)

ฉันคิดว่ามันไม่น่าเป็นไปได้มากที่จะมีคนตั้งชื่อฟังก์ชันแบบนั้นเนื่องจากprintf()เป็นหนึ่งในฟังก์ชันที่ใช้บ่อยที่สุดในนั้น นอกจากนี้ใน C ++ iostreams ยังมีการเรียกใช้cstdioฟังก์ชันเช่น printf


1
ในทางตรงกันข้ามฉันพบว่ามันค่อนข้างเป็นไปได้: printfC ++ เสียอย่างมากเนื่องจากไม่มีการพิมพ์ที่หนักแน่นการแทนที่ด้วยเวอร์ชันที่ดีกว่านั้นค่อนข้างเป็นธรรมชาติ
Konrad Rudolph

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

1
@Alice อืมผมไม่ได้หมดสัญญาใด ๆ : std::printfจะแตกต่างจากmynamespace::printf, และ C ++ stdชัดเจนจะช่วยให้ฉันเพื่อกำหนดฟังก์ชั่นของตัวเองที่มีชื่อเงาผู้ที่มาจากฟังก์ชั่นภายใน นั่นไม่ใช่เรื่องที่ถกเถียงกันได้ สำหรับการอ้างสิทธิ์ของคุณที่printfมีประสิทธิภาพเนื่องจากการพิมพ์หลวมนั่นก็ผิดเช่นกัน printfไม่ได้มีประสิทธิภาพโดยเฉพาะอย่างยิ่งมีการใช้งานที่มีประสิทธิภาพมากขึ้นมากมายที่พิมพ์อย่างรุนแรง
Konrad Rudolph

@KonradRudolph ไม่ถูกต้องแน่นอน; คุณกำลังละเมิดสัญญาที่เขียนไว้ในมาตรฐาน printf ที่ไม่มีตัวบ่งชี้ใด ๆ จะใช้กับโครงสร้าง C อย่างชัดเจน การใช้เนมสเปซของคุณโดยใช้นามแฝงเนมสเปซส่วนกลางไม่ใช่ความคิดที่ดี นั่นไม่ใช่เรื่องที่ถกเถียงกันได้
Alice

5
@ อลิซคุณสามารถอ้างอิงมาตรฐานนี้ได้หรือไม่? ฉันไม่ทราบถึงการใช้คำฟุ่มเฟือยเช่นนี้
Konrad Rudolph

3

จากมาตรฐาน C ++ 11:

ทุกส่วนหัวของ C ซึ่งแต่ละส่วนมีชื่อของฟอร์ม name.h จะทำงานเหมือนกับว่าแต่ละชื่อที่อยู่ในเนมสเปซไลบรารีมาตรฐานโดยส่วนหัว cname ที่เกี่ยวข้องจะถูกวางไว้ภายในขอบเขตเนมสเปซส่วนกลาง ไม่ได้ระบุว่าชื่อเหล่านี้ถูกประกาศหรือกำหนดเป็นครั้งแรกภายในขอบเขตเนมสเปซ (3.3.6) ของมาตรฐานเนมสเปซจากนั้นจะถูกฉีดเข้าไปในขอบเขตเนมสเปซส่วนกลางโดยการประกาศโดยใช้การประกาศอย่างชัดเจน (7.3.3)

ดังนั้นหากคุณใช้<cstdio>คุณสามารถมั่นใจได้ว่าprintfจะอยู่ในnamespace stdนั้นและไม่ได้อยู่ในเนมสเปซส่วนกลาง
การใช้เนมสเปซส่วนกลางจะสร้างความขัดแย้งของชื่อ นี่ไม่ใช่วิธี C ++

ดังนั้นฉันใช้<cstdio>ส่วนหัวและแนะนำให้คุณทำเช่นนั้น


4
แม้ว่าฉันหวังว่ามันจะได้ผลแบบนี้ แต่ก็ไม่เป็นความจริง หากคุณรวมไว้<cstdio>คุณรับประกันว่า std :: printf จะมีอยู่จริง แต่ไม่มีการรับประกันจากมาตรฐาน if :: printf จะหรือจะไม่มีอยู่ด้วย ในความเป็นจริงในทุกคอมไพเลอร์ที่ผมเคยได้ยินของ :: printf ถูกฉีดเข้าไปใน namespace โลกเมื่อคุณ <cstdio>ได้แก่
wjl

3

จากการปฏิบัติของฉันเอง: ใช้std::คำนำหน้า มิฉะนั้นวันหนึ่งabs จะกัดคุณอย่างเจ็บปวดในกรณีที่คุณใช้จุดลอยน้ำ

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


2

การใช้เพียงแค่printfโดยไม่std::สามารถสร้างความขัดแย้งของชื่อและถือเป็นการปฏิบัติที่ไม่ดีโดยนักพัฒนา c ++ จำนวนมาก Google เป็นเพื่อนของคุณในอันนี้ แต่นี่คือลิงค์บางส่วนหวังว่านี่จะช่วยได้

เหตุใด "การใช้ namespace std" จึงถือว่าเป็นการปฏิบัติที่ไม่ดี http://www.cplusplus.com/forum/beginner/61121/


4
using namespace stdเป็นการปฏิบัติที่ไม่ดี แต่การใช้printfโดยไม่มีstd::คุณสมบัติไม่ได้
syntagma

using namespace std;ไม่ใช่ปัญหาของฉันที่นี่ ฉันไม่เคยใช้มัน printf();และstd::printf();ทำงานใน C ++ โดยไม่มีusing namespace std;นั่นคือเหตุผลที่ฉันโพสต์คำถาม
DeiDei

@REACHUS ไม่เห็นด้วย ไม่มีความแตกต่างระหว่างสองสถานการณ์
Konrad Rudolph

ฉันจะไม่ใช้std::printfมันให้ความรู้สึกแปลก ๆ
trenki

@KonradRudolph ฉันไม่ได้บอกว่ามีความแตกต่างฉันแค่แสดงความคิดเห็นของฉัน (ดูคำตอบของฉันสำหรับเหตุผลเพิ่มเติม)
syntagma

2

ใน stdio

นี่คือเวอร์ชัน C ++ ของ Standard C Library header @c stdio.h และเนื้อหา (ส่วนใหญ่) เหมือนกับส่วนหัวนั้น แต่ทั้งหมดอยู่ใน namespace @c std (ยกเว้นชื่อที่กำหนดเป็นมาโครใน ค).

ดังนั้นจึงไม่ควรสร้างความแตกต่างใด ๆ

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