سؤال

لنظام أنا في حاجة لتحويل مؤشر طويلة ثم يعود إلى نوع مؤشر.كما يمكنك تخمين هذا غير آمنة للغاية.ما أردت القيام به هو استخدام dynamic_cast للقيام التحويل حتى إذا إختلطت عليهم سوف تحصل على مؤشر فارغة.هذه الصفحة يقول http://publib.boulder.ibm.com/infocenter/lnxpcomp/v7v91/index.jsp?topic=/com.ibm.vacpp7l.doc/language/ref/clrc05keyword_dynamic_cast.htm

على dynamic_cast مشغل ينفذ نوع التحويلات في وقت التشغيل.على dynamic_cast مشغل يضمن تحويل مؤشر إلى قاعدة الفئة مؤشر إلى فئة مشتقة ، أو التحويل من lvalue في اشارة الى قاعدة الطبقة إلى إشارة إلى فئة مشتقة.A برنامج وبذلك يمكن استخدام فئة التسلسل الهرمي بأمان.هذا المشغل ، على typeid المشغل توفير وقت التشغيل نوع المعلومات (RTTI) الدعم في C++.

وأود أن تحصل على خطأ إذا كان فارغة لذلك كتبت بلدي ديناميكية الزهر

template<class T, class T2> T mydynamic_cast(T2 p)
{
    assert(dynamic_cast<T>(p));
    return reinterpret_cast<T>(p);
}

مع MSVC أحصل على خطأ "خطأ C2681:"الطويل":غير صالح نوع التعبير عن dynamic_cast".اتضح أن هذا سوف تعمل فقط مع الطبقات التي لها وظائف افتراضية...WTF!أنا أعرف وجهة ديناميكية يلقي كانت أعلى/أسفل صب الميراث المشكلة ولكن أعتقد أيضا أنه من أجل حل يلقي نوع المشكلة بشكل حيوي.وأنا أعلم أنني يمكن استخدام reinterpret_cast ولكن هذا لا يضمن نفس النوع من الأمان.

ماذا يجب أن تستخدم للتحقق إذا التلبيس هي نفس النوع ؟ يمكنني المقارنة بينهما typeid ولكن سيكون لدي مشكلة عندما أريد أن التلبيس مشتقة إلى قاعدتها.كيف يمكنني حل هذا ؟

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

المحلول

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

والحيلة هي أن كل الفئة التي تمر بها كمقبض يجب أن ترث من الفئة الأساسية المشتركة.

#include <stdexcept>
#include <typeinfo>
#include <string>
#include <iostream>
using namespace std;


// Any class that needs to be passed out as a handle must inherit from this class.
// Use virtual inheritance if needed in multiple inheritance situations.
class Base
{

public:
    virtual ~Base() {} // Ensure a v-table exists for RTTI/dynamic_cast to work
};


class ClassA : public Base
{

};

class ClassB : public Base
{

};

class ClassC
{
public:
    virtual ~ClassC() {}
};

// Convert a pointer to a long handle.  Always use this function
// to pass handles to outside code.  It ensures that T does derive
// from Base, and that things work properly in a multiple inheritance
// situation.
template <typename T>
long pointer_to_handle_cast(T ptr)
{
    return reinterpret_cast<long>(static_cast<Base*>(ptr));
}

// Convert a long handle back to a pointer.  This makes sure at
// compile time that T does derive from Base.  Throws an exception
// if handle is NULL, or a pointer to a non-rtti object, or a pointer
// to a class not convertable to T.
template <typename T>
T safe_handle_cast(long handle)
{
    if (handle == NULL)
        throw invalid_argument(string("Error casting null pointer to ") + (typeid(T).name()));

    Base *base = static_cast<T>(NULL); // Check at compile time that T converts to a Base *
    base = reinterpret_cast<Base *>(handle);
    T result = NULL;

    try
    {
        result = dynamic_cast<T>(base);
    }
    catch(__non_rtti_object &)
    {
        throw invalid_argument(string("Error casting non-rtti object to ") + (typeid(T).name()));
    }

    if (!result)
        throw invalid_argument(string("Error casting pointer to ") + typeid(*base).name() + " to " + (typeid(T).name()));

    return result;
}

int main()
{
    ClassA *a = new ClassA();
    ClassB *b = new ClassB();
    ClassC *c = new ClassC();
    long d = 0; 


    long ahandle = pointer_to_handle_cast(a);
    long bhandle = pointer_to_handle_cast(b);
    // long chandle = pointer_to_handle_cast(c); //Won't compile
    long chandle = reinterpret_cast<long>(c);
    // long dhandle = pointer_to_handle_cast(&d); Won't compile
    long dhandle = reinterpret_cast<long>(&d);

    // send handle to library
    //...
    // get handle back
    try
    {
        a = safe_handle_cast<ClassA *>(ahandle);
        //a = safe_handle_cast<ClassA *>(bhandle); // fails at runtime
        //a = safe_handle_cast<ClassA *>(chandle); // fails at runtime
        //a = safe_handle_cast<ClassA *>(dhandle); // fails at runtime
        //a = safe_handle_cast<ClassA *>(NULL); // fails at runtime
        //c = safe_handle_cast<ClassC *>(chandle); // Won't compile
    }
    catch (invalid_argument &ex)
    {
        cout << ex.what() << endl;
    }

    return 0;
}

نصائح أخرى

dynamic_cast يمكن أن تستخدم فقط بين الطبقات ذات الصلة من خلال الميراث.لتحويل مؤشر طويلة أو العكس بالعكس ، يمكنك استخدام reinterpret_cast.للتحقق ما إذا كان مؤشر null, يمكنك assert(ptr != 0).ومع ذلك ، فإنه عادة ما لا المستحسن استخدام reinterpret_cast.لماذا تحتاج إلى تحويل مؤشر إلى فترة طويلة ؟

وثمة خيار آخر هو استخدام الاتحاد:

union  U { 
int* i_ptr_;
long l;
}

مرة أخرى, الاتحاد أيضا ضروري إلا نادرا.

وتذكر أنه في Windows 64، مؤشر ستكون كمية 64 بت ولكن سوف long لا تزال كمية 32 بت و هو كسر الشفرة. على أقل تقدير، تحتاج إلى جعل اختيار نوع عدد صحيح تعتمد على منصة. أنا لا أعرف ما إذا MSVC لديه الدعم لuintptr_t، ونوع المنصوص عليها في C99 لعقد مؤشرات. التي من شأنها أن تكون أفضل نوع لاستخدام إذا كان متوفرا.

وأما بالنسبة لبقية، والبعض الآخر قد عالجت السبب هو ولماذا لdynamic_cast مقابل reinterpret_cast بما فيه الكفاية.

وreinterpret_cast هو يلقي الصحيح لاستخدامه هنا.

وهذا هو الى حد كبير <م> فقط شيء يمكن أن تفعله بأمان.

وreinterpret_cast من نوع مؤشر إلى نوع T والعودة إلى نوع مؤشر الأصلي غلة المؤشر الأصلي. (على افتراض T هو مؤشر أو عدد صحيح النوع الذي هو على الأقل كبير مثل نوع مؤشر الأصلي)

ملحوظة أن reinterpret_cast من نوع مؤشر إلى T هي غير محددة. لا توجد ضمانات حول قيمة من نوع T، <م> إلا أنه إذا كنت ثم reinterpret_cast مرة أخرى إلى النوع الأصلي، يمكنك الحصول على القيمة الأصلية. حتى على افتراض انك لا تحاول أن تفعل أي شيء مع قيمة طويلة وسيطة في قضيتك، reinterpret_cast غير آمنة تماما والمحمولة.

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

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

مثل الآخرين القول بالفعل ، فإنه لم يتم تعريف السلوك إلى استخدام dynamic_cast على غير متعدد الأشكال فئة (باستثناء عندما كنت تفعل upcast ضمنا على أي حال ، يمكن تجاهلها هنا) ، كما يعمل فقط على مؤشرات أو المراجع.لا لا يتجزأ من أنواع.

يمكنك استخدام أفضل ::intptr_t وجدت في مختلف posix النظم.يمكنك استخدام هذا النوع مثل النوع المتوسط يمكنك المدلى بها.

بشأن التحقق من ما إذا كان التحويل سوف تنجح ، يمكنك استخدام sizeof:

BOOST_STATIC_ASSERT(sizeof(T1) >= sizeof(T2));

سوف تفشل في تجميع الوقت إذا كان التحويل لا يمكن القيام به.أو الاستمرار في استخدام تأكيد مع هذا الشرط ، وسوف يؤكدون في وقت التشغيل بدلا من ذلك.

تحذير: هذا لن يمنعك من الصب T* إلى intptr_t العودة إلى U* مع U نوع آخر من ت.وهكذا, هذا فقط يضمن لك الزهر لن تتغير قيمة المؤشر إذا كنت من الزهر T* إلى intptr_t والعودة إلى T*.(بفضل نيكولا مشيرا إلى قد تتوقع حماية آخر).

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

struct PtrWrapper {
  void* m_theRealPointer;
  std::string m_type;
};

void YourDangerousMethod( long argument ) {

   if ( !argument ) 
     return;

   PtrWrapper& pw = *(PtrWrapper*)argument;

   assert( !pw.m_type.empty() );

   if ( pw.m_type == "ClassA" ) {
     ClassA* a = (ClassA*)pw.m_theRealPointer;
     a->DoSomething();
   } else if (...) { ... }

}

dynamic_cast<> هو يلقي يعتزم استخدامها فقط على للتحويل أنواع (في الأشكال المعنى).مما اضطر طاقم pointer إلى long (litb بشكل صحيح يشير إلى static_assert لضمان توافق حجم) كل المعلومات عن نوع المؤشر يتم فقدان.ليس هناك طريقة لتنفيذ safe_reinterpret_cast<> للحصول على المؤشر مرة أخرى:كل قيمة و نوع.

لتوضيح ما أعنيه:

struct a_kind {}; 
struct b_kind {}; 

void function(long ptr) 
{} 

int 
main(int argc, char *argv[]) 
{ 
    a_kind * ptr1 = new a_kind; 
    b_kind * ptr2 = new b_kind;

    function( (long)ptr1 );
    function( (long)ptr2 );

    return 0;
}

لا يوجد طريقة function() لتحديد نوع المؤشر مرت و "أسفل" يلقي ذلك إلى نوع المناسبة ، إلا إذا إما:

  • طويلة ملفوفة من قبل كائن مع بعض المعلومات من هذا النوع.
  • نوع نفسها المشفرة في الكائن المشار إليه.

كل الحلول هي قبيحة ينبغي تجنبها ، حيث يتم RTTI بدلاء.

وأيضا، أفضل استخدام size_t بدلا من فترة طويلة - وأعتقد أن ضمنت هذا النوع لتكون متوافقة مع حجم مساحة العنوان

وبمجرد أن قرر أن يلقي مؤشر إلى فترة طويلة، كنت ألقى سلامة نوع من الرياح.

ويستخدم dynamic_cast للادلاء ارتفاعا وانخفاضا شجرة الاشتقاق. وهذا هو، من مؤشر الفئة الأساسية إلى مؤشر فئة مشتقة. إذا كان لديك:

class Base
{
};

class Foo : public  Base
{
};

class Bar : public Base
{
};

ويمكنك استخدام dynamic_cast بهذه الطريقة ...

Base* obj = new Bar;

Bar* bar = dynamic_cast<Bar*>(obj); // this returns a pointer to the derived type because obj actually is a 'Bar' object
assert( bar != 0 );

Foo* foo = dynamic_cast<Foo*>(obj);  // this returns NULL because obj isn't a Foo
assert( foo == 0 );

و... ولكن لا يمكنك استخدام يلقي الديناميكي للادلاء في لمن شجرة الاشتقاق. تحتاج reinterpret_cast أو الطراز C يلقي لذلك.

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