2010-02-19 3 views
27

Я читаю некоторый код C++ и замечаю, что есть «#include» как в файлах заголовков, так и в файлах .cpp. Я думаю, если я переведу все «#include» в файле, скажем, foo.cpp, тоже его «файл заголовка foo.hh и пусть foo.cpp включает только foo.hh, код должен работать в любом случае без учета таких проблем, как недостатки, эффективность и т. д., где «включить» включить в C++

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

ответ

30

Как правило, поместите свои включенные в .cpp-файлы, когда сможете, и только в файлах .h, если это невозможно.

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

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

+1

Ваша интерпретация «шаблона extern» - очень желаемое за действительное. Вы будете разочарованы. –

+0

@nobugz: О? действительно? Goddam it Я надеюсь, что это не так ... скорость компиляции шаблона - одна из худших вещей о C++. Возможность скрывать реализации от тех, кто не хочет их видеть, будет настоящим благом. – jkp

+0

Похоже, что вы сбиваете с толку новый шаблон extern с 'export' (который почти заброшен). http://www.cppreference.com/wiki/keywords/export – luke

5

Вы должны сделать все остальные файлы, включая ваш заголовочный файл, транзитивно, включая все #include s в своем заголовке.

В C++ (как в C) #include обрабатывается препроцессором, просто вставляя весь текст в файл #include d вместо оператора #include. Таким образом, с большим количеством #include s вы можете буквально похвастаться размером вашего скомпилируемого файла сотнями килобайт - и компилятор должен разобрать все это для каждого отдельного файла. Обратите внимание, что один и тот же файл, включенный в разные места, должен быть повторно переписан в каждом месте, где он равен #include d! Это может замедлить компиляцию до сканирования.

Если вам нужно объявить (но не определить) вещи в своем заголовке, используйте forward declaration вместо #include s.

+0

Или, вернее, использовать форвардное объявление вместо '# include' в своих .h-файлах, когда это возможно. Иногда это неизбежно. Например, если класс A имеет элемент данных класса B, тогда A.h должен будет включить B.h. –

1

Если вы #include .cpp-файлы, вы, вероятно, закончите загрузкой ошибок «множественного определения» из компоновщика. Вы можете в теории # включить все в единую единицу перевода, но это также означает, что все нужно перестраивать каждый раз, когда вы вносите изменения в один файл. Для реальных проектов это неприемлемо, поэтому у нас есть компоновщики и инструменты вроде make.

+0

@Neil: нет, если заголовки написаны правильно! – jkp

+0

@jkp.Файлы cpp не являются заголовками – 2010-02-19 15:57:13

+0

@Neil: мой плохой. Да, это не хорошая практика. – jkp

1

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

12

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

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

Включить загрязнение файла, на мой взгляд, является одной из наихудших форм гниения кода.

Редактировать: Heh. Похоже, парсер ест символы> и <.

1

.hh (или .h) файлы должны быть для деклараций.

Файлы

.cpp (или .cc) предназначены для определений и реализаций.

Осознайте, что оператор #include равен буквально. #include "foo.h" буквально копирует содержимое foo.h и вставляет его, когда директива include находится в другом файле.

Идея состоит в том, что некоторые другие файлы bar.cpp и baz.cpp могут захотеть использовать некоторый код, который существует в foo.cc. Обычно это делается для bar.cpp и baz.cpp до #include "foo.h", чтобы получить объявления о функциях или классах, которые они хотели использовать, а затем во время ссылки компоновщик подключил эти функции в строке .cpp и baz.cpp для реализаций в foo.cpp (вот в чем смысл компоновщика).

Если вы положили все в foo.h и попытались сделать это, у вас возникла бы проблема. Скажем, что foo.h объявляет функцию с именем doFoo(). Если определение (код для) этой функции находится в foo.cc, это нормально. Но если код для doFoo() перемещен в foo.h, а затем вы включите foo.h внутри foo.cpp, bar.cpp и baz.cpp, теперь есть три определения для функции с именем doFoo(), и ваш компоновщик будет жаловаться, потому что вам не разрешено иметь более одного предмета с тем же именем в той же области.

0

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

(begin myheader.h) 
#ifndef _myheader_h_ 
#define _myheader_h_ 
struct blah {}; 
extern int whatsit; 
#endif //_myheader_h_ 

Теперь, если вы #include «myheader.h» в других файлах заголовков, это будет получить только один раз включен (из-за _myheader_h_ определяется). Я считаю, что MSVC имеет «#pragma once» с эквивалентной функциональностью.

+0

'#pragma once' является стандартом де-факто к настоящему времени - gcc также поддерживает его, и я считаю, что некоторые другие также делают это. –

1

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

Стандартным примером является #include <vector>. Возвращает вам векторный класс. И плот внутренних заголовочных файлов CRT, которые необходимы для компиляции векторного класса должным образом, что вам действительно не нужно и не нужно знать.

1

В то время как заголовочный файл должен включать только то, что ему нужно, «то, что ему нужно» является более жидким, чем вы могли бы подумать, и зависит от цели, по которой вы помещаете заголовок. Я имею в виду, что некоторые заголовки на самом деле являются документами интерфейса для библиотек или другого кода. В этих случаях заголовки должны включать (и, возможно, #include) все, что потребуется разработчику для правильного использования вашей библиотеки.

Смежные вопросы