Стоит ли полагаться на другой заголовок для заголовков, которые он включает? - PullRequest
3 голосов
/ 23 декабря 2009

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

#include "that.h"
#include "there.h"

class Foo {
 protected:
  // Functions that do stuff with varOne and varTwo
 private:
  that varOne; 
  there varTwo;
 ...
};

Тогда в классах, которые наследуются от foo (и, следовательно, включают foo.h), не могли бы вы также включить это и это? Обычно я включаю все, что нужно классу, независимо от того, получит ли он их от другого. Это избыточно?

Ответы [ 6 ]

3 голосов
/ 23 декабря 2009

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

хиджра:

#ifndef __A_H
#define __A_H
// whatever
#endif

b.h:

#ifndef __B_H
#define __B_H
#include "a.h"
// whatever
#endif

c.cpp:

#include "a.h"
#include "b.h"
//whatever

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

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

Если вы используете #pragma once в своих заголовках, есть большая вероятность, что осведомленный компилятор кеширует имена файлов на более высоком уровне и вообще игнорирует второе включение a.h. Это тоже может ускорить сборку.

1 голос
/ 23 декабря 2009

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

1 голос
/ 23 декабря 2009

По моему опыту, лучше отправлять туда объявления и в Foo.h, а не включать их вообще, поскольку они не являются технически необходимыми, если они передаются по ссылке.

В этом кадре Foo.h будет выглядеть так:

class that;
class there;

class Foo
{
    public:

        void func( that &param ) = 0;
        void funcTwo( there &param ) = 0;
        ...
};
0 голосов
/ 23 декабря 2009

Затем в классах, которые наследуются от foo (и, следовательно, включает foo.h), будет Вы также потрудитесь, включая это и это? * * 1002

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

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

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

0 голосов
/ 23 декабря 2009

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

.h файл должен включать только то, что используется в заголовке. Например, ему не нужен stdio, если в заголовке нет объявлений io, вместо этого он должен помещаться в файл src, только если он использует printf, например.

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

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

редактировать: более конкретный пример inc1.h

#ifndef INC1_H
#define INC1_H
inc1_struct {                                                            
   int x;
};
#endif

inc2.h

#ifndef INC2_H
#define INC2_H
#include "inc1.h"
void inc2_f();
struct inc1_struct *inc2_callinc1();
#endif 

prog.cpp #include "inc1.h" #include "inc2.h"
#включают #include

void inc2_f() {
   printf("inc2_f\n");
}

struct inc1_struct *inc2_callinc1() {
   return (struct inc1_struct*) malloc(sizeof(struct inc1_struct));
}

int main(int argc, char **argv) {
   struct inc1_struct *s = inc2_callinc1();
   return 0;
}

Это скомпилируется, но допустим, вы не хотите выставлять

struct inc1_struct *inc2_callinc1();

в inc2.h

Тогда вам не нужно будет включать inc1.h в файл inc2.h, но если вы не хотите также удалять определение inc2_callinc1 () в prog.cpp, вам нужно включить inc1. ч.

0 голосов
/ 23 декабря 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...