Пример частного / публичного заголовка? - PullRequest
26 голосов
/ 16 февраля 2010

Может кто-нибудь дать мне пример того, как работают публичные и частные заголовки? Я немного читал в сети, но не могу найти много полезной информации с примерами кодов. Мне посоветовали использовать частные заголовки для разделения открытых и закрытых частей моего кода для создания статической библиотеки. После некоторого прочтения у меня есть общее представление о том, как это работает, но я был бы очень признателен за хороший пример для начала. В частности, я не совсем понимаю, как поместить функции интерфейса в мой открытый заголовок, а частные переменные / функции - в мой частный заголовок? Спасибо!

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 не может #include 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 ...

Ответы [ 5 ]

23 голосов
/ 16 февраля 2010

У вас есть два заголовочных файла 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.

10 голосов
/ 16 февраля 2010

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

Таким образом, пользователю будет дано указание на то, что он должен включать только публичные заголовки - те, которые находятся в «inc», и только те заголовки содержат то, что ему действительно нужно, в то время как все остальные заголовки являются «частными» и выходными его области интересов, если он не хочет читать в реализации.

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

2 голосов
/ 13 октября 2012

Я считаю, что подход с двумя заголовками и одним источником является хрупким. Если вы забыли обновить заголовок «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's public

#ifdef MYCLASS_IMPL
private:

    // Implementation details

#endif
};

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


Чтобы ответить на вопрос Артона: прошло много времени с тех пор, как я смотрел на это, но я думаю, что проблема была связана с созданием экземпляров объектов на основе публичного заголовка, а затем с использованием частного заголовка другого размера объекта. Во время выполнения смещение объекта не будет совпадать, что приведет к разрушению памяти. Похоже, что ответ erenlender охватывает этот случай с парой публичного / частного класса.

2 голосов
/ 16 февраля 2010

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

0 голосов
/ 22 июня 2012

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

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