กลับไปหน้าบทความ

อ่าน 4 นาที

กฎนิยามข้อเดียว

ซี++

กฎการนิยามเดียว ( One Definition RuleหรือODR ) เป็นกฎสำคัญของภาษาโปรแกรมC++ ที่กำหนดว่า คลาส/โครงสร้าง และฟังก์ชันที่ไม่ใช่แบบอินไลน์...

กฎนิยามข้อเดียว

กฎการนิยามเดียว ( One Definition RuleหรือODR ) เป็นกฎสำคัญของภาษาโปรแกรมC++ ที่กำหนดว่า คลาส/โครงสร้าง และฟังก์ชันที่ไม่ใช่แบบอินไลน์ ไม่สามารถมีการนิยามมากกว่าหนึ่งครั้งในโปรแกรมทั้งหมด และเทมเพลตและชนิดข้อมูล ไม่สามารถมีการนิยามมากกว่าหนึ่งครั้งต่อหน่วยการแปล กฎนี้ถูกกำหนดไว้ในมาตรฐาน ISO C++ ( ISO/IEC 14882 ) ปี 2003 ในส่วนที่ 3.2 ภาษาโปรแกรมอื่นๆ บางภาษาก็มีกฎที่คล้ายกัน แต่มีการกำหนดที่แตกต่างกันออกไปเพื่อจุดประสงค์เดียวกัน

สรุป

โดยสรุป ODR ระบุว่า:

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

การละเมิด ODR บางอย่างจะต้องได้รับการวินิจฉัยโดยคอมไพเลอร์การละเมิดอื่นๆ โดยเฉพาะอย่างยิ่งการละเมิดที่ครอบคลุมหน่วยการแปล ไม่จำเป็นต้องได้รับการวินิจฉัย[ 1 ]

ตัวอย่าง

โดยทั่วไป หน่วยการแปลควรมีคำจำกัดความของคลาสประเภทใดประเภทหนึ่งไม่เกินหนึ่งรายการ ในตัวอย่างนี้ มีคำจำกัดความของคลาสประเภทเดียวกันสองรายการMyClassอยู่ในหน่วยการแปล เดียวกัน ซึ่งมักเกิดขึ้นหาก มีการรวม ไฟล์ส่วนหัวสองครั้งโดยไฟล์ต้นฉบับเดียวกันโดยไม่มีการป้องกันส่วนหัว ที่เหมาะสม

class MyClass {}; // การนิยาม MyClass ครั้งแรกclass MyClass {}; // เกิดข้อผิดพลาด การนิยาม MyClass ครั้งที่สอง

ต่อไปนี้ การสร้างตัวชี้ไปยังMyStructหรือการกำหนดฟังก์ชันที่รับการอ้างอิงไปยังMyStructเป็นตัวอย่างของโครงสร้างที่ถูกต้องตามกฎหมาย เนื่องจากไม่จำเป็นต้องให้ประเภทของMyStructสมบูรณ์ดังนั้นจึงไม่จำเป็นต้องมีการกำหนด[ 2 ]

การกำหนดวัตถุประเภทMyStructฟังก์ชันที่รับอาร์กิวเมนต์ประเภทMyStructหรือการใช้MyStructใน นิพจน์ sizeofเป็นตัวอย่างของบริบทที่ S ต้องสมบูรณ์ และดังนั้นจึงต้องมีการกำหนด[ 2 ]

struct MyStruct ; // การประกาศ MyStruct MyStruct * p ; // ถูกต้อง ไม่จำเป็นต้องกำหนดนิยามvoid f ( MyStruct & ); // ถูกต้อง ไม่จำเป็นต้องกำหนดนิยามvoid f ( MyStruct * ); // ถูกต้อง ไม่จำเป็นต้องกำหนดนิยามMyStruct f (); // ถูกต้อง ไม่จำเป็นต้องกำหนดนิยาม - นี่เป็นการประกาศฟังก์ชันเท่านั้น!MyStruct s ; // เกิดข้อผิดพลาด ต้องกำหนดsizeof ( MyStruct ); // เกิดข้อผิดพลาด ต้องกำหนด sizeof ( MyStruct );

มากกว่าหนึ่งคำจำกัดความ

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

หากโปรแกรมมีคำจำกัดความของประเภทมากกว่าหนึ่งรายการ คำจำกัดความแต่ละรายการจะต้องเทียบเท่ากัน[ 3 ]

นิยามของสมาชิกข้อมูลคงที่แบบ static

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

struct MyClass { static const int N = 10 ; }; char data [ MyClass :: N ]; // N "ถูกใช้งาน" โดยไม่ต้องกำหนดนิยามภายนอกคลาส

โดยไม่มี การกำหนดขอบเขต เนมสเปซสำหรับN.

อย่างไรก็ตาม ถ้อยคำของมาตรฐาน C++ ปี 1998 ยังคงกำหนดให้ต้องมีการกำหนดสมาชิกหากมีการใช้สมาชิกนั้นในโปรแกรม[ 4 ]ซึ่งรวมถึงสมาชิกที่ปรากฏที่ใดก็ได้ยกเว้นในฐานะตัวดำเนินการของsizeofหรือtypeidซึ่งทำให้รูปแบบข้างต้นไม่ถูกต้อง[ 5 ]

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

struct MyClass1 { static const int N = 10 ; static const int U = N ; // ถูกต้องตามกฎ C++03 };char data [ Class1 :: N ]; // ถูกต้องตาม C++03template < int > struct MyClass2 ;template <> struct MyClass2 < Class1 :: N > {}; // ถูกต้องตามหลัก C++03

อย่างไรก็ตาม การใช้สมาชิกอินทิกรัลคงที่คงที่ทุกที่ยกเว้นในกรณีที่ต้องใช้นิพจน์ค่าคงที่อินทิกรัล ต้องมีการกำหนด: [ 7 ]

struct MyClass { static const int N = 10 ; };int main () { int i = MyClass :: N ; // รูปแบบไม่ถูกต้องใน C++03 ต้องมีการกำหนด MyClass::N }

ข้อกำหนดนี้ได้รับการผ่อนปรนในมาตรฐานC++11ใน ภายหลัง [ 7 ]

ตัวอย่างที่แสดงผลข้างเคียงที่ไม่คาดคิด

เราต้องการไฟล์ 4 ไฟล์ ได้แก่ " Base.cppm ", " Dummy1.cppm ", " Dummy2.cppm " และ " Main.cpp "

Base.cppm :

โมดูล;ส่งออกโมดูลวิกิพีเดียตัวอย่างฐาน;ส่งออกเนมสเปซวิกิพีเดีย:: examples {// คลาสพื้นฐานนามธรรม class Base { public : virtual void myFunc ( ) = 0 ; virtual ~ Base () = default ; };}

ดัมมี่1.ซีพีพี

โมดูล;ส่งออกโมดูลวิกิพีเดียตัวอย่างดัมี่2 ;import std ;import wikipedia.examples.Base ;ส่งออกเนมสเปซวิกิพีเดีย:: examples {คลาสDummy : public Base { public : void myFunc () override { std :: println ( "odr ONE dummy: Hello" ); } };Base * odr1Create () { return new Dummy (); }}

Dummy2.cppm :

โมดูล;ส่งออกโมดูลวิกิพีเดียตัวอย่างดัมี่2 ;import std ;import wikipedia.examples.Base ;ส่งออกวิกิพีเดีย:: ตัวอย่าง{class Dummy : public Base { public : void myFunc () override { std :: println ( "odr TWO dummy: World" ); } };Base * odr2Create () { return new Dummy (); }}

Main.cpp :

import wikipedia.examples.Base ; import wikipedia.examples.Dummy1 ; import wikipedia.examples.Dummy2 ;using wikipedia :: examples :: Base ; using wikipedia :: examples :: Dummy1 ; using wikipedia :: examples :: Dummy2 ;int main ( int argc , char * argv []) { Base * o1 = odr1Create (); Base * o2 = odr2Create (); o1 -> myFunc (); o2 -> myFunc ();ลบo1 ; ลบo2 ; }

เมื่อดำเนินการแล้ว ผลลัพธ์ ที่คาดหวังคือ:

odr ONE หุ่นจำลอง: สวัสดี odr TWO dummy: World 

แต่ผลลัพธ์ที่น่าจะเป็นคือ:

odr ONE หุ่นจำลอง: สวัสดี odr ONE หุ่นจำลอง: สวัสดี 

ปัญหาคือ ตัวเชื่อมโยงของ C++ ต้องหาวิธีสร้างตารางเมธอดเสมือนสำหรับDummyคลาส (สองคลาสที่แตกต่างกัน) และวิธีนี้จะใช้ได้ผลก็ต่อเมื่อชื่อคลาสแตกต่างกันเท่านั้น

ดูเพิ่มเติม

ดึงข้อมูลมาจาก " https://en.wikipedia.org/w/index.php?title=One_Definition_Rule&oldid=1340046117 "

สรุปเนื้อหา

ข้อมูลสำคัญจากบทความ

ข้อมูลสำคัญเกี่ยวกับ กฎนิยามข้อเดียว

กฎการนิยามเดียว ( One Definition RuleหรือODR ) เป็นกฎสำคัญของภาษาโปรแกรมC++ ที่กำหนดว่า คลาส/โครงสร้าง และฟังก์ชันที่ไม่ใช่แบบอินไลน์...

ตัวอย่าง

โดยทั่วไป หน่วยการแปลควรมีคำจำกัดความของคลาสประเภทใดประเภทหนึ่งไม่เกินหนึ่งรายการ ในตัวอย่างนี้ มีคำจำกัดความของคลาสประเภทเดียวกันสองรายการ MyClass อยู่ใน หน่วยการแปล เดียวกัน ซึ่งมักเกิดขึ้นหาก มีการรวม ไฟล์ส่วนหัว สองครั้งโดยไฟล์ต้นฉบับเดียวกันโดยไม่มีการ...

มากกว่าหนึ่งคำจำกัดความ

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

นิยามของสมาชิกข้อมูลคงที่แบบ static

ในภาษา C++ ก่อนการกำหนดมาตรฐาน สมาชิกข้อมูลแบบ static ทั้งหมดจำเป็นต้องมีการนิยามไว้นอกคลาสของมัน อย่างไรก็ตาม ในระหว่างกระบวนการกำหนดมาตรฐานของ C++ ได้มีการตัดสินใจยกเลิกข้อกำหนดนี้สำหรับสมาชิกจำนวนเต็มแบบ static const...