เป็นความคิดที่ดีหรือไม่ที่จะ“ #define me (* this)”?


19

แมโครนี้สามารถกำหนดในส่วนหัวร่วมบางส่วนหรือดีกว่าเป็นพารามิเตอร์บรรทัดคำสั่งคอมไพเลอร์:

#define me (*this)

และตัวอย่างการใช้งาน:

some_header.h:

inline void Update()
{
    /* ... */
}

main.cpp:

#include "some_header.h"

class A {
public:
    void SetX(int x)
    {
        me.x = x;   
        me.Update(); 
    }

    void SomeOtherFunction()
    {
        ::Update();
    }

    /*
        100 or more lines
        ... 
    */

    void Update()
    {
        // ... 
    }

    int x;  
};

ดังนั้นในวิธีการเรียนเมื่อฉันเข้าถึงสมาชิกชั้นเรียนผมมักจะใช้และเมื่อมีการเข้าถึงตัวระบุทั่วโลกผมใช้เสมอme ::สิ่งนี้จะช่วยให้ผู้อ่านที่ไม่คุ้นเคยกับรหัส (อาจเป็นตัวฉันเองหลังจากผ่านไปไม่กี่เดือน) ข้อมูลที่แปลเป็นภาษาท้องถิ่นของสิ่งที่เข้าถึงได้โดยไม่จำเป็นต้องดูที่อื่น ฉันต้องการกำหนดmeเพราะฉันพบว่าการใช้this->ทุกที่ที่มีเสียงดังและน่าเกลียดเกินไป แต่#define me (*this)ถือได้ว่าเป็นการฝึกภาษา C ++ ที่ดี? มีบางประเด็นที่เป็นประโยชน์กับmeแมโครหรือไม่ และถ้าคุณเป็นโปรแกรมเมอร์ C ++ จะเป็นผู้อ่านโค้ดบางตัวโดยใช้meมาโครคุณจะชอบหรือไม่?

แก้ไข:เนื่องจากมีคนจำนวนมากที่ไม่โต้แย้งการใช้ในทางตรงกันข้ามmeแต่ในทางตรงกันข้ามมันชัดเจน ฉันคิดว่ามันอาจจะไม่ชัดเจนว่าสิ่งที่เป็นประโยชน์ของ "ชัดเจนนี้ทุกที่"

อะไรคือประโยชน์ของ "ชัดเจนทุกที่"?

  • ในฐานะผู้อ่านรหัสคุณมีความมั่นใจว่ามีการเข้าถึงอะไรบ้างและคุณสามารถจดจ่อกับสิ่งต่าง ๆ ได้มากกว่าการตรวจสอบยืนยันในรหัสที่อยู่ไกลออกไปซึ่งเข้าถึงได้จริง ๆ ในสิ่งที่คุณคิดว่าเข้าถึงได้
  • คุณสามารถใช้ฟังก์ชั่นการค้นหาได้มากขึ้นโดยเฉพาะ การค้นหา " this->x" สามารถให้ผลลัพธ์ที่คุณต้องการมากกว่าการค้นหา " x" เท่านั้น
  • เมื่อคุณลบหรือเปลี่ยนชื่อสมาชิกคอมไพเลอร์จะแจ้งเตือนคุณในที่ที่สมาชิกนี้ใช้งานอยู่ (ฟังก์ชั่นทั่วโลกบางอย่างสามารถมีชื่อเดียวกันและมีโอกาสที่คุณสามารถแนะนำข้อผิดพลาดหากคุณไม่ได้ใช้สิ่งนี้อย่างชัดเจน)
  • เมื่อคุณกำลัง refactoring รหัสและทำให้ฟังก์ชั่นที่ไม่ใช่สมาชิกจากสมาชิก (เพื่อให้ encapsulation ดีขึ้น) ชัดเจนนี้แสดงสถานที่ที่คุณต้องแก้ไขและคุณสามารถแทนที่ได้อย่างง่ายดายด้วยตัวชี้ไปยังอินสแตนซ์ของคลาสที่กำหนดให้เป็นพารามิเตอร์ฟังก์ชั่นที่ไม่ใช่สมาชิก
  • โดยทั่วไปเมื่อคุณเปลี่ยนรหัสมีความเป็นไปได้มากกว่าที่จะเกิดข้อผิดพลาดเมื่อคุณไม่ได้ใช้สิ่งนี้อย่างชัดเจนมากกว่าเมื่อคุณใช้รหัสนี้อย่างชัดเจนทุกที่
  • เสียงนี้ชัดเจนน้อยกว่าเสียงรบกวนอย่างชัดเจน„ m_“ เมื่อคุณเป็นสมาชิกจากภายนอก ( object.membervs object.m_member) (ขอบคุณ @Kaz ที่จะตรวจสอบจุดนี้)
  • วิธีนี้จะช่วยแก้ปัญหาที่ชัดเจนสำหรับสมาชิกทุกคน - คุณลักษณะและวิธีการในขณะที่„ m_“ หรือคำนำหน้าอื่น ๆ นั้นสามารถนำไปใช้งานได้จริงสำหรับคุณลักษณะเท่านั้น

ผมอยากจะขัดและขยายรายการนี้บอกฉันว่าคุณรู้เกี่ยวกับผลประโยชน์อื่น ๆ และกรณีการใช้งานสำหรับการอย่างชัดเจนนี้ในทุกที่


3
คุณควรหลีกเลี่ยงการมีอาร์กิวเมนต์ของฟังก์ชันและสมาชิกที่มีชื่อเหมือนกัน
pjc50

4
เตือนฉันเกี่ยวกับรหัส VB ​​ของเจ้านายของฉัน ฉันฉันฉันทุกที่ เสียงเห็นแก่ตัว : p อย่างไรก็ตามยังมีคำตอบให้พูดทั้งหมด
MetalMikester

19
มันเป็นความคิดที่ยอดเยี่ยม! และสำหรับผู้ที่มาจากภูมิหลังของ Python ทำไม#define self (*this)ล่ะ? คุณสามารถผสมทั้งสองมาโครและมีบางไฟล์ที่เลียนแบบ VB และ Python อื่น ๆ :)
logc

11
ทำไมไม่เพียงออกไปทั้งหมดและทำ#include "vb.h", #Include pascal.hหรือ#include FOTRAN.hมีคนต่อไปที่จะสัมผัสรหัสของคุณส่งไปยังTDWTF
Dan Neely

4
โอ้ได้โปรดอย่า คุณสามารถช่วยตัวเองให้เดือดร้อนด้วยการประกาศคุณลักษณะme_xต่างๆดังนี้
Gort the Robot

คำตอบ:


90

ไม่มันไม่ใช่.

ระวังผู้เขียนโปรแกรมที่จะรักษารหัสของคุณเป็นเวลาหลายปีนับจากนี้หลังจากที่คุณออกจากทุ่งหญ้าสีเขียวและทำตามแบบแผนทั่วไปของภาษาที่คุณใช้ ใน C ++, คุณเกือบจะไม่ต้องเขียนthisเพราะชั้นจะรวมอยู่ในสัญลักษณ์เพื่อความละเอียดและเมื่อเป็นสัญลักษณ์ที่พบในขอบเขตชั้นthis->บอกเป็นนัย ๆ ดังนั้นอย่าเขียนเหมือนที่ทุกคนทำ

หากคุณมักจะสับสนว่าสัญลักษณ์ใดมาจากขอบเขตของคลาสวิธีปกติคือการใช้รูปแบบการตั้งชื่อทั่วไปสำหรับสมาชิก (เขตข้อมูลและวิธีการส่วนตัวบางครั้งฉันไม่เห็นมันใช้สำหรับวิธีสาธารณะ) คนทั่วไปรวมถึง suffixing ด้วย_หรือ prefixing ด้วยหรือm_m


12
"ใน C ++ คุณแทบไม่ต้องเขียนสิ่งนี้เลย"นั่นเป็นข้อตกลงที่ไม่เกี่ยวข้องกับคำถามทั้งหมด บางคน (เช่นฉัน) ชอบเขียนthisเพื่อให้ชัดเจนว่าตัวแปรไม่ได้อยู่ในวิธีการ คำถามที่นี่เป็นเรื่องเกี่ยวกับว่าmeควรมีมาโครหรือไม่ไม่ใช่thisสิ่งที่ควรใช้
Mehrdad

8
@Mehrdad: เพราะ OP อย่างชัดเจนว่าเขาใช้แทนme. this->เนื่องจากไม่จำเป็นต้องthis->มีจึงไม่จำเป็นme.และไม่จำเป็นต้องมีมาโคร
Martin York

11
@ LokiAstari: ไม่มีคำถามแม้แต่คำแนะนำจาก OP จากระยะไกลด้วยความสงสัยว่าthis->"จำเป็น" หรือไม่ ในความเป็นจริง OP บอกว่าเขาใช้this->ทั้งๆที่ไม่จำเป็น คำถามเกี่ยวกับว่าme.ควรจะใช้แทน this->ซึ่งคำตอบนี้ไม่ได้ตอบจริง
Mehrdad

4
@ Mehrdad: แต่เพียงใส่ใช่หรือไม่ทำให้คำตอบที่น่าเบื่อมาก ดังนั้นคำอธิบายมักจะมีประโยชน์มากในการอธิบายวิธีการเข้าถึงคำตอบ (ถูกเข้ารหัสโดยวิธีการตอบ ) ข้อสรุปนี้ไม่ควรใช้เนื่องจาก OP มีทัศนคติที่ไม่ถูกต้องในการเริ่มต้นเกี่ยวกับการใช้thisและทำให้ต้องมีการอภิปรายในประเด็นนั้นเพื่อให้ได้ข้อสรุปที่สมดุลอย่างสมบูรณ์
มาร์ตินยอร์

1
+1 เพื่ออธิบายว่าทำไมแทนที่จะพูดว่า "ไม่มันเป็นวิธีปฏิบัติที่ไม่ดี"
user253751

42

ดังนั้นคุณต้องการสร้างภาษาใหม่ จากนั้นทำเช่นนั้นและอย่าทำลาย C ++

มีสาเหตุหลายประการที่ไม่ควรทำ:

  1. ทุกมาตรฐานการเข้ารหัสปกติจะแนะนำให้หลีกเลี่ยงมาโคร ( นี่คือเหตุผล )
  2. การบำรุงรักษาโค้ดด้วยมาโครนั้นทำได้ยากขึ้น การเขียนโปรแกรมทุกคนใน C ++ รู้ว่าอะไรthisคืออะไรและด้วยการเพิ่มมาโครเช่นนี้คุณจะเพิ่มคำหลักใหม่ ถ้าทุกคนแนะนำสิ่งที่พวกเขาชอบ รหัสจะมีลักษณะอย่างไร
  3. คุณไม่ควรใช้(*this).หรือthis->เลยยกเว้นในบางกรณีพิเศษ (ดูคำตอบนี้และค้นหา "this->")

รหัสของคุณไม่แตกต่างจาก#define R returnที่ฉันเห็นในรหัสจริง เหตุผล? พิมพ์น้อยลง!


ออกนอกหัวข้อเล็กน้อย แต่ที่นี่ฉันจะขยายในจุดที่ 3 (อย่าใช้(*this).หรือthis->ในชั้นเรียน)

ก่อนอื่น(*this).หรือthis->ใช้เพื่อเข้าถึงตัวแปรสมาชิกหรือฟังก์ชั่นของวัตถุ การใช้มันไร้ความหมายและหมายถึงการพิมพ์ที่มากขึ้น นอกจากนี้การอ่านรหัสดังกล่าวก็ยากขึ้นเพราะมีข้อความมากขึ้น นั่นหมายถึงการบำรุงรักษายากขึ้น

ดังนั้นกรณีที่คุณต้องใช้this->คืออะไร?

(a) โชคไม่ดีที่เลือกชื่ออาร์กิวเมนต์

ในตัวอย่างthis->นี้จำเป็นเนื่องจากอาร์กิวเมนต์มีชื่อเหมือนกับตัวแปรสมาชิก:

struct A {
  int v;
  void foo( int v ) {
    this->v =v;
  }
};

(b) เมื่อต้องรับมือกับแม่แบบและการสืบทอด (ดูนี้ )

ตัวอย่างนี้จะไม่สามารถคอมไพล์ได้เนื่องจากคอมไพเลอร์ไม่ทราบว่าชื่อตัวแปรใดvที่เข้าถึง

template< typename T >
struct A {
  A(const T& vValue):v(vValue){}

  T v;
};

template< typename T >
struct B : A<T>
{
    B(const T& vValue):A<T>(vValue){}

    void foo( const T & newV ) {
      v = newV;
    }
};

1
"ดังนั้นคุณต้องการสร้างภาษาใหม่จากนั้นทำเช่นนั้นและอย่าทำลาย c ++" - ฉันไม่เห็นด้วย. จุดแข็งที่สำคัญของ C และ C ++ คือความยืดหยุ่น OP ไม่ได้ทำให้ C + + หมดอำนาจเพียงแค่ใช้ความยืดหยุ่นในวิธีการเฉพาะเพื่อให้ง่ายต่อการเขียนโค้ด
user253751

2
@immibis ถูกต้อง สิ่งที่ง่ายกว่าสำหรับเขาคือยากสำหรับทุกคน เมื่อคุณทำงานเป็นทีมการใส่ความไร้สาระดังกล่าวลงในโค้ดจะไม่ทำให้คุณชื่นชอบมาก
BЈовић

2
คำตอบของคุณไม่แตกต่างจาก "กำหนดเป็นสิ่งที่ชั่วร้าย"
นาย Lister

7
@MrLister คำตอบของฉันคือ "นิยามโง่ ๆ ที่ชั่วร้าย" มาโครในคำถามนั้นโง่ที่จะใช้และอย่างที่ฉันแสดงไม่จำเป็นเลย รหัสเป็นสิ่งที่ดีและดียิ่งขึ้นโดยไม่ต้อง*this.และthis->
BЈовић

1
@Mehrdad คนยังคงเพิ่มคำนำหน้าและคำต่อท้ายให้กับตัวแปรสมาชิกหรือไม่? ผมคิดว่าก็คือ "คงที่" กับหนังสือเช่นรหัสสะอาด this->มีประโยชน์เช่นเดียวกับautoใน pre-c ++ 11 กล่าวอีกอย่างหนึ่งก็เพียงเพิ่มรหัสเสียง
BЈовић

26

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

การใช้สิ่งนี้ -> ทุกที่มีเสียงดังและน่าเกลียดเกินไป

อาจเป็นเช่นนั้นสำหรับคุณอาจเป็นเพราะคุณเขียนโปรแกรมเป็นภาษาจำนวนมากโดยใช้คำหลักme(ฉันคิดว่า Visual Basic หรือไม่) แต่ในความเป็นจริงมันเป็นเรื่องของการคุ้นเคยกับมัน - มันthis->ค่อนข้างสั้นและฉันคิดว่าโปรแกรมเมอร์ C ++ ที่มีประสบการณ์ส่วนใหญ่จะไม่เห็นด้วยกับความคิดเห็นของคุณ และในกรณีข้างต้นการใช้this->หรือการใช้meไม่เหมาะสม - คุณจะได้รับความยุ่งเหยิงในปริมาณที่น้อยที่สุดโดยปล่อยให้คำหลักเหล่านั้นออกเมื่อเข้าถึงข้อมูลสมาชิกภายในฟังก์ชั่นสมาชิก

หากคุณต้องการให้ตัวแปรสมาชิกส่วนตัวของคุณแตกต่างจากโลคัลให้เพิ่มลิงก์บางอย่างm_เป็นคำนำหน้าหรือขีดล่างเป็นส่วนต่อท้าย (แต่อย่างที่คุณเห็นที่นี่แม้แต่อนุสัญญานี้ก็มีเสียงดังเกินไป


12
_ควรต่อท้าย ; ส่วนนำหน้าจะถูกสงวนไว้สำหรับสัญลักษณ์ภายในของไลบรารีมาตรฐานและส่วนขยายผู้ขาย
Jan Hudec

4
@JanHudec เฉพาะเมื่อเริ่มต้นด้วย__(ขีดล่างสองอัน) หรือขีดล่างตามด้วยตัวพิมพ์ใหญ่ เครื่องหมายขีดล่างเดี่ยวตามด้วยตัวอักษรตัวพิมพ์เล็กสามารถใช้ได้ตราบใดที่ไม่ได้อยู่ในเนมสเปซส่วนกลาง
Eric Finn

1
@EricFinn: ฉันรวมสองกฎเข้าด้วยกัน เครื่องหมายขีดล่างหรือเครื่องหมายขีดล่างสองตัวตามด้วยตัวพิมพ์ใหญ่สำหรับสัญลักษณ์ภายในและขีดล่างเดียวตามด้วยตัวอักษรตัวพิมพ์เล็กสำหรับส่วนขยายเฉพาะสำหรับระบบ แอปพลิเคชันไม่ควรใช้อย่างใดอย่างหนึ่ง
Jan Hudec

@JanHudec ไม่ทราบถึงกฎสำหรับส่วนขยายเฉพาะระบบ ฉันจะแสดงความคิดเห็นก่อนหน้าของฉันเพื่อให้บริบทสำหรับความคิดเห็นของคุณ
Eric Finn

2
@JanHudec: ตัวพิมพ์เล็กสำหรับส่วนขยายเฉพาะระบบหรือไม่ คุณสามารถโพสต์ข้อมูลอ้างอิงไปยังส่วนของมาตรฐาน C ++ ที่ระบุสิ่งนี้ได้หรือไม่
Mehrdad

18

โปรดอย่าทำมัน! ฉันพยายามที่จะรับมือกับฐานรหัสขนาดใหญ่ที่มีมาโครอยู่ทั่วทุกที่เพื่อบันทึกการพิมพ์ สิ่งที่ไม่ดีเกี่ยวกับการนิยามใหม่นี้สำหรับฉันคือตัวประมวลผลล่วงหน้าจะแทนที่ทุกที่แม้ว่าจะไม่ได้อยู่ในขอบเขต / ใช้ไม่ได้เช่นฟังก์ชั่นแบบสแตนด์อโลนเพื่อนร่วมงานของคุณอาจมีตัวแปรท้องถิ่นที่เรียกฉัน (s) เขาจะไม่แก้ไขข้อบกพร่องอย่างมีความสุข ... คุณจบลงด้วยการมีมาโครที่คุณไม่สามารถใช้ได้ในทุกขอบเขต


6

NO!

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


ในทางปฏิบัติพวกเขามีแนวโน้มที่จะวางเมาส์เหนือ "ฉัน" กับ IDE และดูคำจำกัดความ
user253751

2
@immibis: ในทางปฏิบัติแม้ว่าพวกเขายังมีแนวโน้มที่จะพูดอะไรบางอย่างเช่น "ผู้เขียนอึนี้?" : P
cHao

@immibis: coders ส่วนใหญ่ที่ฉันทำงานด้วยไม่ใช้เมาส์ - มันช้าเกินไป
JBRWilkinson

อันที่จริงการผลักสิ่งนี้ในส่วนหัวเป็นปัญหาที่แท้จริง หากคุณใช้มันในไฟล์ cpp ของคุณฉันไม่เห็นปัญหาเลย หากเรามีการผลักดันแบบ pragma เป็นมาตรฐานฉันจะแนะนำวิธีการทำเช่นนี้ในส่วนหัว แต่เราไม่ พูดตามฉัน. #defines เป็นสากล
Joshua
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.