LINUX.ORG.RU

[C++] *_cast`ы

 


0

1

Друзья, прошу помощи в постижении дзен.

Никак не найду на гугло-просторах ответа на главный вопрос бытия: какой *_cast в C++ нужно применить в конкретный момент.

Сложилось мнение, что dynamic_cast нужно использовать для отсеивания неподходящих потомков, расположенных по указателю на родительский класс; reinterpret_cast - для приведения указателей; static_cast - для всего остального. Но пятая точка вместе с коллегой подсказывают, что я могу ошибаться.

Объясните пожалуйста в каких случаях нужно какие cast`ы использовать и собственно в чем их главное отличие.

Заранее благодарен за обмен опытом (:

★★

не нужно гадать, откройте книгу наконец

s0L
()

dynamic - вниз по иерархии и только с задействованным RTTI. static - произвольный указатель к void* и обратно. reinterpret - указатель к инту и обратно. const - для нейтрализации const, как ни странно.

const86 ★★★★★
()
Ответ на: комментарий от const86

> dynamic - вниз по иерархии и только с задействованным RTTI. static - произвольный указатель к void* и обратно.

dynamic - вверх/вниз по иерархии, static - вверх( если нет виртуального наследования ), в отличие от dynamic - static не имеет лишнего оверхеда в рантайме

anonymous
()
Ответ на: комментарий от const86

const - для нейтрализации const, как ни странно.

И volatile тоже, не забываем.

Pavval ★★★★★
()

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

parent() -> close(); на что компилятор нас послал на ... тогда я предложил создать указатель нашего родительского класса и через динамик каст выцепить сам объект....а мой многоуважаемый коллега не стал слушать и впендюрил туда реинтерпрет каст,после чего программа тоже смогла работать(что совсем не удивительно).мое эго не осталось равнодушным к таким действиям,здесь то и начался спор по поводу вверх кастов.... просим объяснить нам дол...ам что и когда используется...на сколько я понял что динамик каст нужен тогда,когда есть указатель - папа,в котором может находиться любой его наследник,и чтобы его выцепить как раз и нужен динамик...(в программе так и получилось что папа это всемогущий куобджект,а наследник наш клас,тоесть : наш_класс *р=динамик_каст<наш_класс*>( парент() ); р->клоуз(); что изменится если динамик заменить на реинтерирет? и что правильней использовать?

energyclab
()
Ответ на: комментарий от energyclab

без кода тут сложно что-то сказать. К тому же, если вы уже РАБОТАЕТЕ и не знаете что такое касты в С++ и гугл, то срочно читать стандарт и вики.

Касты нужно использовать те, которые сделаны специально для этой задачи. Преобразование детей в родителей делается через dynamic_cast, reinterpret_cast нужен для другого.

former_anonymous ★★★
()
Ответ на: комментарий от energyclab

что правильней использовать?

это. но вообще у вас какая-то беда в архитектуре, и нет культуры технического общения. что за классы? что вам сказал компилятор?

libastral.so.2 во все поля

jtootf ★★★★★
()
Ответ на: комментарий от energyclab

мы пишем интерфейс на куте и в определенный момент в классе - наследнике понадобилось добраться до методов родителя этого класса

И в чем проблема? публичные и защищенные методы родителя доступны в наследнике без всяких кастов. А для доступа к приватным методам вам никакой каст не поможет.

как пологается взяли функцию парент()

Как я понял parent() в QObject возвращает указатель на родительский объект и это не имеет ничего общего с родительским классом.

наш_класс *р=динамик_каст<наш_класс*>( парент() ); р->клоуз(); что изменится если динамик заменить на реинтерирет? и что правильней использовать?

Если parent вернет указатель на класс не являющийся предком вашего класса то dynamic_cast вернет null, а reinterpret_cast вернет какой-то указатель и результат дальнейшей с ним работы будет неопределен.

Vinick ★★
()
Ответ на: комментарий от const86

dynamic - вниз по иерархии и только с задействованным RTTI

т.е. если g++ отдать ключик -no-rtti (или как его там), то dynamic cast совсем работать не будет? Или будет работать в одном направлении? Удивлен, потому что когда-то собирал какие-то либы, которые отдавали этот ключик компилятору и при этом спокойно использовали dynamic_cast'ы (без ифдефов).

anonymous
()
Ответ на: комментарий от energyclab

попытались сделать следующее...

parent() -> close(); на что компилятор нас послал на ... тогда я предложил создать указатель нашего родительского класса и через динамик каст выцепить сам объект....а мой многоуважаемый коллега не стал слушать и впендюрил туда реинтерпрет каст,после чего программа тоже смогла работать(что совсем не удивительно).

ОМГ! Что за контора-то? Чтобы знать, с чьим софтом лучше не связываться...

Покажите чтоли кусок кода, а то непонятно, кто от кого как наследуется и что именно вы хотите сделать.

DELIRIUM ☆☆☆☆☆
()
Ответ на: комментарий от energyclab

Чуваки, вам срочно нужно хоть что-нибудь почитать по с++. Иначе, с нулевыми знаниями, вы не долго протянете.

В данном случае работа через парента - зло (а вдруг забудешь его указать). Лучше всего связка сигнала/слота.

panter_dsd ★★★★
()
Ответ на: комментарий от anonymous

если g++ отдать ключик -no-rtti (или как его там), то dynamic cast совсем работать не будет?

$ cat x.cxx 
struct A { virtual ~A() {} };
struct B: public A {};
B *f(A *a) { return dynamic_cast<B*>(a); }
$ g++ -fno-rtti -c x.cxx 
x.cxx: In function 'B* f(A*)':
x.cxx:3: error: 'dynamic_cast' not permitted with -fno-rtti
$ g++ -c x.cxx && echo $?
0

Вверх по иерархии кастить можно всегда простым присваиванием, для этого не нужно RTTI, а dynamic_cast и static_cast ничего дополнительного в таком случае не сделают.

const86 ★★★★★
()

Если заботитесь о размерах бинарников и скорости работы, лучше обходиться без dynamic_cast и RTTI

annulen ★★★★★
()
Ответ на: комментарий от energyclab

Если parent() — это QObject::parent(), то правильно использовать qobject_cast. Он работает без включённого RTTI и между разделяемыми библиотеками.

Варианты (в зависимости от связности кода):

MyParent * myParent = qobject_cast<MyParent*>( parent() );
Q_ASSERT( myParent );
myParent->close();
if ( MyParent * myParent = qobject_cast<MyParent*>( parent() ) )
{
    myParent->close();
}
Dendy ★★★★★
()

Я где-то читал мнение что их лучше вообще не использовать. Плохо вериться что любую архитектуру можно переписать так, чтобы избавиться от cast'ов.

dynamic_cast нужно использовать для отсеивания неподходящих потомков, расположенных по указателю на родительский класс

Как вариант: в debug коде dynamic_cast, в release — static_cast.

AST-PM-105
()
Ответ на: комментарий от AST-PM-105

>Как вариант: в debug коде dynamic_cast, в release — static_cast.

Бгг. А ты знаешь, что у них разное предназначение? Это всё равно, что в релизной сборке true на false везде заменить.

yoghurt ★★★★★
()
Ответ на: комментарий от yoghurt

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

template <typename To, typename From>
To assert_cast(From p)
{
    assert(dynamic_cast<To>(p));
    return static_cast<To>(p);
}

Конечно когда dynamic_cast используется для проверки возможности приведения, такая функция не подойдет.

AST-PM-105
()
Ответ на: комментарий от energyclab

А не проще сделать так:

class A
{
  void foo();
}


class B: public A
{
    void foo2()
    {
        A::foo();
    }
}

Или я не правильно понял про

в определенный момент в классе - _наследнике_ понадобилось добраться до методов _родителя_

trex6 ★★★★★
()
Ответ на: комментарий от energyclab

как пологается взяли функцию парент() и попытались сделать следующее...

parent() -> close(); на что компилятор нас послал на ...

Для вас есть еще вот такие костыли:

// let's hope there is slot or Q_INVOKABLE "close" method in parent() object
QMetaObject::invokeMethod(parent(), "close");

rival ★★
()
Ответ на: комментарий от yoghurt

> Хотя, смотря с какой стороны смотреть :)

Хорошее замечание. В моей голове корни вверху, то есть вверх - это от дочернего класса к базовому.

const86 ★★★★★
()
Ответ на: комментарий от const86

Благодарю. Наиболее полезный ответ из всех.

Dikar ★★
() автор топика
Ответ на: комментарий от panter_dsd

Полностью прочитана, но не до конца освоено книга Дейтлов.

Dikar ★★
() автор топика
Ответ на: комментарий от DELIRIUM

Даю гарантию 99.9% что вы не свяжетесь с нашей конторой и даже о ней никогда не услышите если я вам сейчас не скажу её название.

Dikar ★★
() автор топика
Ответ на: комментарий от Dendy

у нас классы очень большие...И делать так как вы предложили нам влом.. поясняю суть

class A {
   virtual A* parent();  // вернет 0 так как у него отца нету
};

class B : public A {  // при создании в нем указывается папа А
   void close() //
   virtual A* parent();  // вернет объект А, присвоенный указателю А*
};

class C : public B {  //при создании в нем указывается папа В
   virtual A* parent();  // вернет объект В, присвоенный указателю А*
   void doSomeThing();
};

void C :: doSomeThing() {
  // надо добраться до метода close() класса B
  В *papaB = dynamic_cast<B*>( parent() );
  if( papaB != 0 )
   papaB -> close()
}

Разве это не правильно??? На сколько я знаю для этого и применяется dynamic

energyclab
()
Ответ на: комментарий от trex6

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

svu ★★★★★
()
Ответ на: комментарий от svu

Если речь идет о Qt, то наследник понимается не в терминах C++, а в терминах иерархии виджетов. В этом случае без dynamic_cast'а не обойтись. reinterpret_cast работать не должен. если он сработал, то нам повезло.

Reset ★★★★★
()
Ответ на: комментарий от energyclab
class Parent : public QObject {
public:
    Parent(QObject *parent = 0) : QObject(parent) {}
    void die() { deleteLater(); }
};


class Child : public QObject {
public:
    Child(Parent *parent) : QObject(parent) {}
    Parent* parent() { return static_cast<Parent*>(QObject::parent()); }
    void killParent() { parent()->die(); }
};

В данной ситуации static_cast'а вполне хватит.

anonymous
()
Ответ на: комментарий от Reset

> В этом случае без dynamic_cast'а не обойтись. reinterpret_cast работать не должен. если он сработал, то нам повезло.

Даже если мы наверняка знаем класс объекта?

anonymous
()
Ответ на: комментарий от AST-PM-105

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

У нас в проекте есть как раз такая функция.

Pavval ★★★★★
()
Ответ на: комментарий от Reset

Хороший язык. Настолько мощная ОО, что иерархию виджетов пришлось реализовывать отдельно от иерархии объектов?

Или имеется в виду иерархия виджетов конкретного окна?

svu ★★★★★
()
Ответ на: комментарий от svu

Или имеется в виду иерархия виджетов конкретного окна?

именно

Reset ★★★★★
()
Ответ на: комментарий от const86

Функция называется «CMN_STATIC_CAST». Юзется там, где обычно юзают static_cast, только дает доп проверки. При чем тут вирт. наследовние?

Pavval ★★★★★
()
Ответ на: комментарий от anonymous

>> В этом случае без dynamic_cast'а не обойтись. reinterpret_cast работать не должен. если он сработал, то нам повезло.

Даже если мы наверняка знаем класс объекта?

Как раз этот вопрос и является интересным

Dikar ★★
() автор топика
Ответ на: комментарий от Dikar

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

Класс_родитель :: метод()

А dynamic_cast необходим при полиморфной обработке( например если есть массив указателей базового класса, по которым находятся различные производные классы )

energyclab
()
Ответ на: комментарий от panter_dsd

>Чуваки, вам срочно нужно хоть что-нибудь почитать по с++

Чуваки, вам срочно нужно хоть что-нибудь почитать по архитектуре.
fix

ps И возвращайтесь на Дельфи.

malbolge ★★
()
Ответ на: комментарий от energyclab

>А dynamic_cast

А *_cast

А вообще это дурной тон.
Как минимум в проекте имеется в наличии хромоногая декомпозиция всего.

Хотя, если это суппорт чего-то древнего, то...

malbolge ★★
()

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

dynamic_cast - то же самое что и static_cast, но только в случае фактического приведения к неверному типу(если объект не того типа в который его пытаются привести) - возвращается ноль, работает в рантайме, а значит более медленный чем остальные, но и наиболее умный.

reinterpret_cast - приводит объект любого типа, в любой другой тип, делает простую замену типов.

const_cast(ахтунг) - снимает квалификатор const и volatile.

Си каст - пытается использовать static_cast, reinterpret_cast и static_cast, по мере необходимости.

Есть интересные нюансы при использовании множественного наследования. reinterpret_cast-у на это абсолютно положить прибором - он тупо заменяет один тип другим. static_cast приведёт корректно(с учётом внутренних смещений), но не в случае виртуального наследования. dynamic_cast - единственный кто приведёт корректно даже в случае множественного виртуального наследования.

Как-то так.

Booster ★★
()
Ответ на: комментарий от malbolge

1. Если ума много, то можно было указать названия/авторов нормальных книг по архитектуре.

2. Интересно откуда такой гений знает как должен писать программист на Delphi? Возможно сам грешен?

Dikar ★★
() автор топика
Ответ на: комментарий от Booster

Спасибо, еще один адекватный и полезный комментарий.

Dikar ★★
() автор топика
Ответ на: комментарий от Dikar

Все просто, чтобы не использовать *касты, потребуется хотя бы раз нормально прочесть книжку Б4 (GoF). Но мерило любой теории - практика и эксперименты. Поэтому предлагаю почитать даже не GoF (если самому практикой заняться лень) ну... Ну, хотя бы влиссидеса.
И, после прочтения, тебя (необязательно) посетит дао касательно токена.
;)

malbolge ★★
()
Ответ на: комментарий от Reset

>Если речь идет о Qt, то наследник понимается не в терминах C++, а в терминах иерархии виджетов. В этом случае без dynamic_cast'а не обойтись.

Учи Qt и не используй dynamic_cast там, где хватит qobject_cast

annulen ★★★★★
()
Ответ на: комментарий от Dikar

qobject_cast это тот же dynamic_cast, только использующий не RTTI информацию, а свою. В общем те же яйца, только в профиль. Единственно удобство которое я вижу в этом случае, то что класс не обязан быть полиморфным, то есть ему не нужно содержать ни одного виртуального метода, но для qobject по-моему это не актуально. :)

Booster ★★
()
Ответ на: комментарий от Booster

Да, для QObject все что не полиморфно неактуально :D

Dikar ★★
() автор топика
Ответ на: комментарий от Booster

>Единственно удобство которое я вижу в этом случае, то что класс не обязан быть полиморфным

ОНО НЕ ТРЕБУЕТ RTTI

И еще работает быстрее, так как заточено под линейную иерархию

annulen ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.