لماذا يرفع المُنشئ المحمي خطأ هذا الرمز؟
-
25-09-2019 - |
سؤال
سؤال واحد حول مُنشئ محمي. تعلمت أنه يمكن استخدام المُنشئ المحمي في الفصل المشتق. إلى أي حال ، وجدت الرمز أدناه لديه خطأ. لماذا يحدث مثل هذا؟
class A
{
protected:
A(){}
};
class B: public A {
public:
B() {
A* f=new A(); // Why it is not working here
}
};
المحلول
هذا لا علاقة له بالمشاركات على وجه التحديد. هذا هو كيف protected
أعمال الوصول.
الطريقة protected
يعمل محدد الوصول ، فهو يسمح للفئة المشتقة B
للوصول إلى محتويات كائن من الفئة الأساسية A
فقط عندما يكون هذا الكائن من الفصل A
هو كائن فرعي للطبقة B
. هذا يعني أن الشيء الوحيد الذي يمكنك القيام به في الكود الخاص بك هو الوصول إلى محتويات A
عبر B
: يمكنك الوصول إلى أعضاء A
من خلال مؤشر من النوع B *
(أو مرجع من النوع B &
). لكنك لا تستطيع الوصول إلى نفس الأعضاء من خلال مؤشر من النوع A *
(أو المرجع A &
).
النظر في المثال التالي
class A {
protected:
int i;
};
class B : A {
void foo() {
i = 0; // OK
this->i = 0; // OK
B *pb = this;
pb->i = 0; // OK
A *pa = this;
pa->i = 0; // ERROR
((A *) this)->i = 0; // ERROR
}
};
في ما سبق B::foo
, ، يمكنك الوصول إلى عضو الأساس A::i
باستخدام سهل فقط i
بناء الجملة. هذا يعادل استخدام this->i
بناء الجملة. كلاهما سيعمل ، لأن المؤشر this
لديه النوع B *
, ، أي أنك تصل A::i
شامل مؤشر من النوع B *
. هذا هو بالضبط ما protected
من المفترض أن يسمح محدد الوصول. الوصول من خلال pb
المؤشر يعمل لنفس السبب.
ومع ذلك ، عند "تحويل" this
مؤشر للكتابة A *
, ، لم يعد بإمكانك الوصول A::i
من خلال هذا المؤشر الجديد ، على الرغم من أنك لا تزال تحاول الوصول إلى نفس العضو كما كان من قبل.
عند تطبيقها على المُنشئين ، protected
يحتوي محدد الوصول على تأثير محدد للغاية: لا يمكن استخدام مُنشئ محمي إلا لتهيئة المشروعات الفرعية من الفئة الأساسية. لا يمكن استخدامه لتهيئة الكائنات المستقلة (وهو ما كنت تحاول القيام به). بمعنى آخر ، فإن المُنشئين المحميين هم طريقة أخرى لتنفيذ مفهوم فئة مجردة في C ++ (جنبا إلى جنب مع الأساليب الافتراضية النقية). إذا كانت مُنشئو فصلك محميين ، فسيكون فصلك بفعالية نبذة مختصرة. لا يمكنك استخدامه لتحديد الكائنات المستقلة "من الخارج". (بالطبع ، لا ينطبق ما ورد أعلاه داخل الأصدقاء ، وكذلك داخل الفصل نفسه).
نصائح أخرى
عندما يكون لدى الفئة الأساسية مُنشئًا محميًا ، لا يمكنك إنشاء إنشاء الفصل مباشرة. ولكن يمكنك القيام بذلك لاستدعاء المُنشئ من مُنشئ الفئة الأساسية:
class A {
protected:
A() {}
};
class B: public A {
public:
B() : A() // allowed to access constructor like this
{
A* f = new A(); // Not allowed to access constructor like this!
}
};
تمنحك مكالمة مباشرة إلى المُنشئ كما هو موضح أدناه الخطأ التالي مع الإصدار 4.1.2:
A* f = new A(); // Not allowed to access constructor like this!
test.cpp:4: error: A::A() is protected
ومع ذلك ، فإنك هذه الدعوة إلى المنشئ لا تعطي أي أخطاء:
B() : A() // allowed to access constructor like this
والسبب وراء ذلك هو أن المكالمة الثانية تصل إلى مُنشئ A () من خلال الميراث ، وهو أمر مسموح به. ومع ذلك ، فإن هذا يحاول إنشاء مثيل جديد من A () عن طريق استدعاء المُنشئ مباشرة:
A* f = new A(); // Not allowed to access constructor like this!
قد يبدو هذا غير بديهي ، حيث يجب أن يكون B قادرًا على الوصول إلى مُنشئ A لأن B يرث من A. ومع ذلك ، إذا أعلنت منشئًا محمي في C ++ ، لا يمكنك إنشاء مثيل لتلك الفئة إلا من خلال الميراث أو علاقة صديق.
اسمحوا لي أن أضع إجابتي في الخطوات:
1) لا يتم الوراثة من البنائين ولهذا السبب في الطبقة المشتقة ، لا يمكن أن يكونوا أكثر من ذلك.
2) يتم استدعاء البنائين ولا يسمى.
3) إذا كنت قد أعلنت وظيفة بسيطة في print void محمية () ثم حاولت الاتصال بها في B ، لكان قد نجحت. هذا يحدث BCOZ ، B قد ورث هذه الوظيفة.
4) عندما تفعل شيئًا مثل هذا B: A () ، فأنت تستدعي المُنشئ وهذا مسموح به.
5) حاول جعل ب فئة صديق من A ثم قم بالتشغيل ومعرفة ما إذا كان يعمل.
أتمنى أن يساعدك هذا.
كان لدي نفس السؤال مثل هذا ، و هذا الرابط اجعلني واضحا.
cppreference يقول مثل هذا:
Protected members form the interface for the derived classes (which is
distinct from the public interface of the class).
A protected member of a class Base can only be accessed
1) by the members and friends of Base
2) by the members and friends (until C++17) of any class derived from Base, but only when operating on an object of a type that is derived from Base (including this)