Вопрос:

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

c++ inheritance forward-declaration

31507 просмотра

4 ответа

16614 Репутация автора

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

class Foo;

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

class Bar {};

class Foo: public Bar;

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

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

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

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

Автор: anon Источник Размещён: 29.01.2010 01:52

Ответы (4)


41 плюса

33862 Репутация автора

Решение

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

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

Автор: Joe Размещён: 29.01.2010 01:56

40 плюса

29079 Репутация автора

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

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

Автор: Jonathan M Davis Размещён: 29.01.2010 02:06

-1 плюса

32855 Репутация автора

Я не думаю, что это полезно. Учтите: вы определили класс, 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();
}
Автор: Andrew Aylett Размещён: 02.02.2010 11:22

19 плюса

345 Репутация автора

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

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

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

Автор: user1332054 Размещён: 13.04.2012 05:15
Вопросы из категории :
32x32