سؤال

في كثير من الأحيان أقوم بإضافة Empty طريقة لكائنات C++ الخاصة بي لمسح الحالة الداخلية باستخدام رمز مشابه لما يلي.

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        *this = Foo();
    }
};

يبدو أن هذا أفضل من تكرار التعليمات البرمجية في المنشئ، لكنني أتساءل عما إذا كان ذلك *this = Foo() هو نهج شائع عند الرغبة في مسح كائن؟هل هناك أي مشاكل مع هذا الانتظار ليعضني على مؤخرتي؟هل هناك طرق أخرى أفضل لتحقيق هذا النوع من الأشياء؟

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

المحلول

وكنت السماح منشئ الدعوة وظيفة لي بدلا من ذلك:

class Foo
{
private:
    int n_;
    std::string str_;
public:
    Foo()
    {
        Reset();
    }

    void Reset()
    {
        n_ = 1234;
        str_ = "Hello, world!";
    }
};

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

نصائح أخرى

والمشاكل المحتملة؟ كيف يمكنك أن تعرف أن * هذا هو حقا فو؟

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

وشخصيا، كنت إزالة الأسلوب الخالي، وتحل محل جميع استخدامات هذه الطريقة مع هذا:

// let's say, that you have variables foo and pfoo - they are properly initialized.
Foo foo, *pfoo;

// replace line "foo.Empty()" with:
foo = Foo();

// replace line "pfoo->Empty()" with:
delete pfoo;
pfoo = new Foo();
// or
*pfoo = Foo();

وأنا حقا لا أرى أي ميزة في وجود هذا الأسلوب الخالي. انه يخفي حقيقة ما يحدث في الكائن على ساحرة انه دعا، اسم ليس الخيار الأفضل سواء.

إذا كان الطالب يريد فعلا كائن نظيفة - أنه سوف يكون بناء الكائن نفسه لا مشكلة

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

وهناك شيء أكثر شيوعا من ما كنت قد اقترحت. باستخدام المبادلة.

وأساسا كنت تفعل شيئا مثل هذا:

T().swap(*this);

ولأن العديد من الحاويات القياسية (جميع الحاويات STL؟) وقتا طريقة تبادل مستمر، وهذا هو وسيلة لطيفة وبسيطة لمسح الحاويات وتأكد من انها التخزين يتم تحريرها.

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

والنظر في استخدام new التنسيب:

void Empty() {
    this->~Foo();
    new (this) Foo();
}

والتعليمات البرمجية استدعاء operator = التي قد تؤدي إلى جميع أنواع الآثار الجانبية.

تعديل في الرد على التعليقات. - هذا الرمز <م> بالتأكيد اضحة المعالم، والمعيار يسمح بشكل صريح. انا ذاهب الى الرد على الفقرة في وقت لاحق إذا وجدت الوقت. حول delete - بطبيعة الحال. ما قصدته كان ~Foo()، وكان هذا خطأ غير مقصود. ونعم، روب هو حق أيضا؛ التدمير الكائن ضروري فعلا هنا للاتصال المدمر سلسلة ل.

وهذا يمكن أن يكون مصدرا محتملا للتسرب الذاكرة إذا كان لديك الذاكرة المخصصة حيوي في منشئ.

وهنا هو كيف أفعل ذلك:

class Foo {
private:
    int n_;
    std::string str_;
public:
    Foo() : n_(1234), str_("Hello, world!")
    {
    }

    void Empty()
    {
        Foo f;
        swap(f);
    }

    void swap(Foo & other) {
        std::swap(n_, other.n_);
        swap(str_, other.str_);
    }
};

void swap(Foo & one, Foo & other) {
    one.swap(other);
}

ووضع ظيفة المبادلة في نفس مساحة الاسم مثل الطبقة فو. يجد حجة بحث يعتمد عليه عند قيام المستخدمين دعوة لمبادلة لمبادلة اثنين فو. يمكنك تنفيذ operator= باستخدام وظيفة المبادلة للغاية.

يبدو أن هذا أفضل من تكرار التعليمات البرمجية في المُنشئ، لكنني تساءلت عما إذا كانت *this = Foo() طريقة شائعة عند الرغبة في مسح كائن؟

إن مسح كائن ليس أمرًا شائعًا:والأكثر شيوعًا، إما أن يتم إنشاء كائن (وربما حتى كائن غير قابل للتغيير) ويحتوي على بيانات حقيقية، أو لا يتم إنشاء مثيل له.

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

هل هناك أي مشاكل مع هذا الانتظار ليعضني على مؤخرتي؟

نعم:

  • إذا لم يكن هذا حقا Foo ولكن بدلا من ذلك أ DerivedFoo
  • لو Fooعامل التعيين الخاص بـ غير موجود، أو إذا كان به أخطاء (على سبيل المثال.إذا لم يتم تعريفه وكان العامل الافتراضي ليس جيدًا، على سبيل المثال.لأن عضو البيانات هو مؤشر مجرد).

هل هناك طرق أخرى أفضل لتحقيق هذا النوع من الأشياء؟

نعم، ربما تكون الوظيفة المجانية أفضل (من شأنها أن تتجنب المشكلتين المذكورتين أعلاه):

template<class T> void reconstruct(T* p)
{
    p->~T();
    new (p) T();
}

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

ومما يجعلها أكثر أمانا من حيث الذاكرة سيتم استدعاء هذا-> حذف وهذا = فو جديد () - ولكنه سيكون بطيئا

وإذا كنت تريد أن تكون فائق السرعة إنشاء حقل كائن فارغ ثابت وmemcpy في إعادة تعيين.

وإذا كنت تريد أن تكون مجرد بسرعة تعيين خصائص واحدا تلو الآخر.

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

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