سؤال

بدلا من الحاجة إلى تذكر تهيئة بسيطة 'ج' هيكل قد تستمد من الصفر في منشئ مثل هذا:

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct()
    {
        memset(this, 0, sizeof(MY_STRUCT));
    }
};

هذه الحيلة غالبا ما يستخدم لتهيئة Win32 الهياكل و في بعض الأحيان يمكن تعيين كل مكان cbSize الأعضاء.

الآن, طالما ليس هناك دالة ظاهري جدول memset الدعوة إلى تدمير هل هذا آمن الممارسة ؟

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

المحلول

الديباجة:

في حين جوابي هو لا يزال موافق أجد litb الجواب تماما متفوقة على الألغام بسبب:

  1. وهو يعلم لي خدعة أنني لا أعرف (litb إجابات وعادة ما يكون هذا التأثير ، ولكن هذه هي المرة الأولى التي أدون)
  2. فهو يجيب بالضبط السؤال (وهذا هو ، تهيئة الأصلي البنية جزء من الصفر)

لذا رجاء النظر في litb الجواب قبل لي.في الواقع, أقترح السؤال هو المؤلف إلى النظر في litb الجواب السليم.

الرد الأصلي

وضع الكائن الحقيقي (أيstd::string) الخ.داخل استراحة, لأن الكائن الحقيقي سيتم تهيئة قبل memset ثم تجاوزها من قبل أصفار.

باستخدام التهيئة القائمة لا تعمل g++ (أنا متفاجئ...).تهيئة بدلا من ذلك في CMyStruct منشئ الجسم.سيكون C++ ودية:

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct() { n1 = 0 ; n2 = 0 ; }
};

P. S.:أنا افترض أنك فعلت لا السيطرة على MY_STRUCT بالطبع.مع التحكم يمكنك إضافة منشئ مباشرة داخل MY_STRUCT ونسي الميراث.لاحظ أنه يمكنك إضافة غير ظاهري أساليب C مثل البنية, و مازال تتصرف باعتبارها البنية.

تحرير:وأضاف في عداد المفقودين قوسين بعد لو فرانكو التعليق.وذلك بفضل!

تحرير 2 :حاولت قانون g++, و لسبب ما, باستخدام التهيئة القائمة لا يعمل.تصحيح التعليمات البرمجية باستخدام منشئ الجسم.الحل لا يزال ساري المفعول ، على الرغم من.

يرجى تقييم الوظيفة كما رمز الأصلي تم تغيير (انظر التغيير لمزيد من المعلومات).

تحرير 3 :بعد قراءة روب تعليق, اعتقد انه نقطة جديرة بالمناقشة:"متفق عليه ، ولكن هذا يمكن أن تكون هائلة Win32 هيكل والتي قد تتغير مع SDK الجديدة ، لذلك memset هو دليل على المستقبل."

أنا أختلف:مع العلم مايكروسوفت, هذا لن يغير من الكمال التوافق.أنها سوف تخلق بدلا موسعة MY_STRUCTالسابق البنية مع نفسه الأولي تخطيط MY_STRUCT ، additionnal أعضاء في النهاية و يمكن التعرف عليها من خلال "حجم" عضو متغير مثل البنية المستخدمة RegisterWindow, IIRC.

لذا صالحة فقط نقطة المتبقية من روب التعليق هو "هائلة" البنية.في هذه الحالة, ربما memset هو أكثر ملاءمة ، ولكن سيكون لديك لجعل MY_STRUCT متغير عضو CMyStruct بدلا من يرث منه.

أرى آخر هاك, ولكن أعتقد أن هذا من شأنه كسر لأن من الممكن البنية محاذاة المشكلة.

تحرير 4:يرجى إلقاء نظرة على فرانك كروجر هو الحل.لا أستطيع أن أعدك أنه المحمولة (أعتقد ذلك), ولكن لا تزال مثيرة للاهتمام من وجهة النظر التقنية لأنه يدل على حالة واحدة حيث في C++, "هذا" المؤشر "عنوان" التحركات من الفئة الأساسية إلى ورثت الدرجة.

نصائح أخرى

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

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct():MY_STRUCT() { }
};

لهذا العمل ، ينبغي أن يكون هناك أي مستخدم أعلن منشئ في الفئة الأساسية, كما في المثال الخاص بك.

لا سيئة memset من أجل ذلك.ذلك لا يضمن memset يعمل في التعليمات البرمجية الخاصة بك, على الرغم من أنه ينبغي أن العمل في الممارسة العملية.

أفضل بكثير من memset ، يمكنك استخدام هذا قليل الحيلة بدلا من ذلك:

MY_STRUCT foo = { 0 };

هذا سوف تهيئة جميع الأعضاء 0 (أو القيمة الافتراضية iirc) لا تحتاج إلى تحديد قيمة لكل.

هذا يجعلني أشعر أكثر أمنا كما يجب أن تعمل حتى لو كان هناك vtable (أو المترجم تصرخ).

memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

أنا متأكد من الحل الخاص بك سوف تعمل لكن لا شك أن هناك أي ضمانات أن تكون عند خلط memset والطبقات.

هذا هو مثال ممتاز على ترقية C لغة C++ (و السبب في أنه قد لا تعمل دائما...)

المشكلة سيكون لديك مع استخدام memset في C++, البنية و فئة هي بالضبط نفس الشيء إلا أنه بشكل افتراضي ، البنية الرؤية العامة وفئة خاصة الرؤية.

وبالتالي ما إذا كان لاحقا على بعض جيدا معنى مبرمج التغييرات MY_STRUCT مثل ذلك:


struct MY_STRUCT
{
    int n1;
    int n2;

   // Provide a default implementation...
   virtual int add() {return n1 + n2;}  
};

مضيفا أن وظيفة واحدة ، memset الآن قد يسبب فسادا.هناك مناقشة تفصيلية في comp.لانغ.c+

الأمثلة يكون "غير محدد السلوك".

غير جراب ، الأمر الذي مترجم يحدد كائن (جميع دروس قواعد و أعضاء) غير محدد (ISO C++ 10/3).النظر في ما يلي:

struct A {
  int i;
};

class B : public A {       // 'B' is not a POD
public:
  B ();

private:
  int j;
};

هذا يمكن أن تكون وضعت النحو التالي:

[ int i ][ int j ]

أو كما:

[ int j ][ int i ]

ولذلك باستخدام memset مباشرة على عنوان 'هذا' كثيرا غير محدد السلوك.واحدة من الإجابات أعلاه ، للوهلة الأولى يبدو أن يكون أكثر أمانا:

 memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

غير أنني أعتقد أنه بالمعنى الدقيق للكلمة هذا أيضا النتائج في السلوك غير محدد.لا أستطيع أن أجد المعيارية النص ، ومع ذلك المذكرة في 10/5 يقول:"قاعدة الطبقة subobject قد يكون هناك تخطيط (3.7) مختلفة من تخطيط معظم اشتقاق كائن من نفس النوع".

ونتيجة لذلك, أنا المترجم يمكن أن تؤدي الفضاء الأمثل مع مختلف الأعضاء:

struct A {
  char c1;
};

struct B {
  char c2;
  char c3;
  char c4;
  int i;
};

class C : public A, public B
{
public:
  C () 
  :  c1 (10);
  {
    memset(static_cast<B*>(this), 0, sizeof(B));      
  }
};

يمكن أن تكون وضعت النحو التالي:

[ char c1 ] [ char c2, char c3, char c4, int i ]

على 32 بت ، alighments.... الخل 'B', sizeof(ب) من المرجح أن 8 بايت.ومع ذلك, sizeof(ج) يمكن أيضا أن تكون '8' بايت إذا كان المترجم حزم بيانات الأعضاء.وبالتالي فإن الدعوة إلى memset قد الكتابة فوق القيمة المعطاة 'c1'.

تخطيط دقيق من فئة أو بنية غير مضمونة في C++, وهذا هو السبب يجب أن لا تجعل الافتراضات حول حجم من الخارج (هذا يعني أنه إذا كنت غير مترجم).

ربما يعمل, حتى تجد المترجم الذي لا, أو رمي بعض vtable إلى المزيج.

إذا كان لديك بالفعل منشئ, لماذا لا مجرد تهيئة هناك مع n1=0;n2=0;-- هذا هو بالتأكيد أكثر عادي الطريقة.

تحرير:في الواقع, كما paercebal أظهرت المنشئ التهيئة بل هو أفضل.

رأيي هو لا.لست متأكدا ما المكاسب أيضا.

كما تعريفك CMyStruct التغييرات إضافة/حذف أعضاء هذا يمكن أن يؤدي إلى الخلل.بسهولة.

إنشاء منشئ CMyStruct أن يأخذ MyStruct معلمة.

CMyStruct::CMyStruct(MyStruct &)

أو شيء من هذا البحث.ثم يمكنك تهيئة العامة أو الخاصة 'MyStruct' الأعضاء.

من ISO C++ نظر ، هناك مسألتين:

(1) هو كائن جراب ؟ في اختصار لتقف على القديم عادي البيانات القياسية يعدد ما لا يمكن أن يكون في جراب (ويكيبيديا ملخص جيد).إذا لم يكن جراب لا يمكنك memset ذلك.

(2) هناك أعضاء كل بت-صفر هو غير صالح ؟ على ويندوز ويونيكس ، مؤشر فارغة هو كل بت الصفر ؛ فإنه لا يلزم أن يكون.النقطة العائمة 0 كل بت صفر في IEEE754 ، وهو أمر شائع جدا ، على x86.

فرانك Kruegers نصيحة عناوين المخاوف الخاصة بك عن طريق تقييد memset إلى جراب قاعدة من غير جراب الدرجة.

جرب هذا الزائد جديدة.

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

struct MY_STRUCT
{
    int n1;
    int n2;
};

class CMyStruct : public MY_STRUCT
{
public:
    CMyStruct()
    {
        // whatever
    }
    void* new(size_t size)
    {
        // dangerous
        return memset(malloc(size),0,size);
        // better
        if (void *p = malloc(size))
        {
            return (memset(p, 0, size));
        }
        else
        {
            throw bad_alloc();
        }
    }
    void delete(void *p, size_t size)
    {
        free(p);
    }

};

إذا MY_STRUCT هو رمز و كنت سعيدا باستخدام برنامج التحويل البرمجي C++, يمكنك وضع منشئ هناك دون التفاف في الصف:

struct MY_STRUCT
{
  int n1;
  int n2;
  MY_STRUCT(): n1(0), n2(0) {}
};

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

التعليق على litb الجواب (يبدو أنني لم يسمح حتى الآن عن التعليق مباشرة):

حتى مع هذا لطيفة C++-أسلوب حل عليك أن تكون حذرا جدا ان كنت لا تطبق هذا بسذاجة إلى البنية التي تحتوي على غير جراب الأعضاء.

بعض المجمعين ثم لا تهيئة بشكل صحيح بعد الآن.

انظر هذا جواب سؤال مشابه.شخصيا كان لي تجربة سيئة على VC2008 إضافية std::string.

ما أقوم به هو استخدام الكلي التهيئة ، ولكن فقط تحديد المهيآت أعضاء يهمني, e.g:

STARTUPINFO si = {
    sizeof si,      /*cb*/
    0,              /*lpReserved*/
    0,              /*lpDesktop*/
    "my window"     /*lpTitle*/
};

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

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

أفترض هيكل يتم توفيرها لك ولا يمكن تعديلها.إذا كان يمكنك تغيير هيكل ، ثم الحل الواضح هو إضافة منشئ.

لا أكثر من مهندس التعليمات البرمجية الخاصة بك مع C++ مغلفة عند كل ما تريد هو ماكرو بسيطة التهيئة الهيكل الخاص بك.

#include <stdio.h>

#define MY_STRUCT(x) MY_STRUCT x = {0}

struct MY_STRUCT
{
    int n1;
    int n2;
};

int main(int argc, char *argv[])
{
    MY_STRUCT(s);

    printf("n1(%d),n2(%d)\n", s.n1, s.n2);

    return 0;
}

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

template <typename STR>
class CStructWrapper
{
private:
    STR MyStruct;

public:
    CStructWrapper() { STR temp = {}; MyStruct = temp;}
    CStructWrapper(const STR &myStruct) : MyStruct(myStruct) {}

    operator STR &() { return MyStruct; }
    operator const STR &() const { return MyStruct; }

    STR *GetPointer() { return &MyStruct; }
};

CStructWrapper<MY_STRUCT> myStruct;
CStructWrapper<ANOTHER_STRUCT> anotherStruct;

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

struct MY_STRUCT2
{
    int n1;
    std::string s1;
};

CStructWrapper<MY_STRUCT2> myStruct2; // n1 is set to 0, s1 is set to "";

على الجانب السلبي, انها أبطأ منذ كنت صنع إضافية نسخة مؤقتة, وسوف مترجم تعيين كل عضو 0 حدة بدلا من واحد memset.

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