سؤال

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

class Small
{
  public:
    void foo(); 
    void bar(int x);
    // and many more functions
};

class Big
{
  public:
    void foo()
    {
        for (size_t i = 0; i <  VectorOfSmalls.size(); i++)
            VectorOfSmalls[i]->foo();
    }
    void bar(int x)
    {
        for (size_t i = 0; i <  VectorOfSmalls.size(); i++)
            VectorOfSmalls[i]->bar(x);
    }
    // and many more functions
  private:
    vector<Small*> VectorOfSmalls;
};

أريد تبسيط الكود وإيجاد طريقة لعدم تكرار المتجه في كل وظيفة.

لقد فكرت في إنشاء دالة تستقبل مؤشرًا لتعمل، وتستدعي الوظيفة المدببة على كل عضو في المتجه.لكنني لست متأكدًا من أن استخدام المؤشرات للوظائف في C++ يعد فكرة جيدة.

لقد كنت أفكر أيضًا في الممثلين و functionoids, ، لكنه سيجبرني على إنشاء فئة لكل وظيفة ويبدو ذلك مبالغة.

الحل الآخر المحتمل هو إنشاء دالة تتلقى سلسلة، وتستدعي الأمر وفقًا للسلسلة:

void Big::call_command(const string & command)
{
    for (size_t i = 0; i <  VectorOfSmalls.size(); i++)
    {
       if (command == "foo")
           VectorOfSmalls[i]->foo();
       else if (command == "bar")
           VectorOfSmalls[i]->bar();
    }
}
void Big::foo()
{
    call_command("foo");
}

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

فماذا تنصحني؟هل يجب أن أترك كل شيء كما هو الآن؟

يحرر:يمكنني استخدام STL فقط وليس التعزيز (المترجمين القدامى).

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

المحلول

حسنًا، يمكنك إعادة كتابة حلقات for لاستخدام التكرارات والمزيد من STL مثل هذا:

void foo() {
    std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo));
}

void bar() {
    std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar));
}

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

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

class Big {
public:
    enum Command {
        DO_FOO,
        DO_BAR
    };

void doit(Command cmd) {
    switch(cmd) {
    case DO_FOO:
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::foo));
        break;
    case DO_BAR:
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(&Small::bar));
        break;
    }
};

أيضًا، كما ذكرت، من السهل جدًا استبدال &Small::مهما كان مؤشر وظيفة العضو وتمرير ذلك كمعلمة.يمكنك أيضًا جعله قالبًا أيضًا.

class Big {
public:
    template<void (Small::*fn)()>
    void doit() {
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn));
    }
};

ثم يمكنك القيام بما يلي:

Big b;
b.doit<&Small::foo>();
b.doit<&Small::bar>();

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

إذا كنت تريد أن تكون قادرًا على التعامل مع معلمة واحدة، فستحتاج إلى إضافة bind2nd أيضًا، وإليك مثال كامل:

#include <algorithm>
#include <functional>
#include <iostream>
#include <vector>

class Small {
public:
    void foo() { std::cout << "foo" << std::endl; }
    void bar(int x) { std::cout << "bar" << std::endl; }
};


class Big {
public:
    template<void (Small::*fn)()>
    void doit() {
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::mem_fun(fn));
    }

    template<class T, void (Small::*fn)(T)>
    void doit(T x) {
        std::for_each(VectorOfSmalls.begin(), VectorOfSmalls.end(), std::bind2nd(std::mem_fun(fn), x));
    }
public:
    std::vector<Small *> VectorOfSmalls;
};

int main() {
    Big b;
    b.VectorOfSmalls.push_back(new Small);
    b.VectorOfSmalls.push_back(new Small);

    b.doit<&Small::foo>();
    b.doit<int, &Small::bar>(5);
}

نصائح أخرى

إذا كنت تستخدم مكتبة std، فيجب عليك إلقاء نظرة عليها for_each.

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

دفعة :: وظيفة و < وأ href = "http://www.boost.org/doc/libs/1_37_0/libs/bind/bind.html" يختلط = "نوفولو noreferrer"> دفعة :: ربط :

void Big::call_command(const boost::function<void (Small*)>& f)
{
    for (size_t i = 0; i <  VectorOfSmalls.size(); i++)
    {
        f(VectorOfSmalls[i]);
    }
}

int main()
{
    Big b;
    b.call_command(boost::bind(&Small::foo, _1));
    b.call_command(boost::bind(&Small::bar, _1, 5));
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top