سؤال

في Java، يمكنك الحصول على قائمة بالكائنات.يمكنك إضافة كائنات من أنواع متعددة، ثم استردادها والتحقق من نوعها وتنفيذ الإجراء المناسب لهذا النوع.
على سبيل المثال:(أعتذر إذا كان الرمز غير صحيح تمامًا، سأذهب من الذاكرة)

List<Object> list = new LinkedList<Object>();

list.add("Hello World!");
list.add(7);
list.add(true);

for (object o : list)
{
    if (o instanceof int)
        ; // Do stuff if it's an int
    else if (o instanceof String)
        ; // Do stuff if it's a string
    else if (o instanceof boolean)
        ; // Do stuff if it's a boolean
}

ما هي أفضل طريقة لتكرار هذا السلوك في C++؟

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

المحلول

والمثال باستخدام Boost.Variant والزوار:

#include <string>
#include <list>
#include <boost/variant.hpp>
#include <boost/foreach.hpp>

using namespace std;
using namespace boost;

typedef variant<string, int, bool> object;

struct vis : public static_visitor<>
{
    void operator() (string s) const { /* do string stuff */ }
    void operator() (int i) const { /* do int stuff */ }
    void operator() (bool b) const { /* do bool stuff */ }      
};

int main() 
{
    list<object> List;

    List.push_back("Hello World!");
    List.push_back(7);
    List.push_back(true);

    BOOST_FOREACH (object& o, List) {
        apply_visitor(vis(), o);
    }

    return 0;
}

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

نصائح أخرى

boost::variant مشابه لاقتراح ديركينلي لـ boost::any, ، ولكنه يدعم نمط الزائر، مما يعني أنه من الأسهل إضافة كود خاص بالنوع لاحقًا.كما أنه يخصص القيم على المكدس بدلاً من استخدام التخصيص الديناميكي، مما يؤدي إلى تعليمات برمجية أكثر كفاءة قليلاً.

يحرر: كما يشير litb في التعليقات، باستخدام variant بدلاً من any يعني أنه لا يمكنك الاحتفاظ إلا بقيم من أحد قائمة الأنواع المحددة مسبقًا.غالبًا ما تكون هذه نقطة قوة، على الرغم من أنها قد تكون نقطة ضعف في حالة السائل.

فيما يلي مثال (على الرغم من عدم استخدام نمط الزائر):

#include <vector>
#include <string>
#include <boost/variant.hpp>

using namespace std;
using namespace boost;

...

vector<variant<int, string, bool> > v;

for (int i = 0; i < v.size(); ++i) {
    if (int* pi = get<int>(v[i])) {
        // Do stuff with *pi
    } else if (string* si = get<string>(v[i])) {
        // Do stuff with *si
    } else if (bool* bi = get<bool>(v[i])) {
        // Do stuff with *bi
    }
}

(ونعم، يجب عليك استخدام vector<T>::size_type بدلاً من int ل iنوع ، ويجب عليك استخدامه من الناحية الفنية vector<T>::iterator بدلاً من ذلك على أي حال، لكنني أحاول أن أبقي الأمر بسيطًا.)

وC ++ لا يدعم حاويات غير متجانسة.

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

class Dummy {
   virtual void whoami() = 0;
};

class Lizard : public Dummy {
   virtual void whoami() { std::cout << "I'm a lizard!\n"; }
};


class Transporter : public Dummy {
   virtual void whoami() { std::cout << "I'm Jason Statham!\n"; }
};

int main() {
   std::list<Dummy*> hateList;
   hateList.insert(new Transporter());
   hateList.insert(new Lizard());

   std::for_each(hateList.begin(), hateList.end(), 
                 std::mem_fun(&Dummy::whoami));
   // yes, I'm leaking memory, but that's besides the point
}

إذا كنت تنوي استخدام boost يمكنك محاولة boost::any . هنا مثال باستخدام boost::any.

ويمكنك العثور على هذا المقالة من قبل اثنين الرائدة ++ C خبراء من الفائدة.

والآن، boost::variant هو آخر شيء أن نبحث عنها كما j_random_hacker المذكورة. لذلك، وهنا لتحصل على فكرة عادلة ما لاستخدام.

ومع boost::variant رمز أعلاه أن ننظر بشيء من هذا القبيل:

class Lizard {
   void whoami() { std::cout << "I'm a lizard!\n"; }
};

class Transporter {
   void whoami() { std::cout << "I'm Jason Statham!\n"; }
};

int main() {

   std::vector< boost::variant<Lizard, Transporter> > hateList;

   hateList.push_back(Lizard());
   hateList.push_back(Transporter());

   std::for_each(hateList.begin(), hateList.end(), std::mem_fun(&Dummy::whoami));
}

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

وC ++ لديه تركيز أكبر على السلامة نوع من جافا، وهذا هو بالذات نوع غير آمنة.

وقال ذلك، إذا كانت الأشياء لديهم شيء مشترك، لماذا أنت تخزينها معا؟

إذا لديهم أشياء مشتركة، يمكنك جعل فئة لها أن ترث من. بالتناوب، استخدم دفعة :: أي. إذا كانت ترث، لها وظائف افتراضية للاتصال، أو استخدام dynamic_cast <> إذا كان لديك حقا.

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

class MyData
{
public:
  // base classes of polymorphic types should have a virtual destructor
  virtual ~MyData() {} 

  // hand off to protected implementation in derived classes
  void DoSomething() { this->OnDoSomething(); } 

protected:
  // abstract, force implementation in derived classes
  virtual void OnDoSomething() = 0;
};

class MyIntData : public MyData
{
protected:
  // do something to int data
  virtual void OnDoSomething() { ... } 
private:
  int data;
};

class MyComplexData : public MyData
{
protected:
  // do something to Complex data
  virtual void OnDoSomething() { ... }
private:
  Complex data;
};

void main()
{
  // alloc data objects
  MyData* myData[ 2 ] =
  {
    new MyIntData()
  , new MyComplexData()
  };

  // process data objects
  for ( int i = 0; i < 2; ++i ) // for each data object
  {
     myData[ i ]->DoSomething(); // no type cast needed
  }

  // delete data objects
  delete myData[0];
  delete myData[1];
};

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

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

#include <iostream>
#include <vector>

using namespace std;

int main() {

  int a = 4;
  string str = "hello";

  vector<void*> list;
  list.push_back( (void*) &a );
  list.push_back( (void*) &str );

  cout <<  * (int*) list[0] << "\t" << * (string*) list[1] << endl;

  return 0;
}

وبينما لا يمكنك تخزين أنواع بدائية في حاويات، يمكنك إنشاء فصول نوع المجمع البدائية والتي سوف تكون مشابهة لأنواع بدائية autoboxed جاوة (في المثال الخاص بك هي في الواقع يجري autoboxed والحرفية مطبوعة البدائية)؛ تظهر حالات منها في C ++ مدونة (ويمكن أن (تقريبا) يتم استخدامها) تماما مثل المتغيرات أعضاء البدائي / البيانات.

باكر كائن لأنواع المدمج في من هياكل البيانات والخوارزميات مع أنماط التصميم الشيئية في C ++ .

ومع الكائن ملفوفة يمكنك استخدام مشغل ج ++ typeid () لمقارنة نوع. وأنا متأكد من أن المقارنة التالية العمل: if (typeid(o) == typeid(Int)) [أين كثافة العمليات سيكون الطبقة ملفوفة لنوع بدائي كثافة العمليات، الخ ...] (وإلا ببساطة إضافة وظيفة لأغلفة البدائية الخاصة بك وترجع typeid وعلى النحو التالي: if (o.get_typeid() == typeid(Int)) ...

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

أنا عديم الخبرة إلى حد ما، ولكن هذا ما سأختاره-

  1. قم بإنشاء فئة أساسية لجميع الفئات التي تحتاج إلى معالجتها.
  2. اكتب فئة الحاوية/إعادة استخدام فئة الحاوية.(تمت المراجعة بعد رؤية الإجابات الأخرى - كانت نقطتي السابقة غامضة للغاية.)
  3. اكتب رمزًا مشابهًا.

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

آمل أن يساعد هذا.

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

ودعونا رفض المثال الخاص بك، والنظر في شيء أقرب إلى مثال واقع الحياة. على وجه التحديد، بعض التعليمات البرمجية رأيت في مشروع مفتوح المصدر الحقيقي. وحاولت لمضاهاة وحدة المعالجة المركزية في مجموعة شخصية. وبالتالي فإنه من شأنه أن يضع في مجموعة بايت واحد "كود المرجع"، تليها 0 أو 1 أو 2 بايت التي يمكن أن تكون حرف، عدد صحيح، أو مؤشر إلى سلسلة، استنادا إلى رمز مرجع سابق. للتعامل مع ذلك، ينطوي على الكثير من بت تافه.

وبلدي حل بسيط: 4 مداخن منفصلة <> الصورة: واحد للتعداد "شفرة تشغيل" واحد في كل من حرف، [إينتس] والسلسلة. خذ المقبل قبالة كومة شفرة التشغيل، وسوف يأخذك أي من الثلاثة الأخرى للحصول على المعامل.

وهناك فرصة جيدة جدا يمكن التعامل مع المشكلة الفعلية الخاصة بك بطريقة مماثلة.

حسنا، يمكنك إنشاء فئة أساسية ثم قم بإنشاء الطبقات التي ترث منه. ثم، وتخزينها في الأمراض المنقولة جنسيا :: ناقلات.

والجواب القصير هو ... لا يمكنك.

والجواب طويلة هي ... كنت قد لتحديد التسلسل الهرمي الجديد بنفسك من الكائنات التي ترث كل من كائن قاعدة. في جاوة تنحدر كافة الكائنات في نهاية المطاف من "كائن"، وهو ما يسمح لك أن تفعل هذا.

وRTTI (تشغيل الوقت نوع من المعلومات) في C ++ كانت دائما صعبة، خصوصا عبر مترجم.

وأنت أفضل خيار هو استخدام STL وتحديد واجهة من أجل تحديد نوع الكائن:

public class IThing
{
   virtual bool isA(const char* typeName);
}

void myFunc()
{
   std::vector<IThing> things;

   // ...

   things.add(new FrogThing());
   things.add(new LizardThing());

   // ...

   for (int i = 0; i < things.length(); i++)
   {
       IThing* pThing = things[i];

       if (pThing->isA("lizard"))
       {
         // do this
       }
       // etc
   }
}

ومايك

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