سؤال

انا اتعلم c++ و علمت مؤخرا (هنا في تجاوز سعة مكدس) حول النسخ تبديل لغة و لدي بعض الأسئلة حول هذا الموضوع.لذا أفترض أن الطبقة التالية باستخدام النسخ تبديل لغة فقط على سبيل المثال:

class Foo {
private:
  int * foo;
  int size;

public:
  Foo(size_t size) : size(size) { foo = new int[size](); }
  ~Foo(){delete foo;}

  Foo(Foo const& other){
    size = other.size;
    foo = new int[size];
    copy(other.foo, other.foo + size, foo);
  }

  void swap(Foo& other) { 
    std::swap(foo,  other.foo);  
    std::swap(size, other.size); 
  }

  Foo& operator=(Foo g) { 
    g.swap(*this); 
    return *this; 
  }

  int& operator[] (const int idx) {return foo[idx];}
};

سؤالي هو: لنفترض أن لدى فئة أخرى أن يكون فو كائن البيانات ولكن لا توجد مؤشرات أو الموارد الأخرى التي قد تحتاج إلى العرف نسخ أو الإحالة:

class Bar {
private:
  Foo bar;
public:
  Bar(Foo foo) : bar(foo) {};
  ~Bar(){};
  Bar(Bar const& other) : bar(other.bar) {}; 
  Bar& operator=(Bar other) {bar = other.bar;}
};

الآن لدي مجموعة من الأسئلة:

  1. هي الطرق و المنشئات كما نفذت أعلاه Bar فئة آمنة ؟ بعد أن استخدمت النسخ مقايضة Foo تجعلني على يقين من أن أي ضرر يمكن القيام به عند تعيين أو نسخ Bar?

  2. يمر الحجة بالرجوع في منشئ نسخة في المبادلة هو إلزامي ؟

  3. هل من الصواب أن نقول أنه عندما حجة operator= يتم تمريرها حسب القيمة, منشئ نسخة وتسمى هذه الحجة إنشاء نسخة مؤقتة من وجوه ، و أن هذا نسخ ثم تبادلت مع *this?إذا مررت المرجعية في operator= كنت أود أن يكون مشكلة كبيرة ، أليس كذلك ؟

  4. هناك حالات في هذا المصطلح فشل في توفير السلامة الكاملة في نسخ تعيين Foo?

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

المحلول

1 - هي الطرق و المنشئات كما نفذت أعلاه شريط الدرجة آمنة ؟ بعد أن استخدمت النسخ مقايضة فو يجعلني متأكدا من أن أي ضرر يمكن القيام به عند تعيين أو نسخ شريط ؟

بخصوص نسخة المنشئ:هذا هو دائما آمنة (كل شيء أو لا شيء).إما يكمل (كل) ، أو أنه يطرح استثناء (لا شيء).

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

ومع ذلك فإنه سوف لا يزال تكون "آمنة" ، بمعنى أنه لن تسرب أي موارد.وبالطبع الدولة من كل عضو على حدة سوف تكون متسقة - مجرد حالة من الطبقة الجديدة لن تكون متسقة ، لأن أحد الأعضاء تم تعيينه أخرى.

2 - اجتياز حجة بالرجوع في منشئ نسخة في المبادلة هو إلزامي ؟

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

3 - هل من الصواب أن نقول أنه عندما حجة المشغل= يتم تمريرها حسب القيمة, منشئ نسخة وتسمى هذه الحجة إنشاء نسخة مؤقتة من وجوه ، و أن هذا نسخ ثم تبادلت مع *هذا ؟ إذا مررت الإشارة في المشغل= كنت أود أن يكون مشكلة كبيرة ، أليس كذلك ؟

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

4 - هل هناك حالات هذا المصطلح فشل في توفير السلامة الكاملة في نسخ تعيين فو?

ولا أن أعرف.بالطبع يمكن للمرء دائما أن تجعل الأمور تفشل مع خيوط المعالجة المتعددة (إن لم يكن بشكل صحيح متزامنة) ، ولكن يجب أن يكون واضحا.

نصائح أخرى

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

class Foo {
private:
  int size;
  int * foo;

public:
  Foo(size_t size) : size(size), foo(new int[size]) {}
  ~Foo(){delete[] foo;} // note operator delete[], not delete

  Foo(Foo const& other) : size(other.size), foo(new int[other.size]) {
    copy(other.foo, other.foo + size, foo);
  }

  Foo& swap(Foo& other) { 
    std::swap(foo,  other.foo);  
    std::swap(size, other.size); 
    return *this;
  }

  Foo& operator=(Foo g) { 
    return swap(g); 
  }

  int& operator[] (const int idx) {return foo[idx];}
};

و

class Bar {
private:
  Foo bar;
public:
  Bar(Foo foo) : bar(foo) {};
  ~Bar(){};
  Bar(Bar const& other) : bar(other.bar) { }
  Bar& swap(Bar &other) { bar.swap(other.bar); return *this; }
  Bar& operator=(Bar other) { return swap(other); }
}

والذي يستخدم نفس المصطلح في جميع أنحاء

ملاحظة

كما ذكر في التعليق على المسألة ، Bar's مخصص نسخ المنشئات.... الخغير ضرورية ، ولكن سنفترض Bar وقد أشياء أخرى :-)

السؤال الثاني

يمر بالرجوع إلى swap هناك حاجة لأن كلا الحالات يتم تغيير.

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

السؤال الثالث

نعم

السؤال الرابع

لا, ولكنها ليست دائما الطريقة الأكثر فعالية للقيام بهذه الأمور

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

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

ملاحظة: في المثال الخاص بك ، نسخ مبادلة المصطلح دائما يقوم أحد deallocation ، و في كثير من الأحيان (عندما rhs المهمة هو lvalue) تخصيص واحد كذلك.

الملاحظة: عندما size() == rhs.size(), لا deallocation أو تخصيص تحتاج إلى القيام به.كل ما عليك القيام به هو copy.هذا هو كثيرا, أسرع بكثير.

Foo& operator=(const Foo& g) {
    if (size != g.size)
        Foo(g).swap(*this); 
    else
       copy(other.foo, other.foo + size, foo);
    return *this; 
}

أولا-هاء.التحقق من حالة حيث يمكن إعادة تدوير الموارد الأولى.ثم نسخ مبادلة (أو أيا كان) إذا لم تتمكن من إعادة تدوير الموارد الخاصة بك.

ملاحظة: تعليقاتي لا تتعارض مع إجابات جيدة تعطى من قبل الآخرين ، ولا أي صحة القضايا.تعليقي هو فقط حول أداء هامة عقوبة.

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