2014-11-17 3 views
56

Я уверен, что понимаю общую разницу между повышением и понижением, особенно на C++. Я понимаю, что мы не всегда можем опуститься, потому что приведение указателя базового класса к указателю производного класса предполагает, что объект базового класса, на который указывают, имеет все члены производного класса.Когда анонимный переход на C++?

В начале семестра, мой профессор сказал классу, что иногда также запрещено подниматься на C++, но, похоже, я пропустил причину, почему в моих заметках, и я не могу вспомнить, когда это происходит.

Когда это незаконно для повышения в C++?

+0

Я не уверен, но, возможно, Что делать с фактом, что класс может наследовать от нескольких классов, поэтому «родительские кланы» не уникальны? –

+0

@IvanKuckir: в листинге вы всегда указываете тип назначения. Поэтому не имеет значения, что вы можете наследовать несколько классов. Компиляция просто проверяет, является ли тип назначения среди базовых классов. Что ** ** является проблемой, когда вы косвенно наследуете один и тот же тип через два разных пути. – MSalters

ответ

48

Если «незаконный» означает плохо образованный, то он является незаконным, если базовый класс недоступен или неоднозначен.

  • Это недоступно, если, например, базовый класс является закрытым.

    class A {}; 
    class B : A {}; 
    ... 
    B b; 
    A *pa = &b; // ERROR: base class is inaccessible 
    

    Обратите внимание, что даже в C++ 11 С-стиль броска может «пробить» защиту доступа и выполнить формально правильный вентиляционный

    A *pa = (A *) &b; // OK, not a `reinterpret_cast`, but a valid upcast 
    

    Такое использование следует избегать, конечно.

  • Это неоднозначно, если ваш тип источника содержит несколько базовых подобъектов целевого типа (через множественное наследование).

    class A {}; 
    class B : public A {}; 
    class C : public A {}; 
    class D : public B, public C {}; 
    
    D d; 
    A *pa = &d; // ERROR: base class is ambiguous 
    

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

    B* pb = &d; 
    A* pa = pb; // OK: points to 'D::B::A' subobject 
    
+5

Почему его следует «избегать любой ценой», и почему это «конечно»? –

+7

@ Планы гонок на орбите: Безусловно, это хак, который побеждает очень фундаментальные принципы языка. Когда база недоступна, авторам языка пришлось столкнуться с жестким выбором: либо сделать его «reinterpret_cast» (что создаст потенциал для тонких и практически необнаружимых ошибок, если изменения в базовом доступе) или сделает его защищающим «static_cast» '(это то, что они выбрали). Оба варианта ужасны. – AnT

+3

ИМО, «любой ценой», переоценивает ситуацию. Да, этого следует избегать, как правило, но есть значительно хуже, поэтому, если дело доходит до выбора, некоторые издержки могут быть приемлемыми. –

17

Если базовый класс неоднозначен (унаследован два или более раз по разным путям), то вы можете сделать ’ t up up за один шаг.

Если базовый класс недоступен, то единственным способом ускорения является использование стиля C. Это особый случай этого актера, он единственный, кто может справиться с этой задачей. По существу он тогда ведет себя как static_cast, который не ограничен доступностью.


Стандартные.

C++ 11 §5.4/4:

& hellip; в [С литой] выполнением в следующих случаях преобразование является действительным, даже если базовым класс недоступен в static_cast:

  • указатель на объект производного типа класса или именующий или RValue производного типа класса может быть явно преобразован в указатель или ссылку на однозначный тип базового класса, соответственно;
  • указатель на член производного класса может быть явно преобразован в указатель на член класса нечеткого типа виртуального базового класса ;
  • указатель на объект однозначного типа не виртуального базового класса, glvalue однозначного тип не виртуального базового класса или указатель на элемент однозначного типа не виртуального базового класса может быть явно преобразован в указатель, ссылку или указатель на элемент типа производного класса, соответственно.

Пример неоднозначности:

struct Base {}; 
struct M1: Base {}; 
struct M2: Base {}; 
struct Derived: M1, M2 {}; 

auto main() -> int 
{ 
    Derived d; 
    //static_cast<Base&>(d);      //! Ambiguous 
    static_cast<Base&>(static_cast<M2&>(d)); // OK 
} 

Пример недоступного основания, с (обычно) регулировки адреса в гипсе:

struct Base { int value; Base(int x): value(x) {} }; 

class Derived 
    : private Base 
{ 
public: 
    virtual ~Derived() {}  // Just to involve an address adjustment. 
    Derived(): Base(42) {} 
}; 

#include <iostream> 
using namespace std; 

auto main() -> int 
{ 
    Derived d; 
    Base& b = (Base&) d; 
    cout << "Derived at " << &d << ", base at " << &b << endl; 
    cout << b.value << endl; 
}; 
+1

+1 Специально для второго примера. Хотя я бы удалил по существу, так как вы даете точное определение броска для недоступных баз. – Deduplicator

+0

@Deduplicator: Спасибо за этот комментарий. «По существу» - это ласковое слово, охватывающее два случая. (1) Обозначение * cast * (C cast) может добавить 'const_cast'. (2) В случае, когда указанный тип не является базовым классом, а не производным или тем же классом, он с радостью выполнит 'reinterpret_cast' (возможно, также с добавлением' const_cast') в качестве шага в направлении бедствия , Не уверен, что я должен включить такие детали. –

+0

Не в тему, но зачем вы писали 'auto main() -> int' вместо' int main() '? Я не понимаю, почему вы должны использовать синтаксис автоматического вычитания типа, когда тип возврата явно указывается в любом случае (и не полагается на какой-либо параметр 'decltype' параметров функции). Просто пытаюсь понять, есть ли у вас тонкое понимание, которое у меня есть. – Cornstalks

10

Есть два случаи, когда восходящий поток плохо сформирован в C++ (dia gnosed во время компиляции):

  1. базового класса в вопросе не доступен:

    class base {}; 
    class derived : base {}; 
    
    int main() { 
        derived x; 
        base& y = x; // invalid because not accessible. 
        // Solution: C-style cast (as static_cast without access-check) 
        base& y1 = (base&)x; 
    } 
    
  2. Базовый класс суб-объектом является не однозначна:

    class base {}; 
    struct A1 : base {}; 
    struct A2 : base {}; 
    
    struct derived : A1, A2 {}; 
    int main() { 
        derived x; 
        base& y = x; // invalid because ambiguous. 
        // Solution 1, scope resolution: 
        base& y1 = static_cast<A1::base&>(x); 
        base& y2 = static_cast<A2::base&>(x); 
        // Solution 2, intermediate unambiguous steps: 
        A1& a1 = x; 
        A2& a2 = x; 
        base& ya1 = a1; 
        base& ya2 = a2; 
    } 
    
Смежные вопросы