Какие плохие вещи произошли бы, если Вы пишете целый класс в единственном файле в C++?

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

11
задан gilbertc 19 March 2010 в 18:10
поделиться

8 ответов

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

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

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

Например, если у меня есть следующее:

// MyClass.h
#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

#include "Inventory.h"

class MyClass
{
public:
  MyClass();

  void processInventory(Inventory& inventory)
  {
    // Do something with each item in the inventory here
    // that uses iostream, iomanip, sstream, and string
  }
private:
  // ...
};

Идеально было бы записать так:

// MyClass.h
class Inventory;

class MyClass
{
public:
  MyClass();

  void processInventory(Inventory& inventory);
private:
  // ...
};

// MyClass.cc
#include "MyClass.h"

#include <iostream>
#include <iomanip>
#include <sstream>
#include <string>

#include "Inventory.h"

MyClass()::MyClass()
{
}

void MyClass()::processInventory(Inventory& inventory)
{
  // Do something with each item in the inventory here
  // that uses iostream, iomanip, sstream, and string
}

Примечание: включение MyClass.h не означает iostream , iomanip , sstream , строка или Inventory.h должны быть проанализированы. Изменение того, как работает processInventory , не означает, что все файлы, использующие MyClass.h , должны быть перекомпилированы.

Обратите внимание, насколько проще теперь понять, как использовать MyClass . Заголовочные файлы служат важной цели: они показывают людям, как использовать ваш класс. С измененным MyClass.h легко увидеть список функций. Если каждая функция определена в заголовке, вы не можете смотреть только на список функций. Это затрудняет понимание того, как использовать класс.

12
ответ дан 3 December 2019 в 05:12
поделиться

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

2
ответ дан 3 December 2019 в 05:12
поделиться

Самое важное, что нужно знать о #include в отличие от методов импорта на других языках, #include КОПИРУЕТ содержимое того файла, в котором размещена директива #include. Таким образом, объявление и определение класса в одном файле создаст три вещи:

  • Значительно увеличит объем компиляции в
    раз.

  • Если ваши определения не являются встроенными , вы получите ошибки компоновщика, так как компилятор находит несколько
    определений для одних и тех же функций

  • Это приведет к обнаружению реализация для пользователя, а не только интерфейс.

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

1
ответ дан 3 December 2019 в 05:12
поделиться

Вы можете нарушить правило одного определения.

Если вы напишете this:

class foo
{
public:
    void doit();
};

foo::doit() {}

и включите это в несколько классов, у вас будет несколько определений foo::doit, и ваша ссылка не сработает.

Но если вы сделаете все свои классы инлайн, либо определив их в объявлении класса:

class foo
{
public:
    void doit() {
    }
};

либо явно сделав их инлайн:

class foo
{
public:
    void doit();
};

inline void foo::doit() {}

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

5
ответ дан 3 December 2019 в 05:12
поделиться

Обычно вы разделяете объявление и определение класса. Это позволяет вам использовать ваш класс в разных исходных файлах, просто включив объявление.

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

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

1
ответ дан 3 December 2019 в 05:12
поделиться

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

0
ответ дан 3 December 2019 в 05:12
поделиться

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

0
ответ дан 3 December 2019 в 05:12
поделиться

@Bill

Я думаю, что важно подчеркнуть точку зрения Билла:

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

файл .h является более или менее «общедоступным» документом, позволяющим понять, как ваш класс работает в некоторой степени концептуально - Интерфейс. Помните, что исходный файл следует рассматривать как частный. Я помню, как много узнал о том, как работал Unix в первые дни работы над C / C ++, читая файлы заголовков. Также помните, что сложность встроенных функций не должна превышать

1
ответ дан 3 December 2019 в 05:12
поделиться
Другие вопросы по тегам:

Похожие вопросы: