سؤال

تحرير:هذا السؤال هو أكثر حول اللغة الهندسة من C++ نفسها.كنت تستخدم C++ كمثال لإظهار ما أردت ، في الغالب لأنني استخدامه يوميا.لم أكن أريد أن أعرف كيف يعمل على C++ ولكن فتح النقاش على كيفية يمكن أن ينبغي القيام به.

هذه ليست الطريقة التي يعمل بها الآن, هذه هي الطريقة. أتمنى فإنه يمكن القيام به ، و التي من شأنها كسر ج compability بالتأكيد ، لكن هذا ما أعتقد extern "C" هو كل شيء.

أعني في كل وظيفة أو الطريقة التي تعلن الآن لديك صريحة كتابة هذا الكائن سيتم إرسال إشارة التقديم الإشارة المشغل على ذلك.أتمنى أن كل من غير جراب نوع سيتم إرسالها تلقائيا من قبل المرجعية ، لأنني استخدام الكثير, في الواقع كل الكائن الذي هو أكثر من 32 بت في الحجم و هذا تقريبا كل فئة من الألغام.

دعونا تجسد كيف هو الآن نفترض a, ب و ج أن تكون فصول:

class example {
    public:
        int just_use_a(const a &object);
        int use_and_mess_with_b(b &object);
        void do_nothing_on_c(c object);
};

الآن ما أتمنى:

class example {
    public:
        int just_use_a(const a object);
        int use_and_mess_with_b(b object);
        extern "C" void do_nothing_on_c(c object);
};

الآن do_nothing_on_c() يمكن أن تتصرف مثلما هو اليوم.

التي من شأنها أن تكون مثيرة للاهتمام على الأقل بالنسبة لي ، يشعر أكثر من ذلك بكثير واضحة, و أيضا إذا كنت أعلم كل غير جراب المعلمة قادم بالرجوع أعتقد أن الأخطاء ستكون نفس هذا إذا كان صريحة تعلن ذلك.

وجهة نظر أخرى عن هذا التغيير من شخص قادم من ج ، مرجع مشغل يبدو لي طريقة الحصول على متغير عنوان, الطريقة التي استخدمت للحصول على مؤشرات.أعني, هذا هو نفس المشغل ولكن مع مختلف الدلالي في سياقات مختلفة ، أليس هذا شعور خاطئ قليلا بالنسبة لك أيضا ؟

هل كانت مفيدة؟

المحلول

أعتقد أنك في عداد المفقودين نقطة من C++ و C++ دلالات.هل غاب عن الواقع C++ هو الصحيح في تمرير (تقريبا) كل شيء من حيث القيمة, لأنها الطريقة التي فعلت ذلك في C.دائما.ولكن ليس فقط في C ، كما سوف تظهر لك أدناه...

المعلمات دلالات على C

في C ، كل شيء يتم تمريرها حسب القيمة."الأوليات" و "القرون" يتم تمريرها عن طريق نسخ قيمتها.تعديل في وظيفة الخاص بك ، الأصلي لا يمكن تعديلها.لا تزال تكلفة نسخ بعض القرون يمكن أن يكون غير تافهة.

عند استخدام مؤشر التدوين ، ( * ) أنت لا يمر عن طريق الإشارة.أنت تمرير نسخة من العنوان.والتي هي أكثر أو أقل نفس واحد ولكن مع فرق دقيق:

typedef struct { int value ; } P ;

/* p is a pointer to P */
void doSomethingElse(P * p)
{
   p->value = 32 ;
   p = malloc(sizeof(P)) ; /* Don't bother with the leak */
   p->value = 45 ;
}

void doSomething()
{
   P * p = malloc(sizeof(P)) ;
   p->value = 25 ;

   doSomethingElse(p) ;

     int i = p->value ;
   /* Value of p ? 25 ? 32 ? 42 ? */
}

القيمة النهائية p->القيمة 32.لأنه p أقره نسخ القيمة من العنوان.لذا p الأصلية لم يعدل (واحدة جديدة تم تسريبها).

المعلمات دلالات على جافا و سي شارب

يمكن أن تكون مفاجئة بالنسبة للبعض ، ولكن في جاوة ، كل شيء هو نسخ القيمة أيضا.C المثال أعلاه من شأنه أن يعطي نفس النتائج في جافا.هذا هو تقريبا ما تريد ولكنك لن تكون قادرة على تمرير البدائية "بالرجوع/مؤشر" بسهولة كما في C.

في C# ، وأضاف أنها "المرجع" الكلمة.يعمل أكثر أو أقل مثل الإشارة في C++.المهم هو في C#, عليك أن تذكر ذلك على حد سواء في وظيفة الإعلان, و على كل مكالمة.أعتقد أن هذا ليس ما تريد مرة أخرى.

المعلمات دلالات على C++

في C++, كل شيء تقريبا يتم تمريرها عن طريق نسخ القيمة.عندما كنت تستخدم سوى نوع من الرمز ، أنت نسخ الرمز (مثل يتم ذلك في ج).هذا هو السبب عندما كنت تستخدم * أنت تمرير نسخة من عنوان الرمز.

ولكن عندما كنت تستخدم &, ثم نفترض أنك تمر الحقيقي الكائن (سواء كان ذلك البنية, int, مؤشر, أيا كان):الإشارة.

فمن السهل أن خطأ كما syntaxic السكر (أي من وراء الكواليس يعمل مثل مؤشر, و الشفرة التي تم إنشاؤها هي نفسها المستخدمة في مؤشر).ولكن...

والحقيقة أن الإشارة على أكثر من syntaxic السكر.

  • على عكس المؤشرات ، فإنه يخول التلاعب الكائن كما لو كان على المكدس.
  • Unline المؤشرات ، عندما associatied مع const ، فإنه يخول الضمني تشجيع من نوع واحد إلى آخر (من خلال الصانعين ، أساسا).
  • على عكس المؤشرات ، الرمز ليس من المفترض أن تكون فارغة أو غير صالح.
  • على عكس "من نسخة" أنت لا تنفق الوقت بلا جدوى نسخ الكائن
  • على عكس "من نسخة" ، يمكنك استخدامه بمثابة [الخروج] المعلمة
  • على عكس "من نسخة" يمكنك استخدام مجموعة كاملة من OOP في C++ (أييمكنك تمرير كائن كامل إلى وظيفة الانتظار واجهة).

لذا المراجع لديه أفضل من كلا العالمين.

دعونا نرى C سبيل المثال ، ولكن مع C++ الاختلاف على doSomethingElse وظيفة:

struct P { int value ; } ;

// p is a reference to a pointer to P
void doSomethingElse(P * & p)
{
   p->value = 32 ;
   p = (P *) malloc(sizeof(P)) ; // Don't bother with the leak
   p->value = 45 ;
}

void doSomething()
{
   P * p = (P *) malloc(sizeof(P)) ;
   p->value = 25 ;

   doSomethingElse(p) ;

     int i = p->value ;
   // Value of p ? 25 ? 32 ? 42 ?
}

والنتيجة هي 42 القديمة p تسربت ، وحلت محلها الجديد p.لأنه على عكس كود C, نحن لا يمر نسخة من المؤشر ، ولكن الإشارة إلى مؤشر هو مؤشر نفسها.

عند العمل مع C++ المثال أعلاه يجب أن تكون كريستال واضحة.إذا لم يكن ثم كنت في عداد المفقودين شيء.

الختام

C++ هي تمر عن طريق نسخ/القيمة لأن هذه هي الطريقة التي يعمل كل شيء في C ، C# أو جافا (حتى في جافا سكريبت...:-p ...).و مثل C#, C++ لديه إشارة المشغل/الكلمة ، على سبيل المكافأة.

الآن, بقدر ما فهمت ، أنت ربما تفعل ما أسميه نصف jockingly C+, هذا هو ج مع بعض المحدودة C++ الميزات.

ربما الحل هو باستخدام typedefs (سوف غضب C++ الزملاء ، على الرغم من أن ترى رمز الملوثة عديمة الفائدة typedefs...) ، ولكن القيام بذلك سوف فقط التعتيم على الحقيقة كنت في عداد المفقودين حقا C++ هناك.

كما قال في آخر ، يجب تغيير العقلية الخاصة بك من ج التنمية (أيا كان) إلى C++ التنمية ، أو ربما نقل إلى لغة أخرى.ولكن لا تبقي برمجة C مع C++ الميزات ، لأن تجاهل عن وعي/تشويش قوة التعابير التي تستخدمها ، سوف تنتج الأمثل رمز.

ملاحظة:و لا يمر بها نسخ أي شيء آخر من الأوليات.عليك أن يخصوا وظيفة الخاص بك من OO القدرات في C++, هذا ليس ما تريد.

تحرير

كان السؤال إلى حد ما تعديل (انظر https://stackoverflow.com/revisions/146271/list ).اسمحوا لي الأصلية الإجابة الإجابة على الأسئلة الجديدة أدناه.

ما رأيك الافتراضي تمر-بالرجوع دلالات على C++? كما قلت أنه كسر التوافق ، وعليك مختلفة تمر على الأوليات (أيالمدمج في أنواع, والتي يمكن أن تكون مرت نسخ) و البنيات/الكائنات (التي من شأنها أن تمرير المراجع).سيكون لديك لإضافة مشغل آخر يعني "تمر القيمة" (extern "C" فظيعة جدا و بالفعل يستخدم لشيء آخر مختلف تماما).لا, أنا حقا أحب الطريقة التي يتم القيام به اليوم في C++.

[...] إشارة المشغل يبدو لي طريقة الحصول على عنوان متغير ، هذا ما اعتدت على الحصول على مؤشرات.أعني, هذا هو نفس المشغل ولكن مع مختلف الدلالي في سياقات مختلفة ، أليس هذا شعور خاطئ قليلا بالنسبة لك أيضا ؟ نعم و لا.المشغل >> غيرت الدلالي عند استخدامها مع C++ تيارات أيضا.ثم يمكنك استخدام مشغل += لاستبدال وstrcat.أعتقد المشغل & اعتدنا لأن المغزى باسم "عكس مؤشر", و لأنها لم تكن ترغب في استخدام آخر رمز (ASCII محدود نطاق المشغل ::وكذلك مؤشر -> يدل على أن بعض رموز أخرى يمكن استخدامها).ولكن الآن إذا كان يزعجك && حقا سوف يزعج لك ، كما أنها وأضاف الأحادية && في C++0x (نوع من السوبر مرجع...).لم هضم نفسي...

نصائح أخرى

مترجم خيار تماما التغيرات معنى القسم من القانون يبدو وكأنه فكرة سيئة حقا بالنسبة لي.إما الحصول على استخدام C++ جملة أو إيجاد لغة مختلفة.

أنا أفضل عدم إساءة استخدام المراجع أي أكثر من خلال جعل كل (غير مؤهل) المعلمة إشارة.

السبب الرئيسي المراجع أضيفت إلى C++ لدعم المشغل الحمولة الزائدة;إذا كنت تريد "تمر-بالرجوع" دلالات ، ج كان معقولا تماما طريقة للقيام بذلك:المؤشرات.

باستخدام المؤشرات توضح القصد من تغيير قيمة إلى كائن, ومن الممكن أن نرى هذا من خلال النظر فقط في استدعاء دالة, لا يجب أن ننظر إلى إعلان وظيفة لمعرفة ما إذا كان استخدام الإشارة.

كما ترى

أنا لا أريد أن تغيير الحجة ، يجب استخدام مؤشر أو يجب استخدام مرجعية ؟ أنا لا أعرف قوي سبب منطقي.إذا عابرة `لا كائن" (مثلا ، مؤشر فارغة) ، مقبولة ، باستخدام مؤشر يجعل الشعور.أسلوبي هو استخدام المؤشر عندما أريد تعديل كائن لأنه في بعض السياقات التي يجعل من الأسهل على الفور أن التعديل هو ممكن.

من نفس التعليمات.

نعم أنا من رأي أن هذا مربكا جدا الزائد.

هذا هو ما مايكروسوفت عن الحالة:

لا تخلط بين المرجعية الإعلانات مع استخدام عنوان المشغل.عندما & معرف يسبقه نوع ، مثل الباحث أو شار ثم معرف أعلن إشارة إلى نوع.عندما & معرف لم يسبقها نوع الاستخدام هو عنوان المشغل.

أنا لست كبيرة على C أو C++, ولكن يمكنني الحصول على أكبر الصداع فرز مختلف الاستخدامات * و على كل اللغات مما أفعل الترميز في المجمع.

أفضل نصيحة هي أن تجعل عادة من التفكير في ما كنت تريد حقا أن يحدث.مرورا إشارة لطيفة عندما لا يكون لديك نسخة المنشئ (أو لا تريد استخدام ذلك) أرخص الأجسام الكبيرة.ومع ذلك ، ثم الطفرات إلى المعلمة ورأى خارج الصف.يمكنك بدلا من ذلك يمكن أن يمر بها const مرجع-ثم لا توجد الطفرات ولكن لا يمكنك إجراء تعديلات المحلية.تمر const قبل-قيمة رخيصة من الأشياء التي يجب أن تكون للقراءة فقط في الدالة وتمرير غير const من قيمة عندما تريد نسخة التي يمكن أن تجعل المحلية التعديلات.

كل التقليب (حسب القيمة/قبل المرجعية const/غير const) وقد الاختلافات الهامة التي هي بالتأكيد ليست ما يعادلها.

عند تمرير من حيث القيمة ، يتم نسخ البيانات إلى المكدس.في حال كان لديك المشغل= محددة البنية أو فئة أن يتم تمرير ذلك ، فإنه يحصل على تنفيذها.لا يوجد مترجم التوجيه وأنا على علم أن يغسل هراء الضمني اللغة الارتباك أن التغيير المقترح من شأنه بطبيعتها السبب.

مشترك أفضل الممارسات لتمرير القيم const المرجعية ، ليس فقط من قبل المرجعية.هذا يضمن أن قيمة لا يمكن تغيير في وظيفة الدعوة.هذا هو عنصر واحد من const-الصحيح تعليمات البرمجة الأساسية.

تماما const-الصحيح codebase يذهب إلى أبعد من ذلك ، مضيفا const إلى نهاية النماذج.النظر:

void Foo::PrintStats( void ) const {
   /* Cannot modify Foo member variables */
}

void Foo::ChangeStats( void ) {
   /* Can modify foo member variables */
}

إذا كنت تريد تمرير فو الكائن في وظيفة مسبوقة مع const, كنت قادرا على استدعاء PrintStats().المترجم أن الخطأ على استدعاء ChangeStats().

void ManipulateFoo( const Foo &foo )
{
    foo.PrintStats();  // Works
    foo.ChangeStats(); // Oops; compile error
}

أنا بصراحة أعتقد أن هذا كله يمر قيمة/يمر بالرجوع فكرة في C++ هي مضللة. كل شيء يتم تمرير قيمة.لديك ثلاث حالات:

  1. حيث يمكنك تمرير نسخة محلية من متغير

    void myFunct(int cantChangeMyValue)
    
  2. حيث يمكنك تمرير نسخة من مؤشر إلى متغير

    void myFunct(int* cantChangeMyAddress) {
        *cantChangeMyAddress = 10;
    }
    
  3. حيث يمكنك تمرير 'المرجعية', ولكن من خلال مترجم ماجيك انها مجرد كما لو كنت تمرير مؤشر ببساطة dereferenced في كل مرة.

    void myFunct(int & hereBeMagic) {
        hereBeMagic = 10; // same as 2, without the dereference
    }
    

أنا شخصيا أجد أنه أقل مربكة أن تذكر أن كل شيء يمر القيمة.في بعض الحالات التي قد تكون القيمة العنوان الذي يسمح لك لتغيير الأمور خارج الدالة.

ما يوحي لن تسمح مبرمج للقيام رقم 1.أنا شخصيا أعتقد أن ذلك سيكون فكرة سيئة أن يسلب هذا الخيار.واحد زائد الرئيسية من C/C++ هو وجود لها غرامة الحبيبات إدارة الذاكرة.صنع كل شيء يمر من خلال الإشارة هو مجرد محاولة لجعل C++ مثل جافا.

هناك شيء غير واضح.عندما تقول:

الباحث ب(ب ¶م) ؛

ماذا كنت تنوي الثانية 'ب'?هل نسيت أن أقدم نوع ؟ هل نسيت أن تكتب بشكل مختلف فيما يتعلق الأول 'ب'?ألا تعتقد أنه من الواضح أن أكتب شيئا مثل:

class B{/*something...*/};
int b(B& param);

منذ الآن, أعتقد أنك تعني ما أكتب.

إن السؤال هو "لا أعتقد أنه سيكون من الأفضل أن المترجم النظر في كل تمريرة-من قبل-القيمة من غير جراب تمر-الحكم؟".المشكلة الأولى هي أنه سوف كسر العقد الخاص بك.أفترض أنك تعني تمر-CONST-مرجع ، وليس فقط من خلال الإشارة.

السؤال الآن هو خفض هذا واحد:"هل تعرف إذا كان هناك بعض المجمعين التوجيه التي يمكن تحسين وظيفة دعوة قيمة؟"

الجواب الآن هو "لا أدري".

أعتقد أن c++ تصبح فوضوي جدا إذا كنت تبدأ في خلط كل نوع من المعلمات المتاحة مع const الاختلافات.

فإنه يحصل بسرعة من جهة أن تراك كل نسخ منشئات يدعو كل dereferences طاقتها وهلم جرا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top