2017-01-29 35 views
0

Предположим, у нас есть класс A и B следующим образом:Может ли static_cast выполняться из базового класса в производный класс, если производный класс содержит дополнительные методы и члены?

class A 
{ 
    private: 
    int a; 

    public: 

    void seta(int a_) 
    { 
     a=a_; 
    } 
    int geta() 
    { 
    return a; 
    } 
    }; 

class B: public A 
{ 
    private: 
    int b; 

    public: 

    int getb() 
    { 
     return b; 
    } 

    void setb() 
    { 
    b=geta()+1; 
    } 

}; 

и предположим, что я делаю такой код в функции:

A* a=new A(); 
a->seta(5); 
B* b=static_cast<B*>(a); 
b->setb(); 
cout<<b->getb()<<" and "<<b->geta()<<endl; 

Этот код компилируется и работает, но меня смущает, почему? Если a является указателем на класс A, и при распределении только память для class A членов зарезервирована (во время выполнения), почему после статического приведения кажется, что этот объект фактически является экземпляром class B. Это безопасная операция?

+0

Я подумал, что это неопределенное поведение. И должна ли ваша верхняя строка говорить «A и B»? – Carcigenicate

+0

Я тоже подумал. Но похоже, что это работает, и я не знаю почему? – user3616359

+2

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

ответ

3

[expr.static.cast]/11, курсив мой:

prvalue типа «указатель на CV1B», где B является тип класса, может быть преобразован в prvalue типа «указатель на CV2D », Где D является производным классом (статья 10) от B, если cv2 является такой же cv-квалификацией, как или больше cv-квалификацией, чем, cv1. Если B является виртуальным базовым классом D или базовым классом виртуального базового класса D, или если нет действительного стандартного преобразования из «указателя на D» на «указатель на B» (4.11), программа плохо сформирована , Значение нулевого указателя (4.11) преобразуется в значение нулевого указателя для типа назначения. Если значение prvalue типа «указатель на cv1B» указывает на B, который на самом деле является подобъектом объекта типа D, полученный указатель указывает на объект-объект типа D. В противном случае поведение не определено.

0

Это небезопасная вещь. Следующий код демонстрирует, почему

#include <iostream> 
using namespace std; 

class A 
{ 
private: 
    int a; 

public: 
    void seta(int a_) { 
    a=a_; 
    cout << "seta to: " << a << endl; 
    } 

    int geta() { 
    return a; 
    } 
}; 

class B: public A 
{ 
    private: 
    int b; 

    public: 

    int getb() { 
    return b; 
    } 

    void setb() { 
    b=geta()+1; 
    cout << "setb to: " << b << endl; 
    } 
}; 

int main() { 

    A as[2]; 

    A* a1=as; 
    A* a2=&as[1]; 

    a1->seta(5); 
    a2->seta(4); 

    cout << "a1: " 
     << a1->geta() 
     << endl; 

    cout << "a2: " 
     << a2->geta() 
     << endl; 

    B* b=static_cast<B*>(a1); 

    b->setb(); 

    a2->seta(3); 

    cout << "b->geta(): " 
     << b->geta() 
     <<" and b->getb(): " 
     << b->getb() 
     << endl; 

    size_t sizeofa(sizeof(A)); 
    cout << "sizeofa: " 
     << sizeofa 
     << endl; 

    size_t sizeofb(sizeof(B)); 
    cout << "sizeofb: " 
     << sizeofb 
     << endl; 

} 

Выход

seta to: 5 
seta to: 4 
a1: 5 
a2: 4 
setb to: 6 
seta to: 3 
b->geta(): 5 and b->getb(): 3 
sizeofa: 4 
sizeofb: 8