Пример частного/публичного заголовка?
Может кто-нибудь, пожалуйста, дайте мне пример того, как работают публичные и частные заголовки? Я читал в сети, но я не могу найти много полезной информации с примерами кодов. Мне сообщили, что я должен использовать частные заголовки для разделения публичной и частной частей моего кода для создания статической библиотеки. После некоторого чтения у меня есть общее представление о том, как это работает, но по-настоящему оценит хороший пример, чтобы начать меня. В частности, я не совсем понимаю, как поместить функции интерфейса в мой общий заголовок и частные переменные/функции в моем закрытом заголовке? Спасибо!
EDIT:
Возможно, я не правильно формулирую свой вопрос, но я имел в виду, например, что у меня есть myMath.h и myMath.cpp, а myMath.h:
class myMath{
public:
void initialise(double _a, double _b);
double add();
double subtract();
private:
double a;
double b;
};
И myMath.cpp имеет реализации функций. Как я могу сделать так, чтобы myMath.h имел только три публичные функции, а частные переменные были определены в другом файле (например, myMath_i.h), и эти три файла были такими, что после создания статической библиотеки, Пользователям требуется только myMath.h. Это также означает, что myMath.h не может # включить myMath_i.h. Так что-то вроде:
myMath.h:
class myMath{
public:
void initialise(double _a, double _b);
double add();
double subtract();
}
и myMath_i.h:
class myMath{
private:
double a;
double b;
}
Конечно, это невозможно, потому что тогда я буду переопределять класс myMath...
Ответы
Ответ 1
У вас есть два файла заголовка MyClass.h и MyClass_p.h и один исходный файл: MyClass.cpp.
Давайте посмотрим, что внутри них:
MyClass_p.h:
// Header Guard Here
class MyClassPrivate
{
public:
int a;
bool b;
//more data members;
}
MyClass.h:
// Header Guard Here
class MyClassPrivate;
class MyClass
{
public:
MyClass();
~MyClass();
void method1();
int method2();
private:
MyClassPrivate* mData;
}
MyClass.cpp:
#include "MyClass.h"
#include "MyClass_p.h"
MyClass::MyClass()
{
mData = new MyClassPrivate();
}
MyClass::~MyClass()
{
delete mData;
}
void MyClass::method1()
{
//do stuff
}
int MyClass::method2()
{
return stuff;
}
Храните свои данные в MyClassPrivate и распространяйте MyClass.h.
Ответ 2
Вы можете объявить все интерфейсы и константы, которые вы хотите предоставить пользователю библиотеки, в отдельный файл заголовка (или набор файлов) и поместить его в подкаталог "inc" - эти заголовки будут "общедоступными". Вы также будете использовать другие файлы заголовков, чтобы объявлять классы и вещи, которые вы не хотите раскрывать, поскольку эти данные являются деталями реализации - вы поместите эти файлы в подкаталог "src" - они будут "private".
Таким образом, пользователю будет дан намек на то, что он должен включать только публичные заголовки - те, которые находятся в "inc" , и только те заголовки содержат то, что ему действительно нужно, в то время как все остальные заголовки "private" и вне его интереса, если он не хочет читать в ходе реализации.
Когда вы публикуете свою библиотеку как двоичную - статическую или динамическую библиотеку, вы копируете только содержимое "inc" и результат компиляции - этого достаточно для пользователя, и таким образом он не видит ваших источников реализации.
Ответ 3
Публичные заголовки - это те, которые нужны пользователям библиотеки. Частные заголовки - это те, которые необходимы для компиляции библиотеки, но которые не нужны пользователям библиотеки. Выполнение раскола может быть довольно сложным, и многие библиотеки просто не беспокоят.
Ответ 4
Я действительно нашел подход с двумя заголовками и одним источником хрупким. Если вы забыли обновить заголовок "public" после изменения заголовка 'private', ваш код может компилироваться, а затем segfault где-то еще во время выполнения. У меня это случалось со мной несколько раз, поэтому я предпочитаю писать код, который не будет компилироваться, если все не будет правильно.
MyClass.cpp реализуется следующим образом:
#define MYCLASS_IMPL
#include "MyClass.h"
// Implementation follows
...
MyClass.h - интересный бит:
#ifdef MYCLASS_IMPL
#include everything needed for the private: section
#endif
#include everything needed for the public: section only
class MyClass
{
public:
// Everything that public
#ifdef MYCLASS_IMPL
private:
// Implementation details
#endif
};
Если цель состоит в том, чтобы скрыть детали реализации (например, закрытую библиотеку), вам может потребоваться использовать подход с двумя заголовками. Если вы не хотите перетаскивать зависимости только для использования класса, подход с одним заголовком может быть простым и надежным решением.
Чтобы ответить на вопрос Артона: прошло некоторое время с тех пор, как я посмотрел на это, но я думаю, что проблема связана с созданием объектов на основе общедоступного заголовка, а затем с учетом другого размера объекта с закрытым заголовком. Во время выполнения смещение в объект не будет соответствовать, что приведет к разрыву памяти. Кажется, что ответ erenlender охватывает этот случай с помощью пары public/private class.
Ответ 5
Мне было интересно то же самое, поскольку я перехожу с C на С++ в качестве основного языка программирования. Я думаю, лучший способ - использовать интерфейсы (абстрактные классы на С++): вы публикуете публичный интерфейс, а ваша частная реализация просто использует интерфейс в качестве базового класса.