سؤال

هل هناك نمط قانوني أو موصى به لتنفيذ التحميل الزائد للمشغل الحسابي في فئات تشبه الأرقام C ++؟

من الأسئلة الشائعة عن C ++ ، لدينا مشغل مهام آمن من الاستثناء يتجنب معظم المشكلات:

class NumberImpl;

class Number {
   NumberImpl *Impl;

   ...
};

Number& Number::operator=(const Number &rhs)
{
   NumberImpl* tmp = new NumberImpl(*rhs.Impl);
   delete Impl;
   Impl = tmp;
   return *this;
}

ولكن بالنسبة للمشغلين الآخرين ( +، += ، إلخ ..) يتم تقديم القليل من المشورة بخلاف جعلهم يتصرفون مثل المشغلين على الأنواع المدمجة.

هل هناك طريقة قياسية لتحديد هذه؟ هذا ما توصلت إليه - هل هناك مخاطر لا أراها؟

// Member operator
Number& Number::operator+= (const Number &rhs)
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number lhs, const Number &rhs)
{
     return lhs += rhs;
}
هل كانت مفيدة؟

المحلول

في كتاب Bjarne Stroustrup "لغة البرمجة C ++"، في الفصل 11 (واحد مخصص لحمل المشغل الزائد) ، يمر عبر فئة من أجل نوع رقم معقد (القسم 11.3).

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

بشكل عام ، ما لديك يبدو جيدًا.

نصائح أخرى

الشيء الكبير الذي يجب مراعاته عند كتابة أي مشغل هو أن مشغلي الأعضاء لا يخضعون لتحويلات على المعلمة اليسرى:

struct example {
  example(int);
  example operator + (example);
};

void foo() {
  example e(3), f(6);
  e + 4; // okay: right operand is implicitly converted to example
  e + f; // okay: no conversions needed.
  6 + e; // BAD: no matching call.
}

هذا لأن التحويل لا ينطبق على this لوظائف الأعضاء ، وهذا يمتد إلى المشغلين. إذا كان المشغل بدلاً من ذلك example operator + (example, example) في مساحة الاسم العالمية ، سيتم تجميعها (أو إذا تم استخدام Pass-Const-Ref).

نتيجة لذلك ، مثل المشغلين المتماثلون + و - يتم تنفيذها عمومًا على أنها غير أعضاء ، في حين أن مشغلي التخصيص المركب مثل += و -= يتم تنفيذها كأعضاء (يقومون أيضًا بتغيير البيانات ، مما يعني أنها يجب أن تكون أعضاء). وبما أنك ترغب في تجنب ازدواجية الكود ، يمكن تنفيذ المشغلين المتماثلون من حيث المركب المركب (كما في مثال الكود الخاص بك ، على الرغم من أن الاتفاقية توصي بعمل مؤقت داخل الوظيفة).

الاتفاقية هي الكتابة operator+(const T&) و operator-(const T&) من ناحية operator+=(const T&) و operator-=(const T&). إذا كان من المنطقي إضافة وطرح/من الأنواع البدائية ، فيجب عليك كتابة مُنشئ يبني الكائن من النوع البدائي. بعد ذلك ، سيعمل المشغلون المحمولين على الأنواع البدائية ، لأن المترجم سوف يطلق على المنشئ المناسب ضمنيًا.

كما ذكرت نفسك ، يجب عليك تجنب منح امتيازات الوصول إلى الوظائف التي لا تحتاج إليها. ولكن في الكود الخاص بك أعلاه ل operator+(Number, const Number&) أنا شخصياً أقوم بإجراء كل من المعلمات المراجع واستخدام درجة الحرارة. أعتقد أنه ليس من المستغرب أن يكون المعلق أسفل سؤالك قد فاته هذا ؛ ما لم يكن لديك سبب وجيه لعدم ذلك ، تجنب المفاجآت والحيل وأن تكون واضحًا قدر الإمكان.

إذا كنت تريد أن تدمج الكود الخاص بك مع الأنواع الرقمية الأخرى ، على سبيل المثال std::complex, ، احترس من التحويلات الدورية. هذا هو ، لا تزود operator OtherNumeric() في Numeric إذا OtherNumeric يوفر مُنشئًا يأخذ ملف Numeric معامل.

من التقليدي كتابة المشغل x من حيث المشغل = x
كما أنها تقليدية جميع المعلمات للمشغلين القياسيين هي const

// Member operator
// This was OK
Number& Number::operator+= (Number const& rhs) 
{
    Impl->Value += rhs.Impl->Value; // Obviously this is more complicated
    return *this;
}

// Non-member non-friend addition operator
Number operator+(Number const& lhs,Number const& rhs)
{
     // This I would set the lhs side to const.
     // Make a copy into result.
     // Then use += add the rhs
     Number result(lhs);
     return result += rhs;
}

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

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