سؤال

الخلفية

لدي الطبقة الحاوية التي تستخدم ناقلات<std::string> داخليا.لقد قدمت طريقة AddChar(std::string) أن هذا المجمع الدرجة التي لا push_back() الداخلية ناقلات.في قانون بلدي, لا بد لي من إضافة عناصر متعددة إلى الحاوية بعض الوقت.لذلك لا بد لي من استخدام

container.AddChar("First");
container.AddChar("Second");

هذا يجعل رمز أكبر.وذلك لجعلها أكثر سهولة ، أخطط الزائد المشغل <<.حتى أستطيع أن أكتب

container << "First" << "Second"

واثنين من العناصر سوف تحصل على إضافة الأساسية مكافحة ناقلات.

هنا هو رمز اعتدت على ذلك

class ExtendedVector
{
private:
    vector<string> container;

public:
    friend ExtendedVector& operator<<(ExtendedVector& cont,const std::string str){
        cont.AddChar(str);
        return cont;
    }

    void AddChar(const std::string str)
    {
        container.push_back(str);
    }

    string ToString()
    {
        string output;
        vector<string>::iterator it = container.begin();
        while(it != container.end())
        {
            output += *it;
            ++it;
        }
        return output;
    }
};

وهي تعمل كما هو متوقع.

الأسئلة

  1. هو مشغل الزائد مكتوبة بشكل صحيح ؟
  2. هو ممارسة جيدة الزائد المشغلين في مثل هذه الحالات ؟
  3. سوف تكون هناك أي مشكلات في الأداء أو أي قضايا أخرى مع هذا الرمز ؟

أي أفكار ؟

تحرير

بعد سماع تعليقات ممتازة, قررت أن لا تفرط << كما أنه لا معنى له هنا.أنا إزالة المشغل الزائد رمز هنا هو رمز النهائية.

class ExtendedVector
{
private:
    vector<string> container;

public:

    ExtendedVector& AddChar(const std::string str)
    {
        container.push_back(str);
        return *this;
    }

         .. other methods
}

هذا يسمح لي أن أضيف

container.AddChar("First").AddChar("Second")

في C#, أنا يمكن أن تفعل هذا بسهولة باستخدام params الكلمة.رمز سوف يكون مثل

void AddChar(params string[] str)
{
    foreach(string s in str)
       // add to the underlying collection
}

أنا أعرف في C++, يمكننا استخدام ... تحديد المتغير langth من المعلمات.ولكن AFAIK ، فإنه ليس من النوع الآمن.لذلك هو الممارسة الموصى بها للقيام بذلك ؟ حتى أستطيع أن أكتب

container.AddChar("First","Second")

شكرا على الردود.

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

المحلول

هو مشغل الزائد مكتوبة بشكل صحيح ؟

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

class ExtendedVector {
    ...
};

// note, now it is *entirely decoupled* from any private members!
ExtendedVector& operator<<(ExtendedVector& cont, const std::string& str){
    cont.AddChar(str);
    return cont;
}

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

هو ممارسة جيدة الزائد المشغلين في مثل هذه الحالات ؟

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

QStringList items; 
items << "item1" << "item2";

حالة مماثلة هي boost.format وهو أيضا إعادة استعمال operator% من أجل تمرير الوسائط النائبة في سلسلة:

format("hello %1%, i'm %2% y'old") % "benny" % 21

انها بالطبع أيضا يمكن القول أن استخدامها هناك.ولكن استخدام printf تنسيق يحدد معروفة و لذلك استخدامه على ما يرام هناك أيضا ، imho.ولكن كما هو الحال دائما ، كما أن أسلوب شخصي لذلك أعتبر مع حبة الملح :)

كيف يمكنني أن أقبل طول متغير الحجج في typesafe الطريقة ؟

حسنا, هناك طريقة قبول ناقلات إذا كنت تبحث عن متجانسة الحجج:

void AddChars(std::vector<std::string> const& v) {
    std::vector<std::string>::const_iterator cit =
        v.begin();
    for(;cit != v.begin(); ++cit) {
        AddChar(*cit);
    }
}

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

#define GEN_OVERLOAD(X) \
void AddChars(GEN_ARGS(X, std::string arg)) { \
    /* now access arg0 ... arg(X-1) */ \
    /* AddChar(arg0); ... AddChar(arg(N-1)); */ \
    GEN_PRINT_ARG1(X, AddChar, arg) \
}

/* call macro with 0, 1, ..., 9 as argument
GEN_PRINT(10, GEN_OVERLOAD)

أن البرمجية الزائفة.يمكنك إلقاء نظرة على دفعة المعالج المكتبة هنا.

التالي C++ الإصدار سوف نقدم أفضل بكثير من إمكانيات.مهيئ قوائم يمكن استخدامها:

void AddChars(initializer_list<std::string> ilist) {
    // range based for loop
    for(std::string const& s : ilist) {
        AddChar(s);
    }
}

...
AddChars({"hello", "you", "this is fun"});

ومن الممكن أيضا في التالي C++ لدعم التعسفي العديد من (مختلطة من نوع) الحجج باستخدام variadic قوالب.GCC4.4 سوف يكون الدعم لهم.دول مجلس التعاون الخليجي 4.3 بالفعل جزئيا يعتمد عليها.

نصائح أخرى

1) نعم ، ما عدا منذ AddChar العامة ليس هناك سبب يجب أن تكون friend.

2) هذا القول. << هو نوع من في الموقف من كونها المشغل الذي الحمولة الزائدة على الأمور "غريبة" على الأقل على مضض قبلت.

3) لا شيء واضح.كما هو الحال دائما ، التنميط هو صديقك.قد ترغب في النظر في اجتياز سلسلة المعلمات AddChar و operator<< قبل const المرجعية (const std::string&) لتجنب لا لزوم لها نسخ.

هو ممارسة جيدة الزائد المشغلين في مثل هذه الحالات ؟

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

أنا أفضل أن لا تفرط بهذه الطريقة شخصيا بسبب ناقلات عادة لا يكون لها طاقتها shift الأيسر المشغل - انها ليست حقا انها لغة ;-)

ربما العودة إشارة من AddChar بدلا من ذلك مثل ذلك:

ExtendedVector& AddChar(const std::string& str) {
    container.push_back(str);
    return *this;
}

لذلك يمكنك أن تفعل ثم

container.AddChar("First").AddChar("Second");

التي ليست في الحقيقة أكبر بكثير من bitshift المشغلين.

(انظر أيضا لوجان تعليق حول تمرير السلاسل في بالإشارة بدلا من القيمة).

المشغل الحمولة الزائدة في هذه الحالة ليست ممارسة جيدة كما أنه يجعل رمز أقل قابلية للقراءة.القياسية std::vector لا يكون ذلك إما دفع عناصر لأسباب وجيهة.

إذا كنت قلقا حول الطالب رمز كونها طويلة جدا ، يمكن أن تنظر في هذا بدلا من طاقتها المشغل:

container.AddChar("First").AddChar("Second");

ولن يكون هذا ممكنا إذا كان لديك AddChar() عودة *this.

ومن المضحك أن لديك هذا toString() وظيفة.في أن القضية ، operator<< لانتاج تيار سيكون الشيء القياسية استخدام بدلا من ذلك!حتى إذا كنت ترغب في استخدام مشغلي جعل toString() وظيفة ساعة operator<<.

المشغل هو غير صحيح طاقتها هنا.لا يوجد سبب يجعل المشغل لصديق لأنه يمكن أن تكون عضوا في الصف.من الوظائف التي لا الفعلية أفراد الطبقة (مثل عند الحمولة الزائدة << بالنسبة ostream لذلك الكائن يمكن أن يكون الإخراج cout أو ofstreams).

ما كنت فعلا تريد المشغل أن يكون:

ExtendedVector& operator<<(const std::string str){
    AddChar(str);
    return *this;
}

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

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

هذا هو الذهاب الى جعل الأمور مربكة نوعا ما, وأود أن استخدام نفس بناء الجملة كما std::cin إلى متغير:

std::cin >> someint;

"First" >> container;

هذا الطريق هو على الأقل إدخال المشغل.لي عند أي شيء له << مثقلة مشغل أتوقع أن يكون إخراج شيء.تماما مثل std::cout.

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