Отказ от объявления приватных функций в файлах заголовков классов (C++)

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

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

Как я могу этого добиться?


person user664303    schedule 14.04.2011    source источник
comment
Есть подходы, которые хорошо работают, если бы я писал класс с нуля. Однако я не начинаю с нуля; этот класс уже написан.   -  person user664303    schedule 16.04.2011


Ответы (4)


Используйте идиому pImpl. Ваш видимый класс хранит указатель на реальный class и переадресовывает вызовы общедоступным функциям-членам.

РЕДАКТИРОВАТЬ: В ответ на комментарии

// Foo.h:

class FooImpl; // Do *not* include FooImpl.h
class Foo {
public:
  Foo();
  ~Foo();
  //.. also need copy ctor and op=
  int bar();
private:
  FooImpl * Impl;
};

// FooImpl.h:

class FooImpl {
public:
  int bar() { return Bar; }
private:
  int Bar;
};

// Foo.cpp:

#include "FooImpl.h"

Foo::Foo() { Impl = new FooImpl(); }
Foo::~Foo() { delete Impl; }
int Foo::bar() { return Impl->bar(); }

Сохраните фактическую реализацию вашего класса в FooImpl - Foo должен иметь копии public членов FooImpl и просто перенаправлять вызовы на них. Все пользователи будут включать только «Foo.h» — вы можете изменить все личные данные FooImpl так, чтобы пользователи Foo не увидели никаких изменений.

person Erik    schedule 14.04.2011
comment
Спасибо. В общем, это хорошая идея. Однако в данном случае я не начал использовать этот фреймворк, поэтому переход на него потребовал бы значительного переписывания моего класса, включая написание оболочек для всех его общедоступных функций. Я хотел бы просто иметь возможность отправлять подмножество частных функций из заголовка без особых хлопот. - person user664303; 14.04.2011
comment
@user664303: user664303: Наследование может быть решением - эта конкретная проблема является одним из раздражающих заблуждений C++ imo. - person Erik; 14.04.2011
comment
Правильно ли я считаю, что для этого обязательно потребуется приведение указателя экземпляра класса, например: ((priv_class *)this)->priv_func();. Или есть способ избежать этого? - person user664303; 14.04.2011
comment
@ user664303: Предварительное объявление — .h: class FooImpl; class Foo { public: void bar(); FooImpl * Impl; }; .cpp: #include "FooImpl.h" ... void Foo::bar() { Impl->bar(); } - person Erik; 14.04.2011
comment
@Erik: я не понимаю, как FooImpl::bar() может иметь доступ к закрытым членам Foo. Я мог бы сделать Foo другом FooImpl, но мне все равно пришлось бы передавать FooImpl::bar() указатель на экземпляр Foo. - person user664303; 16.04.2011
comment
@ user664303: Foo не имеет закрытых членов (кроме указателя Impl) — у него есть только те же общедоступные функции-члены, что и у FooImpl. См. обновленный ответ с образцом. - person Erik; 16.04.2011

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

заголовок.h:

class FooImpl;

class Foo {
public:
   int bar();
   friend class FooImpl;
private:
   int var;
}

импл.cpp:

#include "header.h"

class FooImpl {
public:
   int bar(Foo &);
}

int FooImpl::bar(Foo &foo) {
return foo.var;
}

int Foo::bar() {
return FooImpl::bar(*this);
}
person user664303    schedule 16.04.2011
comment
Это мое понимание до сих пор, но я не уверен на 100%. Комментарии с благодарностью получены. - person user664303; 16.04.2011
comment
У вас все еще есть int var в class Foo. Разве весь смысл вопроса не в том, чтобы переместить его на class FooImpl? - person balki; 06.03.2013
comment
@balki: Нет, весь смысл этого вопроса заключался в том, как добавлять или редактировать частные функции в классе без изменения заголовка. Если я перемещу частные переменные в FooImpl, мне также придется перенаправить все существующие общедоступные функции в этот класс. Смотрите мой первый комментарий к ответу Эрика об этом. - person user664303; 07.03.2013

Вы ищете брандмауэр компилятора, также известный как PIMPL?

person Nikolai Fetissov    schedule 14.04.2011

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

person Neil Kirk    schedule 12.09.2013