ไกลแค่ไหนที่จะไปกับการพิมพ์ประเภทดั้งเดิมเช่น int


14

ฉันได้เห็นรหัส C ++ เช่นดังต่อไปนี้กับหลายtypedef

อะไรคือประโยชน์ของการใช้หลาย ๆtypedefอย่างเช่นนี้เมื่อเทียบกับการใช้ภาษาซีพลัสพลัส มีวิธีอื่นที่อาจบรรลุผลประโยชน์เหล่านั้นด้วยหรือไม่

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

types.h:

typedef int16_t Version;
typedef int32_t PacketLength;
typedef int32_t Identity;
typedef int32_t CabinetNumber;
typedef int64_t Time64;
typedef int64_t RFID;
typedef int64_t NetworkAddress;
typedef int64_t PathfinderAddress;
typedef int16_t PathfinderPan;
typedef int16_t PathfinderChannel;
typedef int64_t HandsetSerialNumber;
typedef int16_t PinNumber;
typedef int16_t LoggingInterval;
typedef int16_t DelayMinutes;
typedef int16_t ReminderDelayMinutes;
typedef int16_t EscalationDelayMinutes;
typedef float CalibrationOffset;
typedef float AnalogValue;
typedef int8_t PathfinderEtrx;
typedef int8_t DampingFactor;
typedef int8_t RankNumber;
typedef int8_t SlavePort;
typedef int8_t EventLevel;
typedef int8_t Percent;
typedef int8_t SensorNumber;
typedef int8_t RoleCode;
typedef int8_t Hour;
typedef int8_t Minute;
typedef int8_t Second;
typedef int8_t Day;
typedef int8_t Month;
typedef int16_t Year;
typedef int8_t EscalationLevel;

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

DoSomething(EscalationLevel escalationLevel) {
    ...
}

ซึ่งทำให้ฉันสงสัยว่าโทเค็นใดที่อธิบายพารามิเตอร์จริง ๆ : ชนิดพารามิเตอร์หรือชื่อพารามิเตอร์


2
IMHO ดูเหมือนว่าจะเป็นการออกกำลังกายที่ไร้จุดหมาย แต่ฉันแน่ใจว่าคนอื่น ๆ จะไม่เห็นด้วย ...
Nim

1
ประเภทเหล่านั้นมีลักษณะเหมือนชื่อตัวแปร
Captain Giraffe

11
โปรดทราบว่านี้จะสร้างความรู้สึกว่ามันเป็นชนิดปลอดภัย แต่มันไม่ได้อยู่ที่ทั้งหมด - typedefs เพียงสร้างชื่อแทน แต่ไม่มีอะไรหยุดคุณจากการผ่านเช่นฟังก์ชั่นที่มีการโต้แย้งประกาศเป็นประเภทMinute Second
Jesper

2
@ Mark: ดูอีกวิธีหนึ่ง หากคุณทำผิดพลาดในการตัดสินใจประเภทจำนวนเต็มหรือข้อกำหนดใหม่เกิดขึ้นในอนาคตและดังนั้นคุณต้องการที่จะเปลี่ยนแปลงคุณต้องการเปลี่ยน typedef เดียวหรือคุณต้องการค้นหารหัสสำหรับทุกฟังก์ชั่นที่จัดการปีและ เปลี่ยนลายเซ็นหรือไม่ 640k เพียงพอสำหรับทุกคนและทุกสิ่ง ข้อเสียที่สอดคล้องกับ typedef คือคนที่ตั้งใจเขียนโค้ดโดยบังเอิญหรือไม่ก็ขึ้นอยู่กับข้อเท็จจริงที่ว่าปีนั้นเป็นปีที่ 16 บิตจากนั้นมันจะเปลี่ยนและแบ่งรหัสของพวกเขา
Steve Jessop

1
@ Steve Jessop: ฉันไม่สามารถตัดสินใจได้ว่าคุณคิดว่ามันเป็นความคิดที่ดีหรือไม่ดี :-) ส่วนแรกดูเหมือนจะเป็นที่โปรดปราน ฉันเดาว่ามันมีข้อดีข้อเสียแล้ว

คำตอบ:


13

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

Typedefs มีประโยชน์สำหรับเทมเพลตหรือหากคุณต้องการเปลี่ยนประเภทที่ใช้สำหรับพารามิเตอร์บางตัวเช่นเมื่อย้ายจาก 32 บิตเป็นแพลตฟอร์ม 64 บิต


นี่ดูเหมือนจะเป็นฉันทามติทั่วไปแล้ว โดยทั่วไปแล้วคุณจะติดกับ "int" หรือไม่? ฉันต้องมีประสิทธิภาพมากในแอพนี้เมื่อพูดถึงการถ่ายโอนข้อมูลดังนั้นมันจะเป็นแค่การแปลงเป็น int16_t (หรือประเภทการแสดงที่ใหญ่ที่สุดที่จำเป็นสำหรับองค์ประกอบเฉพาะ) ระหว่างการทำให้เป็นอนุกรม?

@ Mark: ใช่นั่นเป็นวิธีที่คุณควรทำ ใช้ typedefs เพื่อแสดงขนาดของชนิดข้อมูลที่ใช้ แต่อย่าแยกประเภทเดียวกันกับที่ใช้ในบริบทที่แตกต่างกัน
Björn Pollex

ขอบคุณ - และเพื่อที่จะอธิบายให้ชัดเจนคุณจะไม่ต้องกังวลกับการใช้ int8_t แทนที่จะเป็น int โดยทั่วไปในรหัส .. ฉันคิดว่าปัญหาหลักของฉันคือ "Identity" ซึ่งเป็นฐานข้อมูลที่สร้างขึ้นจริง ในตอนนี้มันเป็น 32 บิต แต่ฉันก็ยังไม่แน่ใจว่าในที่สุดมันก็อาจกลายเป็น 64 บิตได้ไหม แล้ว int32_t กับ int ล่ะ? int มักจะเหมือนกับ int32_t แต่ฉันไม่สามารถเดาได้บนแพลตฟอร์มอื่นเสมอไป ฉันคิดว่าฉันก็ควรจะติด int "" โดยทั่วไปและ "int64_t" ที่จำเป็น .. ขอบคุณ :-)

@ Mark: สิ่งที่สำคัญเกี่ยวกับ typedefs เช่นint32_tนั้นคือคุณต้องแน่ใจว่ามันถูกต้องเมื่อทำการคอมไพล์บนแพลตฟอร์มที่ต่างกัน หากคุณคาดว่าช่วงของIdentityการเปลี่ยนแปลงในบางจุดฉันคิดว่าฉันต้องการทำการเปลี่ยนแปลงโดยตรงในรหัสที่ได้รับผลกระทบทั้งหมด แต่ฉันไม่แน่ใจเพราะฉันต้องการรู้เพิ่มเติมเกี่ยวกับการออกแบบเฉพาะของคุณ คุณอาจต้องการตั้งคำถามแยกต่างหาก
Björn Pollex

17

ตอนแรกฉันคิดว่า "ทำไมไม่" แต่มันก็เกิดขึ้นกับฉันว่าถ้าคุณจะไปที่ความยาวดังกล่าวเพื่อแยกประเภทเช่นนั้นแล้วให้ใช้ภาษาได้ดีขึ้น แทนที่จะใช้นามแฝงให้กำหนดประเภทจริง:

class AnalogueValue
{
public:
    // constructors, setters, getters, etc..
private:
    float m_value;
};

ไม่มีความแตกต่างด้านประสิทธิภาพระหว่าง:

typedef float AnalogueValue;
AnalogValue a = 3.0f;
CallSomeFunction (a);

และ:

AnalogValue a (3.0f); // class version
CallSomeFunction (a);

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

float amount = 10.00;
CallSomeFunction(amount);

นอกเหนือจากปัญหาการปัดเศษแล้วยังอนุญาตประเภทใด ๆ ที่สามารถแปลงเป็นทุ่น:

int amount = 10;
CallSomeFunction(amount);

ในกรณีนี้ไม่ใช่เรื่องใหญ่ แต่การแปลงโดยนัยอาจเป็นแหล่งของข้อบกพร่องที่ยากต่อการตรึง การใช้ a typedefไม่ได้ช่วยที่นี่เนื่องจากมันเป็นเพียงนามแฝงประเภท

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

class Money {
  Decimal amount;
  Currency currency;
};

Money m(Decimal("10.00"), Currency.USD);
CallSomeFunction(m);

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


1
คุณสามารถเขียนแมโครที่น่ากลัวเพื่อสร้างคลาสนี้ให้คุณได้ทั้งหมด (มาเลย Skizz คุณรู้หรือไม่ว่าคุณต้องการ)

1
สำหรับบางส่วนห้องสมุดหน่วยความปลอดภัยประเภทอาจเป็นประโยชน์ ( tuoml.sourceforge.net/html/scalar/scalar.html ) แทนที่จะเขียนคลาสที่กำหนดเองสำหรับแต่ละรายการ
Steve Jessop

@Chris - ไม่ใช่มาโครแน่นอน แต่อาจเป็นคลาสเทมเพลต ขณะที่สตีฟชี้ให้เห็นชั้นเรียนเหล่านั้นถูกเขียนไปแล้ว
วินไคลน์

4
@Chris: เรียกมาโครBOOST_STRONG_TYPEDEFจริง ๆ )
แมททิวเอ็ม

3

การใช้ typedefs สำหรับประเภทดั้งเดิมเช่นที่ดูเหมือนรหัสสไตล์ C

ใน C ++ คุณจะได้รับข้อผิดพลาดที่น่าสนใจเร็วที่สุดเท่าที่คุณพยายามที่จะฟังก์ชั่นเกินพิกัดสำหรับการพูดและEventLevel Hourทำให้ชื่อประเภทพิเศษนั้นไร้ประโยชน์


2

พวกเรา (ที่ บริษัท ของเรา) ทำมันมากใน C ++ ช่วยทำความเข้าใจและรักษารหัส ซึ่งเป็นสิ่งที่ดีเมื่อมีการเคลื่อนย้ายผู้คนระหว่างทีมหรือทำการปรับโครงสร้างใหม่ ตัวอย่าง:

typedef float Price;
typedef int64_t JavaTimestmap;

void f(JavaTimestamp begin, JavaTimestamp end, Price income);

เราเชื่อว่าเป็นวิธีปฏิบัติที่ดีในการสร้าง typedef ให้กับชื่อมิติจากประเภทการเป็นตัวแทน ชื่อใหม่นี้แสดงถึงบทบาททั่วไปในซอฟต์แวร์ ชื่อของพารามิเตอร์การให้เป็นบทบาทของท้องถิ่น ชอบUser sender, User receiverค่ะ ในบางที่มันสามารถซ้ำซ้อนvoid register(User user)ได้ แต่ฉันไม่คิดว่ามันจะเป็นปัญหา

หลังจากนั้นหนึ่งอาจมีความคิดที่floatไม่ดีที่สุดสำหรับการแสดงราคาเนื่องจากกฎการปัดเศษพิเศษของการจองดังนั้นหนึ่งการดาวน์โหลดหรือใช้ประเภทBCDFloat(ทศนิยมทศนิยมไบนารี) และเปลี่ยน typedef ไม่มีการค้นหาและแทนที่งานจากfloatการBCDFloatที่จะแข็งโดยความจริงที่ว่าอาจมีจำนวนมากลอยอยู่ในรหัสของคุณ

มันไม่ได้เป็น bullet เงินและมี caveats ของตัวเอง แต่เราคิดว่ามันดีกว่าการใช้มันมากกว่า


หรืออย่างที่ Mathieu M. แนะนำไว้ที่โพสต์ของ Skizz ใคร ๆ ก็สามารถทำได้BOOST_STRONG_TYPEDEF(float, Price)แต่ฉันจะไม่ไปไกลขนาดนั้นกับโครงการทั่วไป หรือบางทีฉันจะ ฉันต้องนอนบนมัน :-)
Notinlist

1

typedeftypeโดยทั่วไปช่วยให้คุณเพื่อให้นามแฝงสำหรับนักการ
มันจะช่วยให้คุณมีความยืดหยุ่นในการหลีกเลี่ยงการพิมพ์ยาวtype namesอีกครั้งและอีกครั้งและทำให้คุณได้ง่ายขึ้นสามารถอ่านได้นั้นชื่อนามแฝงที่บ่งชี้เจตนาหรือวัตถุประสงค์ของการtypetype

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


0

ฉันจะไม่ทำอะไรแบบนี้ ตรวจสอบให้แน่ใจว่าพวกเขาทั้งหมดมีขนาดเท่ากันเป็นสิ่งหนึ่ง - แต่คุณเพียงแค่ต้องอ้างถึงพวกเขาเป็นประเภทที่สำคัญแล้ว


0

ใช้ typedefs เช่นนี้ไม่เป็นไรตราบใดที่ใครก็ตามที่ลมขึ้นใช้พวกเขาไม่จำเป็นต้องรู้อะไรเกี่ยวกับการเป็นตัวแทนของพวกเขาอยู่ภายใต้ ตัวอย่างเช่นหากคุณต้องการส่งผ่านPacketLengthวัตถุใด ๆprintfหรือscanfคุณจะต้องทราบประเภทที่แท้จริงของมันเพื่อให้คุณสามารถเลือกตัวระบุการแปลงที่ถูกต้อง ในกรณีเช่นนี้ typedef จะเพิ่มระดับของการทำให้งงงวยโดยไม่ต้องซื้ออะไรตอบแทน; คุณอาจกำหนดวัตถุเป็นเช่นint32_tกัน

หากคุณต้องการบังคับใช้ความหมายเฉพาะสำหรับแต่ละประเภท (เช่นช่วงหรือค่าที่อนุญาต) คุณควรสร้างประเภทข้อมูลนามธรรมและฟังก์ชั่นเพื่อใช้งานกับประเภทนั้นแทนที่จะสร้างประเภท typedef

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