Использование класса в заголовочном файле без доступа к его определению? - PullRequest
1 голос
/ 16 декабря 2010

Это выдержка из Руководства Google по кодированию на c ++ .

Как мы можем использовать класс Foo в заголовке файл без доступа к его определению?

  • Мы можем объявить элементы данных типа Foo * или Foo &.
  • Мы можем объявлять (но не определять) функции с аргументами и / или возвращаемые значения, типа Foo. (Один Исключение составляют аргументы Foo или const Foo & имеет неявное, конструктор с одним аргументом, в котором В случае, если нам нужно полное определение поддержка автоматического преобразования типов.)
  • Мы можем объявить статические члены-члены типа Foo. Это потому что статический члены данных определены вне определение класса.

Что меня интересует, так это исключение во второй пуле. Почему это так? Зачем нужно полное определение, если мы хотим поддерживать автоматическое преобразование типов?

Я предполагаю, что компилятору необходимо полное определение типа назначения из-за временного объекта, который создается при неявном преобразовании. Я правильно угадываю? Есть что-то еще?

EDIT:

На мой взгляд, исключение в руководстве относится к такой ситуации:

class A
{
    public:
        A( int );
};

class B
{
    public:
        B( A const &a );
};

int main()
{
    B b(2);
}

Здесь у нас есть только одно пользовательское неявное преобразование (из int в A) и вызов конструктора, который принимает A const &. Единственное, что имеет смысл в этом исключении, - это поддерживать прямое преобразование, например, из int в A, а затем в B через конструктор, который принимает A const &, позволяя клиентскому коду использовать эту цепочку преобразования без необходимости явно включать заголовочный файл, где объявлен класс A.

Ответы [ 3 ]

2 голосов
/ 16 декабря 2010

Язык C ++ не различает код в заголовочных файлах и других файлах.Он даже не требует, чтобы заголовок был файлом.Так что чисто технически вопрос бессмыслен, но на практике вы ограничиваете то, что делаете в заголовочных файлах, чтобы не противоречить правилу единого определения.Не ограничивая себя, пользователи должны быть осторожны, чтобы включить заголовочный файл только в одну единицу перевода.С надлежащими ограничениями заголовочный файл может быть свободно включен в несколько единиц перевода.

неполный тип - это тот, где размер неизвестен, где sizeof не может использоваться.

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

Это означает, что вы не можете делать вещи, для которых требуется знать размер.А поскольку незавершенность означает, что члены не известны (они обязательно будут известны, если бы размер был известен), вы, как правило, не можете вызывать членов.Исключение: вы можете вызвать деструктор, как в delete pFoo, и компилятор должен это принять, но это неопределенное поведение, если класс Foo имеет нетривиальный деструктор.

исключение, отмеченное в правилах Google, однако, не имеет смысла.

РЕДАКТИРОВАТЬ : я обнаружил, что людям на SO нравится больше, когда все подробно описано, поэтому добавлю обсуждение почему указание не имеет смысла.

Указание говорит, что вы можете "объявить (но не определить)", но что "одно исключение - если аргумент Foo или const Foo & имеет неявный, однозначныйКонструктор аргумента ".

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

#include <iostream>

struct Foo;

Foo bar( Foo const& );  // Declaration of function bar, works fine.

struct Foo
{
    int x_;
    Foo( int x ): x_( x ) {}       // Converting constructor.
};

int main()
{
    std::cout << bar( 42 ).x_ << std::endl;
}

Foo bar( Foo const& foo ) { return foo; }

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

Приветствия & hth.,

0 голосов
/ 16 декабря 2010

Я не знаю, верно ли исключение во втором пункте.Неявные преобразования должны быть известны только при вызове функции, а не при ее объявлении, поэтому следующее работает, даже если C не завершено, а f объявлено:

#include <iostream>
class C;
void f(C);
struct C { C(int i) { std::cout << "C(" << i << ")" << std::endl; } };
void f(C c) { std::cout << "f(C)" << std::endl; }
int main() { f(2); }
0 голосов
/ 16 декабря 2010

Предположим, что foo.h знает только о Foo объявлении

//foo.h

class Foo;
void f(const Foo &); // It is possible to use the reference.

Полное определение в foo.cpp

// foo.cpp

class CanBeConvertedToFoo;
class Foo
{
   Foo (const CanBeConvertedToFoo & x); // implicit constructor
}

class CanBeConvertedToFoo неявно преобразуется в Foo;Но это неизвестно в some.cpp.

// some.cpp

#include "foo.h"
void g(const CanBeConvertedToFoo & x) {
   f(x); // Is it known about implicit conversion ?
}
...