const ฟุ่มเฟือยพิเศษไม่ดีจากจุดยืน API:
ใส่ const ฟุ่มเฟือยพิเศษในรหัสของคุณสำหรับพารามิเตอร์ประเภทที่แท้จริงที่ส่งผ่านโดยค่าจะตัดAPI ของคุณในขณะที่ไม่มีสัญญาที่มีความหมายต่อผู้โทรหรือผู้ใช้ API
มี 'const' มากเกินไปใน API เมื่อไม่ต้องการก็เหมือน " crying wolf " ในที่สุดผู้คนก็จะไม่สนใจ 'const' เพราะมันอยู่ทั่วทุกที่และไม่มีความหมายอะไรเลย
อาร์กิวเมนต์ "reductio ad absurdum" สำหรับ const พิเศษใน API นั้นดีสำหรับสองจุดแรกนี้คือถ้าพารามิเตอร์ const มากกว่าดีแล้วทุกอาร์กิวเมนต์ที่สามารถมี const ได้นั้นควรมี const อยู่ ในความเป็นจริงถ้าดีจริง ๆ คุณต้องการให้ const เป็นค่าเริ่มต้นสำหรับพารามิเตอร์และมีคำหลักเช่น "ไม่แน่นอน" เฉพาะเมื่อคุณต้องการเปลี่ยนพารามิเตอร์
ดังนั้นให้ลองใส่ const ในทุกที่ที่เราทำได้:
void mungerum(char * buffer, const char * mask, int count);
void mungerum(char * const buffer, const char * const mask, const int count);
พิจารณาบรรทัดของรหัสข้างต้น ไม่เพียง แต่การประกาศจะมีความยุ่งเหยิงมากขึ้นและอ่านได้นานขึ้นและยากขึ้นเท่านั้น แต่ผู้ใช้ API สามารถละเว้นคำหลักสามในสี่ทั้งหมดได้อย่างปลอดภัย อย่างไรก็ตามการใช้งานพิเศษของ 'const' ทำให้บรรทัดที่สองอาจเป็นอันตรายได้!
ทำไม?
ความผิดพลาดอย่างรวดเร็วของพารามิเตอร์แรกchar * const buffer
อาจทำให้คุณคิดว่ามันจะไม่แก้ไขหน่วยความจำในบัฟเฟอร์ข้อมูลที่ส่งผ่าน - อย่างไรก็ตามสิ่งนี้ไม่เป็นความจริง!'const' ที่ไม่จำเป็นอาจนำไปสู่ข้อสันนิษฐานที่อันตรายและไม่ถูกต้องเกี่ยวกับ API ของคุณเมื่อสแกนหรืออ่านผิดอย่างรวดเร็ว
const ฟุ่มเฟือยก็ไม่ดีจากจุดยืนการติดตั้งโค้ดด้วยเช่นกัน:
#if FLEXIBLE_IMPLEMENTATION
#define SUPERFLUOUS_CONST
#else
#define SUPERFLUOUS_CONST const
#endif
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count);
หาก FLEXIBLE_IMPLEMENTATION ไม่เป็นความจริงแสดงว่า API นั้น“ มีแนวโน้ม” ที่จะไม่ใช้ฟังก์ชันในวิธีแรกด้านล่าง
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count)
{
// Will break if !FLEXIBLE_IMPLEMENTATION
while(count--)
{
*dest++=*source++;
}
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source, SUPERFLUOUS_CONST int count)
{
for(int i=0;i<count;i++)
{
dest[i]=source[i];
}
}
นั่นเป็นสัญญาที่โง่มากที่จะทำ ทำไมคุณควรทำสัญญาที่ไม่ให้ประโยชน์ใด ๆ แก่ผู้โทรและ จำกัด การใช้งานของคุณเท่านั้น?
ทั้งสองอย่างนี้เป็นการนำไปใช้ที่ถูกต้องสมบูรณ์แบบของฟังก์ชั่นเดียวกัน แต่สิ่งที่คุณทำคือการผูกมือข้างหนึ่งไว้ด้านหลังโดยไม่จำเป็น
ยิ่งกว่านั้นมันเป็นสัญญาที่ตื้นเขินมากซึ่งง่าย (และหลีกเลี่ยงกฎหมาย)
inline void bytecopyWrapped(char * dest,
const char *source, int count)
{
while(count--)
{
*dest++=*source++;
}
}
void bytecopy(char * SUPERFLUOUS_CONST dest,
const char *source,SUPERFLUOUS_CONST int count)
{
bytecopyWrapped(dest, source, count);
}
ดูฉันใช้มันด้วยวิธีใด ๆ แม้ว่าฉันสัญญาว่าจะไม่ - เพียงแค่ใช้ฟังก์ชั่นเสื้อคลุม มันเหมือนเมื่อคนเลวสัญญาว่าจะไม่ฆ่าคนในภาพยนตร์และสั่งให้ผู้ติดตามของเขาฆ่าพวกเขาแทน
กลุ่มของฟุ่มเฟือยเหล่านั้นมีค่าไม่เกินสัญญาจากภาพยนตร์คนเลว
แต่ความสามารถในการโกหกยิ่งแย่ลงไปอีก:
ฉันรู้แจ้งแล้วว่าคุณไม่สามารถจับ const ในส่วนหัว (ประกาศ) และรหัส (definition) โดยใช้ const ปลอม ผู้ให้การสนับสนุนที่มีความสุขอ้างว่าสิ่งนี้เป็นสิ่งที่ดีเพราะมันช่วยให้คุณใส่ความหมายได้เฉพาะในคำจำกัดความ
// Example of const only in definition, not declaration
class foo { void test(int *pi); };
void foo::test(int * const pi) { }
อย่างไรก็ตามการสนทนานั้นเป็นจริง ... คุณสามารถใส่กลุ่มปลอมในการประกาศและไม่สนใจมันในคำจำกัดความ สิ่งนี้ทำให้ const ที่ฟุ่มเฟือยใน API ยิ่งน่ากลัวยิ่งขึ้นและเป็นเรื่องโกหกที่น่ากลัว - ดูตัวอย่างนี้:
class foo
{
void test(int * const pi);
};
void foo::test(int *pi) // Look, the const in the definition is so superfluous I can ignore it here
{
pi++; // I promised in my definition I wouldn't modify this
}
const ที่ฟุ่มเฟือยทั้งหมดจริง ๆ แล้วจะทำให้โค้ดของ implementer อ่านได้น้อยลงโดยบังคับให้เขาใช้สำเนาโลคัลอื่นหรือฟังก์ชัน wrapper เมื่อเขาต้องการเปลี่ยนตัวแปรหรือส่งผ่านตัวแปรโดยการอ้างอิงที่ไม่ใช่ const
ดูตัวอย่างนี้ ข้อใดอ่านได้มากกว่า เห็นได้ชัดว่าเหตุผลเดียวที่ทำให้ตัวแปรพิเศษในฟังก์ชั่นที่สองเป็นเพราะนักออกแบบ API บางคนขว้าง const ที่ฟุ่มเฟือย?
struct llist
{
llist * next;
};
void walkllist(llist *plist)
{
llist *pnext;
while(plist)
{
pnext=plist->next;
walk(plist);
plist=pnext; // This line wouldn't compile if plist was const
}
}
void walkllist(llist * SUPERFLUOUS_CONST plist)
{
llist * pnotconst=plist;
llist *pnext;
while(pnotconst)
{
pnext=pnotconst->next;
walk(pnotconst);
pnotconst=pnext;
}
}
หวังว่าเราจะได้เรียนรู้บางสิ่งที่นี่ const ฟุ่มเฟือยเป็นสิ่งที่อุจาดนัยน์ตา API, รก, น่ารำคาญ, สัญญาตื้นและไม่มีความหมาย, อุปสรรคที่ไม่จำเป็นและบางครั้งนำไปสู่ข้อผิดพลาดที่อันตรายมาก