static_cast сомнение - PullRequest
       6

static_cast сомнение

1 голос
/ 10 января 2011
#include <iostream>
class A
{
public:
    A()
    {
        std::cout << "\n A_Constructor \t" << this <<std::endl;
    }
    void A_Method()
    {
        std::cout <<"\n A_Method \t" << this <<std::endl;
    }
};
class B:public A
{
public:
    B()
    {
        std::cout <<"\n B_Constructor \n";
    }
    void B_Method()
    {
        std::cout <<"\n B_Method \t" << this <<std::endl;
    }
};

int main()
{
    A *a_obj = new A;
    B *b_obj = static_cast<B*> (a_obj);  // This isn't safe.
    b_obj->B_Method();      
    getchar();
    return 0;
}

ВЫХОД:

A_Constructor 001C4890
B_Method 001C4890

Поскольку проверка времени выполнения не участвует в преобразовании типов, static_castне безопасноНо в этом примере я получил то, чего даже не ожидал.Так как нет вызова B::B(), любой из его участников не должен быть в состоянии вызвать b_obj.Несмотря на это, я получил вывод.

В этом простом случае я мог бы преуспеть, хотя это известно небезопасно.Мои сомнения:

  • Хотя вызова B::B() нет, как я смог получить доступ к class B функциям-членам.
  • Кто-нибудь может привести пример, гдеэто небезопасно и может привести к ошибкам (хотя то, что я приводил ранее, может послужить плохим примером, но даже лучше).

Я сделал это в Visual Studio 2010 и с \ Wall опция установлена.

Ответы [ 3 ]

4 голосов
/ 10 января 2011

Это неопределенное поведение.Иногда UB вызывает сбои.Иногда кажется, что «работает».Вы правы в том, что не должны этого делать, хотя в этом случае произошло меньше плохих вещей.

0 голосов
/ 10 января 2011

Я сомневаюсь, что этот конкретный случай UB. Во-первых, он просто приводит указатель от одного типа к другому. Поскольку здесь не задействовано виртуальное / множественное наследование, настройка указателя не выполняется, поэтому значение указателя остается неизменным. Конечно, это указывает на объект неправильного типа, но кого это волнует, если у нас нет доступа к членам B, даже если они есть? И даже если бы была задействована настройка указателя, все равно было бы хорошо, если бы мы не обращались к какой-либо памяти, указанной им, чего у нас нет.

Затем в примере вызывается метод B. Поскольку он не является виртуальным, это просто обычный вызов функции со скрытым аргументом this (представьте себе B::B_Method(this)). Теперь this указывает на объект неправильного типа, но опять же, кого это волнует? Единственное, что он делает, - это печатает его, что всегда безопасно.

На самом деле, вы даже можете вызывать методы, используя нулевой указатель. Он работает до тех пор, пока метод не является виртуальным и не пытается получить доступ к чему-либо, указанному this. Когда-то у меня была библиотека, используемая многими программами. Эта библиотека имела синглтоноподобный класс, который должен был быть создан явно, но на самом деле ни одна из программ не делала этого. Указатель экземпляра был инициализирован в NULL по умолчанию, поскольку он был глобальным. Это работало отлично, так как у класса не было членов данных вообще. Затем я добавил некоторые из них, и все программы неожиданно начали зависать. Когда я узнал причину, у меня был действительно хороший смех. Я использовал объект, который даже не существовал.

0 голосов
/ 10 января 2011

То, что вы пытаетесь, это неопределенное поведение, чтобы НИЧЕГО могло произойти. Кажется, это работает (и, вероятно, работает) хорошо, поскольку вы не пытаетесь получить доступ к каким-либо элементам данных (у вас их нет). Попробуйте добавить некоторые элементы данных в оба класса и получить к ним доступ из методов, вы увидите, что поведение изменится на совершенно непредсказуемое.

...