2013-07-09 2 views
7

Я написал библиотеку (неважно, что она делает), которая, очевидно, имеет свой заголовочный файл. Теперь я хочу скрыть частные элементы этого заголовочного файла, поэтому, если я предоставляю свою библиотеку кому-то, он должен видеть только публичные члены (желательно, не определение класса, не что иное, как определения функций). Один из способов - создать заголовок C-стиля, который будет содержать какой-то метод «init», который будет использоваться для создания экземпляра фактического класса библиотеки, и пользователю придется передать указатель этого объекта на каждую функцию, чтобы сделать работу.Скрытие частных членов библиотеки C++

Это хорошая практика?

Есть ли другие общепринятые способы сделать что-то подобное?

Заранее спасибо.

+2

Похоже, вы можете использовать идиом Pimpl. Но какой бы метод вы ни использовали в конце, вам нужно знать, что скрытие реализации может привести к проблемам с производительностью (компилятор не сможет вставить много строк). – syam

ответ

11

В дополнение к шаблону Factory (который, на мой взгляд, может стать громоздким), вы также можете скрыть личные пользователей за Pimpl (Указатель ВНЕДРЕНИЕ):

// Interface.hpp 
class Implementation; 
class Interface { 
public: 
    Interface() : pimpl(new Implementation()) {} 
    void publicMethod(); 
private: 
    std::unique_ptr<Implementation> pimpl; 
}; 

// Interface.cpp 
class Implementation { 
public: 
    void PrivateMember(); 
}; 

void Interface::publicMethod() { pimpl->PrivateMember(); } 

Это имеет преимущество скрытия реализации, ценой единственного указателя указателя, мало чем отличается от типичной модели Factory, основанной на наследовании.

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

Это также распространенный идиом C++, поэтому другие программисты на С ++ распознают его без вопросов.

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

+1

На вопрос: это, по крайней мере, на мой взгляд, лучший ответ, чем мой. –

+0

@ EricFinn Да, я на самом деле думал то же самое. Спасибо за ответы, вы, ребята, помогли мне LOT :) – khajvah

1

Я думаю, вам нужно создать динамическую библиотеку ссылок (dll).

Please take a quick look at this link:

+0

Спасибо за ответ. Проблема в том, что я пользователь linux, и я не думаю, что есть способ создать DLL в Linux. Каковы альтернативы? – khajvah

+1

На самом деле, я понятия не имею о Linux. но я нашел этот ответ близко к твоему ... Надеюсь, это поможет! http://stackoverflow.com/questions/206272/hiding-private-data-members-c –

+0

Спасибо, я посмотрю на эти идиомы :) – khajvah

6

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

Например, интерфейс:

class Interface { 
public: 
    virtual void publicMethod() = 0; 
... 
}; 

И реализация:

class Implementation : Interface { 
public: 
    virtual void publicMethod(); 
private: 
    int hiddenMethod(); 
}; 

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

class Factory { 
public: 
    //can create and return an Implementation pointer, but caller will get an Interface pointer 
    std::shared_ptr<Interface> getImplementationInstance(); 
} 
+0

Хорошо, допустим, я являюсь пользователем этого интерфейса. Итак, я создаю экземпляр класса Interface, затем вызываю функции, но как он узнает, что я вызываю metonds класса реализации (поскольку мы создали только экземпляр интерфейса) – khajvah

+0

@khajvah Хорошая точка. Я уточню свой ответ. –

+0

@khajvah вам не нужно знать. Но интерфейс должен предоставить вам средство для создания экземпляров различных реализаций и вернуть вам умный указатель на абстрактный базовый тип. – juanchopanza

0

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

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

======= OuterClass.h ===== 
class InnerClass; // forward declaration is all that's needed 

class OuterClass { 
    private: 
     InnerClass *pInner; 

    public: 
     InnerClass(); 

     bool doSomething(); 
}; 

======= OuterClass.cpp ====== 
#include "OuterClass.h" 
#include "InnerClass.h" 

OuterClass::OuterClass() : 
    pInner(new InnerClass()) 
{ 
} 

bool OuterClass::doSomething() 
{ 
    return pInner->doSomething(); 
} 
+0

Можете ли вы расширить свой ответ? Ваш ответ может быть еще более полезным, если вы добавите описания этих шаблонов, как они будут использоваться, и ваши аргументы в пользу их предложения. – greyfade

2

Основание на Eric Finn's answer, вы можете просто объявить interface класс для хранения всех ваших публичных методов, которые считаются вашим API, и скрыть все реализации и частные член/методы в классе реализации, который наследует interface класса, вот пример:

Ваш заголовочный файл: my_api.h

// your API in header file 
// my_api.h 
class interface { 
public: 
    static interface* CreateInstance(); 
    virtual void draw() = 0; 
    virtual void set(int) = 0; 
}; 

ваша реализация (разделяемая библиотека): my_api.cpp (пользователи не будут видеть это, когда вы сделаете это разделяемая библиотека) Таким образом, вы можете скрыть все ваши реализации и приват т.е методы/члены здесь

#include "my_api.h" 
     // implementation -> in .cc file 
class implementation : public interface { 
    int private_int_; 
    void ReportValue_(); 
public: 
    implementation(); 
    void draw(); 
    void set(int new_int); 
}; 

implementation::implementation() { 
    // your actual constructor goes here 
} 

void implementation::draw() { 
    cout << "Implementation class draws something" << endl; 
    ReportValue_(); 
} 

void implementation::ReportValue_() { 
    cout << "Private value is: " << private_int_ << endl; 
} 
void implementation::set(int new_int) { 
    private_int_ = new_int; 
} 
interface* interface::CreateInstance() { 
    return new implementation; 
} 

Как пользователь использует свой API:

#include <iostream> 
#include "my_api.h" 

int main(int argc, const char * argv[]) 
{ 

    using namespace std; 
    interface* a; interface* b; 
    a = interface::CreateInstance(); 
    a->set(1); 
    b = interface::CreateInstance(); 
    b->set(2); 
    b->draw(); 
    a->draw(); 
    return 0; 
} 

Выход:

Implementation class draws 
Private int is: 2 
Implementation class draws 
Private int is: 1  

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

Смежные вопросы