Заголовочные файлы C ++, разделение кода

Я новичок в C ++, и у меня возникло несколько общих вопросов о разделении кода. В настоящее время я создал небольшое приложение в одном файле. Сейчас я хочу преобразовать это в отдельные файлы, чтобы они содержали похожий код или еще много чего. Мой настоящий вопрос прямо сейчас: как мне научиться разделять вещи? На каком невидимом поле код должен быть разделен?

Кроме того, в чем смысл файлов заголовков? Пересылать объявленные методы и классы, чтобы я мог использовать их в своем коде до того, как они будут включены компоновщиком во время компиляции?

Было бы здорово получить любое представление о методах или передовых методах, спасибо!


person Community    schedule 11.11.2008    source источник


Ответы (4)


Заголовочные файлы должны содержать объявления классов и функций.

Исходные файлы содержат определения классов и функций.

Стандартная практика (то есть для облегчения чтения) - иметь одно объявление для каждого файла заголовка и одно определение для каждого исходного файла, хотя для небольших (более простых для чтения) объектов вы иногда группируете их со связанными более существенными объектами.

Пример: меню класса

Menu.h:     Contains the Menu declaration.
Menu.cpp:   Contains the Menu definition.

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

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

person Martin York    schedule 11.11.2008
comment
Обратите внимание, что struct Menu {int bar; void foo (); }; является определением класса Menu, но объявлением функции-члена foo, но определением панели-члена объекта. ваш ответ звучит так, как будто это было объявление класса Menu - person Johannes Schaub - litb; 11.11.2008
comment
Кроме того, файлы заголовков часто содержат определения функций. Например: небольшие функции-члены, предназначенные для встраивания; бесплатные функции объявлены статическими встроенными; шаблоны функций. - person Steve Jessop; 11.11.2008
comment
Это курс для начинающих о том, как вставлять код между соответствующими файлами. - person Martin York; 11.11.2008
comment
В этом случае я позволю вам отказаться от определений функций, но даже начальный курс должен четко понимать разницу между объявлением класса и определением класса, и эти файлы заголовков часто содержат определения классов. - person Steve Jessop; 12.11.2008

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

Бюро переводов

Единица трансляции - это текущий компилируемый код и весь код, включенный в него прямо или косвенно. Одна единица перевода переводится в один файл .o / .obj.

Программа

Это все ваши файлы .o / .obj, связанные вместе в один двоичный файл, который может быть выполнен для формирования процесса.

Каковы основные моменты использования разных единиц перевода?

  1. Уменьшите зависимости, чтобы при изменении одного метода одного класса вам не приходилось перекомпилировать весь код вашей программы, а только затронутую единицу трансляции. An
  2. Уменьшите возможные конфликты имен, используя локальные имена единиц перевода, которые не видны другим единицам перевода при их связывании.

Как теперь разделить код на разные единицы перевода? Ответа на этот вопрос не существует, но вы должны учитывать это в каждом конкретном случае. Часто это ясно, поскольку у вас разные классы, которые можно и нужно помещать в разные единицы перевода:

foo.hpp:

/* Only declaration of class foo we define below. Note that a declaration
 * is not a definition. But a definition is always also a declaration */
class foo;

/* definition of a class foo. the same class definition can appear 
   in multiple translation units provided that each definition is the same  
   basicially, but only once per translation unit. This too is called the  
   "One Definition Rule" (ODR). */
class foo {
    /* declaration of a member function doit */
    void doit();

    /* definition of an data-member age */
    int age;
};

Объявите несколько бесплатных функций и объектов:

/* if you have translation unit non-local (with so-called extern linkage)  
   names, you declare them here, so other translation units can include  
   your file "foo.hpp" and use them. */
void getTheAnswer();

/* to avoid that the following is a definition of a object, you put "extern"  
   in front of it. */
extern int answerCheat;

foo.cpp:

/* include the header of it */
#include "foo.hpp"

/* definition of the member function doit */
void foo::doit() {
    /* ... */
}

/* definition of a translation unit local name. preferred way in c++. */
namespace {
    void help() {
        /* ... */
    }
}

void getTheAnswer() {
    /* let's call our helper function */
    help();
    /* ... */
}

/* define answerCheat. non-const objects are translation unit nonlocal  
   by default */
int answerCheat = 42;

bar.hpp:

/* so, this is the same as above, just with other classes/files... */
class bar {
public:
    bar(); /* constructor */
}; 

bar.cpp:

/* we need the foo.hpp file, which declares getTheAnswer() */
#include "foo.hpp"
#include "bar.hpp"

bar::bar() {
    /* make use of getTheAnswer() */
    getTheAnswer();
}

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

static void help() { 
    /* .... */
}

В ODR также говорится, что у вас не может быть более одного определения любого объекта или не встроенной функции в одной программе (классы - это типы, а не объекты, поэтому это не относится к ним). Таким образом, вы должны следить за тем, чтобы не помещать в заголовки не встроенные функции или не помещать такие объекты, как "int foo;" в заголовках. Это вызовет ошибки компоновщика тогда, когда компоновщик попытается связать единицы перевода, включая эти заголовки, вместе.

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

person Johannes Schaub - litb    schedule 11.11.2008

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

Некоторые основные рекомендации будут

  • Собирайте вещи, которые используются вместе
  • Создавать классы для объектов предметной области (например, файлов, коллекций и т. Д.)

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

// A.h
class A
{
public:
    int fn();
};

Затем вы можете использовать этот класс в нескольких исходных файлах:

// A.cpp
#include "A.h"
int A::fn() {/* implementation of fn */}

//B.cpp
#include "A.h"
void OtherFunction() {
    A a;
    a.fn();
}

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

// B.cpp
#include  "A.cpp" //DON'T do this!

Затем вы можете скомпилировать B.cpp, но когда вы попытаетесь связать свою программу, компоновщик будет жаловаться, что у вас есть несколько определенных объектов - это потому, что у вас есть несколько копий реализации A.

person David Dibben    schedule 11.11.2008

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

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

person Elroy    schedule 18.02.2009