Опасный бросок? - PullRequest
       8

Опасный бросок?

1 голос
/ 08 декабря 2011

Учтите это:

#include <iostream>
using namespace std;
class A{
   protected:
       void some_function(int params)
       {
        //inside A: do something A related 
       }
};

class B: public A{
public:
    void call_some_function(int params)
    {
    some_function(params); // Simple call to A::some_function and NOTHING MORE.
    }
};


int main(int argc, char *argv[])
{
   A* a = new A();

   ((B*)(a))->call_some_function(20); // Is it OK ?
}

Этот код работает Какая опасность его использования?

Ответы [ 7 ]

6 голосов
/ 08 декабря 2011

В C и C ++ обычно неопределенное поведение (читается как «незаконно, но разрешено компилятором») для разыменования объекта одного типа через указатель или ссылку на объект другого типа (с несколькими исключениями, такими как доступ через указатель на базовый класс).Это называется правилом строгого алиасинга.

Подробнее здесь: Что такое правило строгого алиасинга?

Ваш код нарушает это правило, получая доступобъект типа A через указатель на тип B.


Обратите внимание, что обычно компилятор не может проверить ваши статические приведенные значения (в вашем случае C-приведение эквивалентно static_cast).Если вы не уверены в типе объекта, dynamic_cast, чтобы проверить, является ли приведение допустимым во время выполнения, в отличие от static_cast, который проверяется только во время компиляции и допускает некоторые неправильные приведения.

1 голос
/ 08 декабря 2011

На самом деле код только появляется для работы.Вы вызвали неопределенное поведение, сказав компилятору, что у вас есть B, когда у вас есть только A (родительский класс).Он может сломаться в любое время, даже с одним и тем же компилятором.

1 голос
/ 08 декабря 2011

Это неопределенное поведение, так что может случиться что угодно. На практике вы может сойти с рук, если только одно наследство, но в целом на это нельзя рассчитывать; реализация отладки может, например, сгенерируйте код в B::call_some_function, чтобы убедиться, что адрес, переданный как this, соответствует объекту типа B, который на самом деле существует в программе.

1 голос
/ 08 декабря 2011

Одна опасность состоит в том, что call_some_function может вызывать функции (или обращаться к членам в целом) только в A.Любой доступ к члену B приведет к доступу за пределами выделенной памяти, что может иметь катастрофические последствия.

1 голос
/ 08 декабря 2011

Возможно - зависит от того, как компилятор организует память для объектов.

C ++ - это язык, который дает более чем достаточно верёвки, чтобы повеситься с кем-либо в комнате!

Моя точка зрения заключается в том, что вы должны использовать приведение как можно меньше и только в редких случаях.

1 голос
/ 08 декабря 2011

Опасность заключается в том, что имея указатель на A, вы обычно не уверены, является ли он B или C экземпляром производного класса.

0 голосов
/ 08 декабря 2011

Деньги в сценарии, подобном этому (и я уверен, что еще немного):

class B: public A{
public:

    int local_var;

    void call_some_function(int params)
    {
        local_var = 5; // IN HERE.
        some_function(params); // Simple call to A::some_function and NOTHING MORE.
    }
};

В этом случае он попытается получить доступ к неопределенному var (local_var).

Это как:

struct A {
int i1;
int i2;
}

struct B {
int i1;
int i2;
int i3;
}

A a;
(B)a.i1 = 2;

это будет работать (скорее всего), но это плохая практика и в основном неопределенный код.

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