تحتاج إلى توضيحات في CATS على غرار C ، وإعادة تفسير ، و const

StackOverflow https://stackoverflow.com/questions/2346616

سؤال

هل أنا على حق في افتراض أن الممثلين على الطراز C (التي يتم تثبيتها) ليست سوى إعادة الانقسام؟ إن استخدام هذا الأخير لافت للنظر بصريًا وسهل البحث عند البحث عن قوالب سيئة ، وبالتالي يوصى به على القبائل على غرار C؟

إذا تم إلقاء const باستخدام const_cast والكتابة إلى كائن const في الأصل غير محدد ، ما هو الغرض من const_cast؟

ملحوظة: أعلم أن Bjarne يدين بحق العمليات بأنها غير آمنة وحتى يذهب إلى حد الإبلاغ " البشع يجب أن يكون للعملية نموذجًا نحليًا قبيحًا. "ومن هنا جاءت صرف مشغلي الصب في C ++. لذلك سأحاول تقليل استخدامهم. الوعد. :)

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

المحلول

لا. const_cast, ، أ static_cast, ، أ reinterpret_cast, ، أو مزيج منها. في حال لم يكن ذلك كافيًا ، يمكن أن يفعل أيضًا خدعة بسيطة واحدة على الأقل لا مزيج من الأحدث يمكن أن تفعل على الإطلاق!

يمكنك استخدام const_cast مع النتائج المحددة إذا تم تعريف المتغير الأصلي بدون const, ، ولكن كل ما لديك هو const مؤشر أو إشارة إلى هذا الكائن. otoh ، إذا كنت تعتقد أن لديك سبب وجيه لاستخدام أ const_cast, ، هناك احتمالات أنه يجب عليك البحث حقًا mutable في حين أن.

تحرير: أفترض أنني يجب أن أقول ذلك فورًا ، ولكن يمكن أن يتحول فريق C-tyle على طراز C فئة قاعدة لا يمكن الوصول إليها. على سبيل المثال ، فكر في شيء مثل:

تحرير: أقوم بتحديث الكود إلى شيء سيتم تجميعه و (عادة) يوضح المشكلة.

#include <iostream>

class base1 {
public:
    virtual void print() { std::cout << "base 1\n"; }
};

class base2 {
public:
   virtual void print() { std::cout << "base 2\n"; }
};

class derived : base1, base2 {}; // note: private inheritance

int main() {    
    derived *d = new derived;
    base1 *b1 = (base1 *)d;    // allowed
    b1->print();    // prints "base 1"
    base2 *b2 = (base2 *)d;    // also allowed
    b2->print();    // prints "base 2"

//    base1 *bb1 = static_cast<base *>(d);  // not allowed: base is inaccessible

    // Using `reinterpret_cast` allows the code to compile.
    // Unfortunately the result is different, and normally won't work. 
    base1 *bb2 = reinterpret_cast<base1 *>(d);
    bb2->print();   // may cause nasal demons.

    base2 *bb3 = reinterpret_cast<base2 *>(d); 
    bb3->print();   // likewise
    return 0;
}

الرمز باستخدام reinterpret_castسوف تجمع S - ولكن محاولة استخدام النتيجة (من خشية واحدة من الاثنين) سوف تسبب مشكلة كبيرة. ال reinterpret_cast يأخذ يتمركز عنوان الكائن المشتق ومحاولات معاملته كما لو كان النوع المحدد من الكائن الأساسي - وبما أن (على الأكثر) يمكن أن يوجد كائن أساسي واحد في هذا العنوان ، في محاولة لمعاملته كآخرين يمكن أن يسبب/سيؤدي إلى تخصص مشاكل. تحرير: في هذه الحالة ، تكون الفصول متطابقة بشكل أساسي باستثناء ما تطبعها ، لذلك على الرغم من أي شيء يستطع يحدث ، مع معظم المجمعين ، سيقوم كل من الأخيرين بطباعة "قاعدة 1". يأخذ REINTERPRET_CAST كل ما يحدث في هذا العنوان ويحاول استخدامه كنوع محدد. في هذه الحالة ، لقد حاولت (حاول) أن أفعل شيئًا غير ضار ولكن مرئي. في الكود الحقيقي ، ربما لن تكون النتيجة جميلة جدًا.

سيعمل طاقم الممثلين على طراز C مثل static_cast إذا كان الكود قد استخدم الميراث العام بدلاً من القطاع الخاص-أي أنه يدرك مكان كل كائن من فئة الأساس المشتقة ، وضبط النتيجة ، لذلك سيقوم كل مؤشر ناتج العمل لأنه تم ضبطه لتشير في المكان المناسب.

نصائح أخرى

لا ، يمكن أن تعمل الممثلين على غرار C كما reinterpret_castس، const-castS أو static_castق اعتمادا على الموقف. هذا هو السبب في إحباطهم - ترى ممثلين على غرار C في التعليمات البرمجية وتحتاج إلى البحث عن تفاصيل لمعرفة ما ستفعله. علي سبيل المثال:

const char* source;
int* target = (int*)source;// - acts as const_cast and reinterpret_cast at once
//int* target = retinterpret_cast<int*>source;// - won't compile - can't remove const

تذكر أن فريق Const قد يتصرف على شيء آخر ثم المعرف الأصلي:

void doit(const std::string &cs)
{
    std::string &ms = const_cast<std::string &>(cs);
}

int main()
{
    std::string s;
    doit(s);
}

لذا ، بينما تقوم Doit بإلقاء Const ، في هذا المثال ، فإن السلسلة الأساسية ليست const ، لذا لا يوجد سلوك غير محدد.

تحديث

حسنًا ، إليك مثال أفضل عند استخدام Const_cast لا قيمة له تمامًا. نبدأ بفئة قاعدة مع وظيفة افتراضية تأخذ معلمة const:

class base
{
public:
    virtual void doit(const std::string &str);
};

والآن تريد تجاوز هذه الوظيفة الافتراضية.

class child : public base
{
public:
    virtual void doit(const std::string &str)
    {
        std::string &mstr = const_cast<std::string &>(str);
    }
};

بسبب منطق/بنية الكود الخاص بك ، أنت تعرف ذلك child::doit سيتم استدعاؤه فقط مع السلاسل غير الممتدة (و class base ليس تحت سيطرتك ، لذا لا يمكنك تعديله ولا يمكنك تغيير توقيعه child::doit لأنه بعد ذلك لن يتجاوز base::doit). في هذه الحالة ، من الآمن أن نلقي بعيدا.

نعم ، هذا محفوف بالمخاطر. ربما عندما تكتب ذلك ، صحيح أن التنفيذ لن يصل أبدًا child::doit مع سلسلة غير مخصصة والرمز صالح. ولكن قد يتغير ذلك إما أثناء الحفاظ على برنامجك أو ربما عند إعادة البناء والتقاط أحدث إصدار من class base.

const_cast يستخدم لإزالة const من نوع. يمكنه أيضًا إزالته volatile. إذا كان الكائن حقًا const ثم لا يمكن كتابة النتيجة ولا تزال سلوكًا محددًا جيدًا. ومع ذلك ، إذا تم الترويج لها const (من خلال تمريره إلى أ const T وظيفة ، ثم const_castجي العودة إلى غيرconst على ما يرام. (لقد وجدت المزيد من المعلومات هنا)

reinterpret_cast لا تستطيع إزالة const أو volatile من نوع.

أنظر أيضا

إن الممثلين على الطراز C هي في الحقيقة مطرقة للبرمجة - أنت تخبر المترجم بشكل أساسي أن ربط المربعة هناك سوف يناسب هذه الثقب المستدير بغض النظر عن ما. في هذا المعنى، reinterpret_cast متشابه كثيرا.

الميزة الرئيسية التي أراها في استخدام مشغلي الممثلين C ++-النمط هي أنها تسمح لك بالتعبير عن نيتك بشكل أفضل والسماح للمترجم بالاستمرار -كل النمط C يلقي.

متعلق const_cast- غالبًا ما تدخل في الموقف الذي تقوم فيه بتمرير كائن من خلال مرجع const ببساطة لأن واجهة برمجة التطبيقات تتطلب منك القيام بذلك. قل ، لقد حصلت على وظيفة x التي تعود إلى سلسلة على غرار C:

void X(const char *str) { ... }

داخل هذه الوظيفة ، تقوم بتمرير المعلمة إلى دالة C التي تتوقع char *, ، على الرغم من أنها لا تغير السلسلة. الطريقة الوحيدة لاستيعاب هذا ستكون const_cast شارع.

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

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