سؤال

لدي فئة حاوية بسيطة مع منشئ نسخة.

هل تنصح باستخدام Getters و Setters، أو الوصول إلى متغيرات الأعضاء مباشرة؟

public Container 
{
   public:
   Container() {}

   Container(const Container& cont)          //option 1
   { 
       SetMyString(cont.GetMyString());
   }

   //OR

   Container(const Container& cont)          //option 2
   {
      m_str1 = cont.m_str1;
   }

   public string GetMyString() { return m_str1;}       

   public void SetMyString(string str) { m_str1 = str;}

   private:

   string m_str1;
}
  • في المثال، كل الكود مضمن، ولكن في الرمز الحقيقي لدينا لا يوجد رمز مضمن.

تحديث (29 سبتمبر):

بعض هذه الإجابات مكتوبة بشكل جيد ولكن يبدو أنها تفتقد نقطة هذا السؤال:

  • هذا مثال بسيط مفتعلة لمناقشة المتغيرات باستخدام Getters / Seters مقابل

  • قوائم التهيئة أو وظائف المدقق الخاصة ليست فعليا جزءا من هذا السؤال. أنا أتساءل عما إذا كان التصميم سيجعل الرمز أسهل للحفاظ على وتوسيعه.

  • يركز بعض شركته التنفيذية على السلسلة في هذا المثال، لكنه مجرد مثال، تخيل أنه كائن مختلف بدلا من ذلك.

  • أنا لست قلقا بشأن الأداء. نحن لسنا برمجة على PDP-11

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

المحلول

هل تتوقع كيفية إرجاع السلسلة، على سبيل المثال. قلصت مساحة بيضاء، فحص NULL، إلخ؟ نفسه مع SetMystring ()، إذا كانت الإجابة بنعم، فأنت أفضل حالا مع طرق الوصول لأنك لا تضطر إلى تغيير التعليمات البرمجية الخاصة بك في أماكن Zillion ولكن فقط قم بتعديل طرق Getter و Setter هذه.

نصائح أخرى

تعديل: الإجابة على السؤال المحرر :)

هذا مثال بسيط مفتعلة ل ناقش باستخدام getters / setters vs المتغيرات

إذا كانت لديك مجموعة بسيطة من المتغيرات، فلا تحتاج إلى أي نوع من التحقق من الصحة، أو معالجة إضافية، فقد تفكر في استخدام جراب بدلا من ذلك. من أسئلة وأجوبة stroustrup:

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

باختصار، هذا ليس جافا. يجب أن لا تكتب getters / setters عادي لأنها سيئة مثل تعريض المتغيرات لهم أنفسهم.

قوائم التهيئة أو وظائف المدقق الخاصة ليست فعليا جزءا من هذا السؤال. أنا أتساءل عما إذا كان التصميم سيجعل الرمز أسهل للحفاظ على وتوسيعه.

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

أنا لست قلقا بشأن الأداء. نحن لسنا برمجة على PDP-11

هذا يدور حول النمط الأكثر أناقة، على الرغم من أنه في C ++ الرمز الأكثر أناقة لديه أفضل خصائص الأداء عادة.


يجب عليك استخدام initializer list. وبعد في التعليمات البرمجية الخاصة بك، m_str1 هو الافتراضي شيدت ومن بعد تعيين قيمة جديدة. يمكن أن يكون رمزك شيء مثل هذا:

class Container 
{
public:
   Container() {}

   Container(const Container& cont) : m_str1(cont.m_str1)
   { }

   string GetMyString() { return m_str1;}       
   void SetMyString(string str) { m_str1 = str;}
private:
   string m_str1;
};

@ chbrulak يجب أن لا imo التحقق من صحة cont.m_str1 في ال copy constructor. وبعد ما أقوم به، هو التحقق من صحة الأشياء في constructors. وبعد التحقق من الصحة في copy constructor يعني أنك تقوم بنسخ كائن غير مشكل في المقام الأول، على سبيل المثال:

Container(const string& str) : m_str1(str)
{
    if(!valid(m_str1)) // valid() is a function to check your input
    {
        // throw an exception!
    }
}

يجب عليك استخدام قائمة تهيئة، ثم يصبح السؤال لا معنى له، كما هو الحال في:

Container(const Container& rhs)
  : m_str1(rhs.m_str1)
{}

هناك قسم كبير في ماثيو ويلسون cypfect c ++. يشرح كل شيء عن قوائم تهيئة الأعضاء، وكيف يمكنك استخدامها بالاشتراك مع CONST و / أو الإشارات لجعل التعليمات البرمجية أكثر أمانا.

يحرر: مثال يظهر التحقق من الصحة والاتصالات:

class Container
{
public:
  Container(const string& str)
    : m_str1(validate_string(str))
  {}
private:
  static const string& validate_string(const string& str)
  {
    if(str.empty())
    {
      throw runtime_error("invalid argument");
    }
    return str;
  }
private:
  const string m_str1;
};

نظرا لأنها مكتوبة الآن (بدون أي مؤهلات للإدخال أو الإخراج) الخاص بك Getter و STERTER الخاص بك (Accessor and Mutator، إذا كنت تفضل)، فهي تنجذب أي شيء على الإطلاق، بحيث يمكنك أيضا جعل السلسلة العامة ويتم القيام به.

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

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

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

واحدة من نقاط القوة الرئيسية لفئة السلسلة في C ++ تستخدم التحميل الزائد للمشغل حتى تتمكن من استبدال شيء مثل:

strcpy(strcat(filename, ".ext"));

مع:

filename += ".ext";

لتحسين قابلية القراءة. لكن انظر إلى ما يحدث إذا كانت هذه السلسلة جزءا من الفصل الذي يفرضنا على الذهاب إلى Getter و STERTER:

some_object.setfilename(some_object.getfilename()+".ext");

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

some_object.filename += ".ext";

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

اسأل نفسك ما هي التكاليف والفوائد.

التكلفة: أعلى وقت التشغيل النفقات العامة. إن استدعاء الوظائف الافتراضية في CTORS هو فكرة سيئة، ولكن من غير المرجح أن تكون setters and getters الظاهري.

الفوائد: إذا قام STER / Getter بعمل شيء معقد، فأنت لا تكرر الرمز؛ إذا كان الأمر شيئا بلا شك، فأنت لا تنسى أن تفعل ذلك.

سوف تختلف نسبة التكلفة / الفائدة على فئات مختلفة. بمجرد التأكد من أن النسبة، استخدم حكمك. بالنسبة لفئات ثابتة، بطبيعة الحال، ليس لديك STINTERS، ولا تحتاج إلى Getters (نظرا لأن أعضاء CONS ويمكن أن تكون المراجع عامة حيث لا يمكن لأحد تغيير / إعادة استخدامها).

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

وإلا فسوف تضطر إلى التفكير في الواقع.

بنية ألفا {beta * m_beta؛ ألفا (): m_beta (بيتا جديد () جديد) {} ~ alpha () {delete m_beta؛ } ألفا (const alpha & a) {// بحاجة إلى نسخ؟ أو هل لديك دولة مشتركة؟ نسخ على الكتابة؟ m_beta = بيتا جديد (* A.M_BETA)؛ // خطأ m_beta = a.m_beta؛ }

لاحظ أنه يمكنك التجول حول Segfault المحتمل باستخدام smart_ptr - ولكن يمكنك الحصول على الكثير من المرح تصحيح الأخطاء الناتجة.

بالطبع يمكن أن تحصل حتى أكثر تسلية.

  • الأعضاء التي يتم إنشاؤها عند الطلب.
  • new beta(a.beta) يكون خاطئ - ظلم - يظلم في حال تقدمك بطريقة أو بأخرى تعدد الأشكال.

... المسمار غير ذلك - يرجى التفكير دائما عند كتابة منشئ نسخة.

لماذا تحتاج betters و setters على الإطلاق؟

بسيطة :) - إنهم يحافظون على الثمانية - أي ضمان الفصل الخاص بك يجعل، مثل "myString دائما لديه عدد من الأحرف".

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

كما قال آراك، فإن الأفضل سيستخدم قائمة تهيئة.


ليس بسيطا جدا (1):
سبب آخر لاستخدام Getters / Setters لا يعتمد على تفاصيل التنفيذ. هذه هي فكرة غريبة للحصول على نسخة ماسكة نسخة، عند تغيير تفاصيل التنفيذ هذه تحتاج دائما إلى ضبط CDA على أي حال.


ليس بسيطا جدا (2):
لإثبات خطأي، يمكنك بناء الثباتات التي تعتمد على المثيل نفسه، أو عامل خارجي آخر. مثال (مثير جدا) على سبيل المثال: "إذا كان عدد الحالات حتى، فإن طول السلسلة هو حتى، وإلا فإنه غريب". في هذه الحالة، سيتعين على Copy Ctor رمي السلسلة أو ضبطها. في مثل هذه الحالة، قد يساعد ذلك في استخدام STINTERS / Getters - ولكن هذا ليس هو CAS العام. يجب أن لا تستمد القواعد العامة من الشذوذ.

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

ناهيك أن نذكر أنك ربما توفر مكالمات بدالة قليلة إذا لم يتم إبزيم Getter.

إذا كانت Getters الخاصة بك (مضمنة) لا virtual, ، لا يوجد إيجاد إضافات ولا سفيخ في استخدامهم وصول الأعضاء المباشرين - فهو يبدو مجرد أبله بالنسبة لي من حيث الأناقة، ولكن لا توجد صفقة كبيرة في كلتا الحالتين.

إذا كانت Getters الخاصة بك افتراضية، فهناك يكون ...

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

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

على الرغم من أنني أتفق مع الملصقات الأخرى التي يوجد فيها العديد من المدخل C ++ "No-no's" في عينتك، فإن وضع ذلك إلى الجانب وإجابة على سؤالك مباشرة:

في الممارسة العملية، أميل إلى جعل العديد من مجالات الأعضاء الخاصة بي ولكن ليس كل مجالات عضوية * من أجل البدء، ثم نقلها للحصول على / تعيين عند الحاجة.

الآن، سأكون أول من يقول إن هذه ليست بالضرورة ممارسة موصى بها، وسوف يبحث العديد من الممارسين بذلك، ويقولون إن كل مجال يجب أن يكون له تقرير / جيوب.

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

ymmv.

الترددات اللاسلكية

  • يمكنك استدعاء الحقول "المتغيرات" - أشجعك على استخدام هذا المصطلح فقط للمتغيرات المحلية داخل الوظيفة / الطريقة
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top