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


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

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 что может значительно ускорить компиляцию.

4 52

4 ответа:

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

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

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

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

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

рассмотрим следующий простой дело:

#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-разрядную visual studio 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 * и делаю статическое приведение к возвращаемому значению.

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

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();
}

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

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