Файл класса заголовка для использования другими

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

другой вопрос касается частных статических материалов, могу ли я объявить их в заголовочном файле и реализовать их в модуле .cpp? приватная переменная и общедоступный метод получения, который я пробовал, но не смог реализовать метод на устройстве, он работал только с объявлением и реализацией в заголовке

[] с, Джо


person Jonathan    schedule 05.10.2009    source источник


Ответы (5)


Правильный способ справиться с этим — реализовать идиому pimpl: создать класс или структуру для всех частных данных и поместить указатель на такой объект в заголовочный файл вместе с предварительным объявлением. Теперь ничего из приватных данных и методов не видно из заголовочного файла.

person andreas buykx    schedule 05.10.2009
comment
Вот еще две хорошие ссылки: gotw.ca/gotw/024.htm gotw.ca/gotw/028.htm - person jdehaan; 06.10.2009
comment
Может ли кто-нибудь объяснить эту ссылку: gotw.ca/gotw/028.htm Я не Не понимаю, как функции Allocate и Decallocate будут освобождать и создавать производную структуру. Спасибо! - person Jonathan; 06.10.2009

Правильный ответ — использовать Pimpl (через указатель, как указывает Павел). Существует также сумасшедший, но потенциально правильный способ, описанный Мэтью Уилсоном в Imperfect C++, где вы можете предварительно объявить внутреннюю структуру и включите непрозрачный блок памяти в свой класс, а затем на месте создайте внутреннюю структуру (определение которой сделано в файле реализации) в конструкторе основного класса в файле реализации.

Я должен указать, что Уилсон показывает это в приложении, где он признается в нескольких таких «преступлениях против программирования» в качестве предостережения программистам, пытающимся быть слишком умными. Он говорит, и я говорю, что вы не должны использовать это. Однако, если у вас есть какие-то сверхвысокие требования к производительности, возможно это может оказаться полезным.

person dcw    schedule 05.10.2009
comment
Желательно умный указатель, на самом деле — const std::auto_ptr<T> отлично для этого подходит. - person Pavel Minaev; 06.10.2009
comment
Кроме того, проблема с описанным вами трюком (предварительное выделение блока внутри самого объекта, а не указателя) заключается в том, что он убивает еще одно преимущество, предлагаемое pimpl, которое обычно более важно, чем скрытие частных членов - это позволяет вам писать версии- безопасный код без ограничений на новые поля (поскольку все они идут в приватный объект). В случаях, когда производительность на самом деле достаточно важна, чтобы это стало проблемой, принудительная инкапсуляция, вероятно, еще меньше беспокоит, поэтому просто делайте это как обычно. - person Pavel Minaev; 06.10.2009
comment
Павел, ты прав (насчет версионности). Как я надеюсь, я убедительно сообщил, что ни я, ни книга Уилсона не защищают эту технику. Просто (как это всегда бывает в C++) очень мало обстоятельств, когда это может быть разумным выбором. Предостережение для покупателя! :-) - person dcw; 06.10.2009

У Андреаса есть ответ, но обратите внимание, что это сделает ваш код несколько более тупым для вас:

 // header file

 struct hidden_structure;

 class Foo {
     hidden_structure* hidden_data;
 public:
     Foo();
     ~Foo();

     void doStuff();
 };

 // your cpp file

 struct hidden_structure;
     int stuff;
     hidden_structure() : stuff(0) {}
 }

 Foo::Foo() : hidden_data(new hidden_structure) {}
 Foo::~Foo() { delete hidden_data; }

 void Foo::doStuff() { hidden_data->stuff += 34; } // hey, it does a lot of stuff

Как видите, чем больше данных внутри hidden_structure, тем сложнее это может стать.

person jmucchiello    schedule 05.10.2009
comment
1) Используйте умный указатель, 2) что вы подразумеваете под чем больше данных, тем сложнее? сложность постоянна (вам просто нужно предварять каждую ссылку на поле p-> или как там называется поле pimpl). - person Pavel Minaev; 06.10.2009
comment
такой код кажется уродливым. Я предполагаю, что мне придется сделать этот PIMPL, или я должен удалить все ниже private: прежде чем передать его кому-то? :( Я трачу одно и то же время на кодирование и разработку своего кода. С вами тоже такое происходит? - person Jonathan; 06.10.2009

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

Как это:

.h файл--

class useThis;   // users actually use this class

class publics {  //this is the interface they see
public:
    int foo;
};

Файл .cpp --

#include "foo.h"

class useThis: publics {

private:
    void add(int b);

};

void useThis::add( int b )
{
    foo+= b;
}
person tod frye    schedule 06.10.2009
comment
Я не думаю, что это работает. Когда люди используют заголовок, нет ничего, что связывало бы useThis с publics. - person Jonathan Leffler; 06.10.2009
comment
я уверен, что это работает. все в пабликах также находится в useThis, так как useThis наследуется от пабликов. - person tod frye; 07.10.2009
comment
это работает или нет? это кажется хорошим способом даже для использования с шаблонами - person Jonathan; 08.10.2009

То же, что и код Джона, но я использую указатель вместо ссылок:

// file.h
class TheClass_p;
class TheClass{
  public: 
    TheClass();
    ~TheClass();
  private
    TheClass_p *d;

};

// file.cpp
class TheClass_p {
  int foo;
  float: bar;
};

TheClass::TheClass(){
   d = new TheClass_p;
}

TheClass::~TheClass(){
   delete d;
}

Изменить: добавлен деструктор для устранения утечки памяти

person elcuco    schedule 05.10.2009
comment
Вам нужно освободить память в деструкторе. - person Vitali; 06.10.2009