Подтвердить что ты не робот

В С++ можно ли переслать объявить класс как наследующий из другого класса?

Я знаю, что могу сделать:

class Foo;

но я могу переслать объявление как наследование от другого, например:

class Bar {};

class Foo: public Bar;

Примером использования может служить вариант совместного использования ссылок.

// somewhere.h
class RA {}
class RB : public RA {}

... и затем в другом заголовке, который не включает где-нибудь. h

// other.h
class RA;

class A {
 public:
  virtual RA* Foo();  // this only needs the forward deceleration
}

class RB : public RA; // invalid but...

class B {
 public:
  virtual RB* Foo();  // 
}

Единственной информацией, которую должен обработать компилятор для объявления объявления RB* B:Foo(), является то, что RB имеет RA как общедоступный базовый класс. Теперь, очевидно, вам понадобится где-нибудь. H, если вы намереваетесь делать какие-либо разыменования возвращаемых значений из Foo. Однако, если некоторые клиенты никогда не звонят Foo, тогда нет причин для их включения где-нибудь. H, что может значительно ускорить компиляцию.

4b9b3361

Ответ 1

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

Таким образом, вы не можете пересылать объявить Bar в любом сценарии, где вы затем используете его, чтобы помочь объявить Foo, и в нем нет смысла иметь декларацию forward, которая включает базовый класс - что делает скажите вам, кроме всего прочего?

Ответ 2

Переменные объявления - это объявления, а не определения. Итак, все, что требует объявления класса (например, указатели на этот класс), нуждается только в прямом объявлении. Однако все, что требовало бы определения, то есть должно было бы знать фактическую структуру класса, не будет работать только с прямым объявлением.

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

Ответ 3

Нет переадресации объявления наследования, даже если вы имеете дело только с указателями. Когда речь идет о преобразованиях между указателями, иногда компилятор должен знать детали класса, чтобы сделать преобразование правильно. Это касается множественного наследования. (В частном случае можно выделить некоторые части частей иерархии, которые используют только одно наследование, но это не является частью языка.)

Рассмотрим следующий тривиальный случай:

#include <stdio.h>
class A { int x; };
class B { int y; };
class C: public A, public B { int z; };
void main()
{ 
    C c; A *pa = &c; B *pb = &c; C *pc = &c; 
    printf("A: %p, B: %p, C: %p\n", pa, pb, pc);
}

Полученный результат (с использованием 32-битной визуальной студии 2010):

A: 0018F748, B: 0018F74C, C: 0018F748

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

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

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

class RA;
class A             { public: virtual RA *fooRet(); };
class RB;
class B : public A  { public: virtual RB *fooRet(); };

Но это будет:

class RA;
class A             { public: virtual RA *fooRet(); };
class RA { int x; };
class RB : public RA{ int y; };
class B : public A  { public: virtual RB *fooRet(); };

Это полезно, если у вас есть объекты типа B (не указатели или ссылки). В этом случае компилятор достаточно умен, чтобы использовать прямой вызов функции, и вы можете использовать возвращаемый тип RB * напрямую без кастования. В этом случае, как правило, я иду вперед и делаю возвращаемый тип RA * и выполняю статическое действие на возвращаемом значении.

Ответ 4

Я не считаю это полезным. Рассмотрим: вы определили класс, Bar:

class Bar {
public:
    void frob();
};

Теперь вы объявляете класс Foo:

class Foo;

Все, что вы можете сделать с помощью Foo, - это создать указатель на него. Теперь предположим, что вы добавили информацию о том, что Foo получен из Bar:

class Foo: public Bar;

Что вы теперь можете сделать, чего не могли сделать раньше? Я думаю, что все, что вы можете сделать, это принять указатель на Foo и перевести его в указатель на Bar, а затем использовать этот указатель.

void frob(Foo* f) {
    Bar *b = (Bar)f;
    b->frob();
}

Однако вы должны были сгенерировать указатель в другом месте, так что вы могли бы просто принять указатель на Bar.

void frob(Bar* b) {
    b->frob();
}