Как этот код запрещает наследование? - PullRequest
3 голосов
/ 26 августа 2011

Я нашел довольно странный код:

class Base {
public:
    virtual bool IsDerived() const { return false; }
};

class Derived : public Base {
public:
    bool IsDerived() const { return true; }
};

Derived* CastToDerived( Base* base )
{
    // private and protected inheritance from Derived is prohibited
    Derived* derived = dynamic_cast<Derived*>(base);
    if( derived == 0 ) {
       assert( !base->IsDerived() );
    }
    return derived;
}

Я не понимаю отрывок о частном и защищенном наследовании.

Предположим, я наследую от Derived с protected модификатором:

class FurtherDerived : protected Derived {
};

Что происходит? Как это будет assert срабатывать?

Ответы [ 4 ]

1 голос
/ 26 августа 2011

Если у вас есть Protected или Private Наследование, вы не можете сделать:

Base *ptr = new Derived();

и вы не можете,

Derived *ptr1 = new Derived();
Base *ptr = ptr1;

Это потому, что Base является недоступной базой Derived

Поскольку вы не можете иметь указатель класса Base, указывающий на объект класса Derived, эта проверка выглядит избыточной.


EDIT:
Даже если вы не можете напрямую назначить объект класса Derived указателю класса Base, это может произойти и другими способами, например: если функция класса Derived возвращает указатель класса Base.

Короче говоря, указатель класса Base может указывать на объект класса Derived, даже если деривация равна protected или private.

Учитывая вышеизложенное,

Согласно стандарту C ++:
5.2.7.8:

Проверка времени выполнения логически выполняется следующим образом:
- Если в самом производном объекте, на который указывает (ссылается) v, v указывает (ссылается) на подобъект общедоступного базового класса объекта T, и если только один объект типа T получен из указанного подобъекта (ссылается на v), результатом является указатель (ссылка на lvalue) на этот объект T.
- В противном случае, если v указывает (ссылается) на подобъект общедоступного базового класса самого производного объекта, а тип самого производного объекта имеет базовый класс типа T, который является однозначным и public, результатом является указатель (ссылающийся на lvalue) на подобъект T самого производного объекта .
- В противном случае проверка во время выполнения завершится неудачно.

Обратите внимание, что стандарт специально налагает требование деривации: Public.
Таким образом, dynamic_cast обнаружит обработку приведения как неподходящего приведения, если деривация равна protected или private, и вернет NULL (так как вы используете указатель), и будет вызван assert.

Так что да, код очень правильный. И это действительно делает то, что говорит комментарий


Этот образец демонстрирует, что он работает согласно комментариям:

#include<iostream>
class Base 
{
    public:
        virtual bool IsDerived() const { return false; }
};

class Derived : protected Base 
{
    public:
        bool IsDerived() const { return true; }
        Base* getBase() { return this; }
};

Derived* CastToDerived( Base* base )
{
     // private and protected inheritance from Derived is prohibited
     Derived* derived = dynamic_cast<Derived*>(base);
     if( derived == 0 ) 
     {
         std::cout<< "!base->IsDerived()";
     }
     return derived;
}


int main()
{
    Derived *ptr3 = new Derived();
    Base *ptr = ptr3->getBase();
    Derived *ptr2 = CastToDerived(ptr);
    return 0;
}
0 голосов
/ 26 августа 2011

Я не понимаю отрывок о частном и защищенном наследовании.

Ответ прост. Этот комментарий, как и многие другие комментарии, является комментарием, который никоим образом не описывает код или форму.

Пример:

class PrivateDerived : private Derived {
public:
   Base* cast_to_base () {
      return dynamic_cast<Base*>(this);
   }   
};  

void check_base (const char * id, Base* pbase) {
   if (pbase == 0) {
      std::cout << id << " conversion yields a null pointer.\n";
   }
   else {
      std::cout << id << "->IsDerived() = "
                << pbase->IsDerived() << "\n";
      Derived* pderived = CastToDerived (pbase);
      std::cout << "CastToDerived yields "
                << (pderived ? "non-null" : "null") << " pointer.\n";
      std::cout << "pderived->IsDerived() = "
                << pderived->IsDerived() << "\n\n";
   }
}

int main () {
   PrivateDerived private_derived;

   // Good old c-style cast can convert anything to anything.
   // Maybe a bit disfunctional, but it works in this case.
   check_base ("c_style_cast", (Base*)&private_derived);

   // The cast_to_base method can see the private inheritance,
   // and does so without invoking undefined behavior.
   check_base ("cast_method", private_derived.cast_to_base());

   return 0;
}

Протестировано с несколькими версиями gcc и clang; никто из них не выдвинул это assert заявление.

Добавление
Я подозреваю, что произошло то, что на некоторой конкретной машине с каким-то конкретным компилятором рассматриваемый код каким-то образом сумел сделать то, что, по мнению автора, он должен делать. Я подозреваю, что автор никогда не проверял эту предполагаемую проверку, чтобы проверить, действительно ли она работает так, как рекламируется.

0 голосов
/ 26 августа 2011

dynamic_cast выполняет проверку во время выполнения (5.2.7 / 8).

Проверка во время выполнения завершится неудачей, если Base наследуется как защищенное или частное Derived.

Значением неудачной проверки во время выполнения при приведении к указателю является NULL-указатель (5.2.7 / 9).

Таким образом, код является обходным решением для частных и защищенных потомков: если вы унаследуете Derived с protected или private, dynamic_cast вернет NULL и будет выполнена пользовательская проверка.

0 голосов
/ 26 августа 2011

IsDerived - это виртуальная функция, определенная в базовом классе, и функции разрешаются на основе типа static объекта, с помощью которого вы вызываете функцию. Это означает, что это не проблема. Это будет работать. (А может, я что-то упустил в вашем вопросе).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...