لماذا لا يمكن الوصول إلى عضو الفئة الفائقة المحمية في وظيفة الفئة الفرعية عند تمريرها كوسيطة؟
-
23-09-2019 - |
سؤال
أحصل على خطأ في التجميع ، وأنا مرتبك قليلاً. هذا على VS2003.
خطأ C2248: 'a :: y': لا يمكن الوصول إلى العضو المحمي المعلن في الفصل "أ"
class A
{
public:
A() : x(0), y(0) {}
protected:
int x;
int y;
};
class B : public A
{
public:
B() : A(), z(0) {}
B(const A& item) : A(), z(1) { x = item.y;}
private:
int z;
};
المشكلة هي مع x = item.y ؛
يتم تحديد الوصول على أنه محمي. لماذا لا يستطيع مُنشئ الفئة ب الوصول إلى :: y؟
المحلول
هذا بسبب هذا:
class base_class
{
protected:
virtual void foo() { std::cout << "base::foo()" << std::endl; }
};
class A : public base_class
{
protected:
virtual void foo() { std::cout << "A::foo()" << std::endl; }
};
class B : public base_class
{
protected:
virtual void foo() { std::cout << "B::foo()" << std::endl; }
public:
void bar(base_class *b) { b->foo(); }
};
إذا كان ذلك قانونيًا ، فيمكنك القيام بذلك:
A a;
B b;
b.bar(&a);
وستتصل protected
عضو من A من B ، وهو أمر غير مسموح به.
نصائح أخرى
توضح الإجابات الأخرى المنطق وراء منعك B
الكائن من الوصول إلى الأجزاء المحمية من A
في مثالك ، على الرغم من B
'هو' A
. بالطبع ، أسهل طريقة لإصلاح هذه المشكلة هي جعل أجزاء من A you want access to
عامة أو لديها أساليب ملحق يمكن الوصول إليها للجمهور.
ومع ذلك ، قد تقرر أن هذا غير مناسب (أو قد لا يكون لديك سيطرة على تعريف A
). فيما يلي بعض الاقتراحات للسماح لك بالتنقل حول المشكلة ، بترتيب متزايد من الخضوع A
التحكم في الوصول. لاحظ أن كل هذه الحلول تفترض ذلك class A
يمكن نسخه.
في الحالة الأولى ، يمكنك ببساطة استخدام منشئ النسخ لـ A
لإعداد حالة أولية لهذا الجزء من B
كائن ، ثم قم بإصلاحه بعد ذلك:
class B1 : public A
{
public:
B1() : A(), z(0) {}
B1(const A& item) : A(item), z(1) {
// fix up the A sub-object that was copy constructed
// not quite the way we wanted
x = y;
y = 0;
}
private:
int z;
};
أجد ذلك مربكًا بشكل لا يصدق وربما عرضة للخطأ (على افتراض أننا نريد A
كائن فرعي في B
الكائن ليكون مختلفًا عن A
الكائن الذي يتم نقله إلى المُنشئ - وهو موقف غير عادي ، ولكن ما تم تقديمه في المشكلة). ومع ذلك ، فإن حقيقة أنه يمكن القيام به يعطي بعض التبرير للأمثلة الأكثر إثارة التي تتبع ...
المثال التالي يخلق مؤقتًا B
الكائن الذي يحتوي على نسخ دقيق من A
كائن نريد الوصول إليه. يمكننا بعد ذلك استخدام المؤقتة B
كائن للوصول إلى العناصر التي كانت محمية:
class B2 : public A
{
public:
B2() : A(), z(0) {}
B2(const A& item) : A(), z(1) {
// create a special-use B2 object that can get to the
// parts of the A object we want access to
B2 tmp( item, internal_use_only);
x = tmp.y; // OK since tmp is of type B
}
private:
int z;
// create a type that only B2 can use as a
// 'marker' to call a special constructor
// whose only purpose in life is to create
// a B object with an exact copy of another
// A sub-object in it
enum internal_use {
internal_use_only
};
B2( const A& item, internal_use marker) : A(item), z(0) {};
};
أجد أن هذا الحل أقل إرباكًا قليلاً من الأول ، لكنه لا يزال مربكًا (في رأيي). إن وجود نسخة من الكائن B من B Comple فقط للوصول إلى أجزاء الكائن الذي نريده أمر غريب.
يمكننا أن نفعل شيئًا حيال ذلك من خلال إنشاء وكيل خاص A
الكائنات التي تمنح الوصول الذي نريده. لاحظ أن هذا هو الحل البديل "الأكثر إثارة" لأنه شيء يمكن أن يفعله أي فئة للوصول إلى الأجزاء المحمية من A
, ، حتى لو لم تكن فئات فرعية A
أنفسهم. في حالة B
الفصل ، هناك بعض الشرعية للوصول إلى الأجزاء المحمية من A
الكائنات ، منذ ذلك الحين B
هو A
, ، وكما رأينا بالفعل ، هناك حلول تتيح لنا الوصول إلى حقوق فقط class B
بالفعل ، لذلك أنا أعتبر هذا نسخة أنظف من تلك الحلول في class B
حالة.
class B3 : public A
{
public:
B3() : A(), z(0) {}
B3(const A& item) : A(), z(1) {
// a special proxy for A objects that lets us
// get to the parts of A we're interested in
A_proxy tmp( item);
x = tmp.get_y();
}
private:
int z;
class A_proxy : public A
{
public:
A_proxy( const A& other) : A(other) {};
int get_x() {return x;};
int get_y() {return y;};
};
};
تلخص وثائق IBM بشكل أفضل:
يمكن الوصول إلى عضو فئة قاعدة محمية غير مستقر من قبل الأعضاء والأصدقاء في أي فصول مستمدة من تلك الفئة الأساسية باستخدام أحد ما يلي:
- مؤشر إلى فئة مشتقة بشكل مباشر أو غير مباشر
- إشارة إلى فئة مشتقة بشكل مباشر أو غير مباشر
- كائن فئة مشتقة بشكل مباشر أو غير مباشر
وبالتالي ، باستخدام مثالك أعلاه كأساس:
B::B(const A& item) : A(), z(1) {
// NOT OK because `item` is not a reference to the derived class B
//int i = item.y;
// OK because `item` reinterpreted as a reference to the derived class B
// Do not do this (bad!) -- for illustrative purposes only
int i = reinterpret_cast< const B& >(item).y;
// OK because it is equivalent to `this->x = i`,
// where `this` is a pointer to the derived class B
x = i;
}