dynamic_cast и static_cast в C ++

Когда у меня была та же проблема, я остановил свой сервер mongo и снова начал его с помощью команды

mongod --repair

Перед запуском операции восстановления вы должны проверить, достаточно ли свободного места на вашем жестком диске (мин - размер вашей базы данных)

142
задан dbush 18 May 2016 в 17:21
поделиться

7 ответов

Вот сводка по static_cast<> и dynamic_cast<>, в частности, по указателям. Это просто обзор 101 уровня, он не охватывает всех тонкостей.

static_cast< Type* >(ptr)

Здесь берется указатель в ptr и делается попытка безопасно привести его к указателю типа Type*. Приведение выполняется во время компиляции. Приведение будет выполнено только в том случае, если типы связаны. Если типы не связаны, вы получите ошибку компилятора. Например:

class B {};
class D : public B {};
class X {};

int main()
{
  D* d = new D;
  B* b = static_cast<B*>(d); // this works
  X* x = static_cast<X*>(d); // ERROR - Won't compile
  return 0;
}

dynamic_cast< Type* >(ptr)

Это снова попытка взять указатель в ptr и безопасно привести его к указателю типа Type*. Но это приведение выполняется во время выполнения, а не во время компиляции. Поскольку это приведение выполняется во время выполнения, оно полезно, особенно в сочетании с полиморфными классами. Фактически, в некоторых случаях классы должны быть полиморфными, чтобы приведение было законным.

Приведение может происходить в одном из двух направлений: от базового к производному (B2D) или от производного к базовому (D2B). Достаточно просто увидеть, как касты D2B будут работать во время выполнения. Либо ptr является производным от Type, либо нет. В случае с D2B dynamic_cast<>s правила просты. Вы можете попытаться привести что угодно к чему угодно другому, и если ptr действительно был производным от Type, вы получите указатель Type* обратно от dynamic_cast. В противном случае вы получите указатель NULL.

Но касты B2D немного сложнее. Рассмотрим следующий код:

#include <iostream>
using namespace std;

class Base
{
public:
    virtual void DoIt() = 0;    // pure virtual
    virtual ~Base() {};
};

class Foo : public Base
{
public:
    virtual void DoIt() { cout << "Foo"; }; 
    void FooIt() { cout << "Fooing It..."; }
};

class Bar : public Base
{
public :
    virtual void DoIt() { cout << "Bar"; }
    void BarIt() { cout << "baring It..."; }
};

Base* CreateRandom()
{
    if( (rand()%2) == 0 )
        return new Foo;
    else
        return new Bar;
}


int main()
{
    for( int n = 0; n < 10; ++n )
    {
        Base* base = CreateRandom();

            base->DoIt();

        Bar* bar = (Bar*)base;
        bar->BarIt();
    }
  return 0;
}

main() не может сказать, какой объект вернет CreateRandom(), поэтому приведение в стиле C Bar* bar = (Bar*)base; явно не безопасно для типов. Как это можно исправить? Один из способов - добавить в базовый класс функцию типа bool AreYouABar() const = 0; и возвращать true из Bar и false из Foo. Но есть и другой способ: используйте dynamic_cast<>:

int main()
{
    for( int n = 0; n < 10; ++n )
    {
        Base* base = CreateRandom();

        base->DoIt();

        Bar* bar = dynamic_cast<Bar*>(base);
        Foo* foo = dynamic_cast<Foo*>(base);
        if( bar )
            bar->BarIt();
        if( foo )
            foo->FooIt();
    }
  return 0;

}

Приведение выполняется во время выполнения и работает путем запроса объекта (пока не нужно беспокоиться о том, как), спрашивая его, тот ли это тип, который мы ищем. Если да, то dynamic_cast возвращает указатель; в противном случае возвращается NULL.

Для того, чтобы это приведение от базового типа к производному работало с использованием dynamic_cast<>, Base, Foo и Bar должны быть тем, что в стандарте называется полиморфными типами. Чтобы быть полиморфным типом, ваш класс должен иметь хотя бы одну виртуальную функцию. Если ваши классы не являются полиморфными типами, то использование dynamic_cast не будет компилироваться. Пример:

class Base {};
class Der : public Base {};


int main()
{
    Base* base = new Der;
    Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile

    return 0;
}

Добавление виртуальной функции к base, например, виртуального dtor, сделает и Base, и Der полиморфными типами:

class Base 
{
public:
    virtual ~Base(){};
};
class Der : public Base {};


int main()
{
    Base* base = new Der;
    Der* der = dynamic_cast<Der*>(base); // OK

    return 0;
}
262
ответ дан 23 November 2019 в 22:25
поделиться

Нижеследующее не совсем близко к тому, что вы получаете от dynamic_cast C ++ с точки зрения проверки типов, но, возможно, это поможет вам понять его цель немного лучше:

struct Animal // Would be a base class in C++
{
    enum Type { Dog, Cat };
    Type type;
};

Animal * make_dog()
{
   Animal * dog = new Animal;
   dog->type = Animal::Dog;
   return dog;
}
Animal * make_cat()
{
   Animal * cat = new Animal;
   cat->type = Animal::Cat;
   return cat;
}

Animal * dyn_cast(AnimalType type, Animal * animal)
{
    if(animal->type == type)
        return animal;
    return 0;
}

void bark(Animal * dog)
{
    assert(dog->type == Animal::Dog);

    // make "dog" bark
}

int main()
{
    Animal * animal;
    if(rand() % 2)
        animal = make_dog();
    else
        animal = make_cat();

    // At this point we have no idea what kind of animal we have
    // so we use dyn_cast to see if it's a dog

    if(dyn_cast(Animal::Dog, animal))
    {
        bark(animal); // we are sure the call is safe
    }

    delete animal;
}
4
ответ дан 23 November 2019 в 22:25
поделиться

Больше, чем код на C, я думаю, что определение на английском языке могло бы достаточно:

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

class Base { virtual ~Base() {} };
class Derived : public Base {};
class Derived2 : public Base {};
class ReDerived : public Derived {};

void test( Base & base )
{
   dynamic_cast<Derived&>(base);
}

int main() {
   Base b;
   Derived d;
   Derived2 d2;
   ReDerived rd;

   test( b );   // throw: b is not a Derived object
   test( d );   // ok
   test( d2 );  // throw: d2 is not a Derived object
   test( rd );  // ok: rd is a ReDerived, and thus a derived object
}

В этом примере вызов test связывает различные объекты со ссылкой на Base . Внутренне ссылка с понижением к ссылке на Derived типизированным способом: приведение к понижению будет успешным только в тех случаях, когда объект, на который указывает ссылка, действительно является экземпляром Derived ].

10
ответ дан 23 November 2019 в 22:25
поделиться

Вот ответ инженера поддержки Nokia, имя удалено для защиты частной жизни:

Привет,

Спасибо за предложение. У меня есть передал его нашим разработчикам, которые рассмотрит возможность его реализации для будущего выпуска. Вы можете отслеживать его с помощью следующая ссылка:

https://bugreports.qt.io/browse/QTVSADDINBUG-27

С уважением, Инженер по поддержке, Рамки, Nokia >

Первоначальный вопрос

У нас есть довольно много устаревших проектов vcproj, которые мы хотим добавить к нему Qt. В настоящее время у нас нет простого способа преобразования этих устаревших vcproj файлы в vcproj с поддержкой Qt.

Что я имею в виду под Qt-enabled?

На не-Qt-enabled vcproj, если щелкнуть правой кнопкой мыши на имени проекта в обозревателе решений все параметры, связанные с Qt, выделены серым цветом.

Было бы очень полезно, если Nokia добавит эту возможность в надстройку VSTD.

Стипендиаты Stackoverflow, если вы хотите, чтобы эта функция была реализована Nokia, пожалуйста, помогите проголосовать за нее по ссылке выше! =)

-121--1490416-

Интересно, как эти люди попали в компанию в первую очередь:

Этим разработчикам не только не хватает навыков с концепциями программирования, но как правило, способность формулировать решение проблемы в коде.

Простые вещи, такие как запись циклов жесткие для них...

Вам, несомненно, нужно тратить больше времени и усилий на набор рабочей силы, как гласит старая поговорка: поспешность растрачивает деньги.

Теперь, когда вы находитесь в такой ситуации, как вы описываете, заканчивайте свой отчет (как другие намекнули), сделайте его кратким и подчеркивайте, сколько денег это стоит компании, подавайте и ждите лучшего (как вы сказали, у вас «нет возможности наказать человека»).

-121--811911-

dynamic _ cast выполняет проверку типа с использованием RTTI . Если он не будет выполнен, он вызовет исключение (если вы дали ему ссылку) или NULL, если вы дали ему указатель.

3
ответ дан 23 November 2019 в 22:25
поделиться

В языке C нет классов, поэтому написать dynamic_cast на этом языке невозможно. Структуры языка C не имеют методов (как следствие, у них нет виртуальных методов), поэтому в них нет ничего "динамического".

1
ответ дан 23 November 2019 в 22:25
поделиться

Нет, непросто. Компилятор присваивает уникальный идентификатор каждому классу, на эту информацию ссылается каждый экземпляр объекта, и именно это проверяется во время выполнения, чтобы определить, допустимо ли динамическое приведение. Вы можете создать стандартный базовый класс с этой информацией и операторами для выполнения проверки во время выполнения для этого базового класса, тогда любой производный класс будет информировать базовый класс о своем месте в иерархии классов, и любые экземпляры этих классов будут транслироваться во время выполнения через ваши операции.

править

Вот реализация, демонстрирующая один метод. Я не утверждаю, что компилятор использует что-то подобное, но я думаю, что он демонстрирует концепции:

class SafeCastableBase
{
public:
    typedef long TypeID;
    static TypeID s_nextTypeID;
    static TypeID GetNextTypeID()
    {
        return s_nextTypeID++;
    }
    static TypeID GetTypeID()
    {
        return 0;
    }
    virtual bool CanCastTo(TypeID id)
    {
        if (GetTypeID() != id) { return false; }
        return true;
    }
    template <class Target>
    static Target *SafeCast(SafeCastableBase *pSource)
    {
        if (pSource->CanCastTo(Target::GetTypeID()))
        {
            return (Target*)pSource;
        }
        return NULL;
    }
};
SafeCastableBase::TypeID SafeCastableBase::s_nextTypeID = 1;

class TypeIDInitializer
{
public:
    TypeIDInitializer(SafeCastableBase::TypeID *pTypeID)
    {
        *pTypeID = SafeCastableBase::GetNextTypeID();
    }
};

class ChildCastable : public SafeCastableBase
{
public:
    static TypeID s_typeID;
    static TypeID GetTypeID()
    {
        return s_typeID;
    }
    virtual bool CanCastTo(TypeID id)
    {
        if (GetTypeID() != id) { return SafeCastableBase::CanCastTo(id); }
        return true;
    }
};
SafeCastableBase::TypeID ChildCastable::s_typeID;

TypeIDInitializer ChildCastableInitializer(&ChildCastable::s_typeID);

class PeerChildCastable : public SafeCastableBase
{
public:
    static TypeID s_typeID;
    static TypeID GetTypeID()
    {
        return s_typeID;
    }
    virtual bool CanCastTo(TypeID id)
    {
        if (GetTypeID() != id) { return SafeCastableBase::CanCastTo(id); }
        return true;
    }
};
SafeCastableBase::TypeID PeerChildCastable::s_typeID;

TypeIDInitializer PeerChildCastableInitializer(&PeerChildCastable::s_typeID);

int _tmain(int argc, _TCHAR* argv[])
{
    ChildCastable *pChild = new ChildCastable();
    SafeCastableBase *pBase = new SafeCastableBase();
    PeerChildCastable *pPeerChild = new PeerChildCastable();
    ChildCastable *pSameChild = SafeCastableBase::SafeCast<ChildCastable>(pChild);
    SafeCastableBase *pBaseToChild = SafeCastableBase::SafeCast<SafeCastableBase>(pChild);
    ChildCastable *pNullDownCast = SafeCastableBase::SafeCast<ChildCastable>(pBase);
    SafeCastableBase *pBaseToPeerChild = SafeCastableBase::SafeCast<SafeCastableBase>(pPeerChild);
    ChildCastable *pNullCrossCast = SafeCastableBase::SafeCast<ChildCastable>(pPeerChild);
    return 0;
}
1
ответ дан 23 November 2019 в 22:25
поделиться

Если только вы не реализуете свой собственный RTTI с ручной обкаткой (в обход системного), невозможно реализовать dynamic_cast непосредственно в коде пользовательского уровня C++. dynamic_cast очень сильно привязан к системе RTTI реализации C++.

Но чтобы лучше понять RTTI (и, соответственно, dynamic_cast), вам следует ознакомиться с заголовком и оператором typeid. Он возвращает информацию о типе, соответствующую объекту, который у вас под рукой, и вы можете запрашивать различные (ограниченные) вещи у этих объектов информации о типе.

20
ответ дан 23 November 2019 в 22:25
поделиться
Другие вопросы по тегам:

Похожие вопросы: