Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Концепция "Pointer to Implementation"


Автор: EvilsInterrupt 19.1.2010, 01:38
Вероятно все сталкивались с методикой pointer to impl, пример приведу тут:

содержимое инклуда stack.h:
Код

class c_stack {
  struct link;
  link * head;
public:
  void initialize();
  void push(void * data);
  void *peek();
  void *pop();
  void cleanup();
};


содержимое файла-реализация stack.cpp:
Код

struct c_stack::link{
  void * data;
  link * next;
  void initialize(void * dat, link * nxt);
}*head;

void c_stack::link::initialize(void *dat, c_stack::link *nxt)
{
  data = dat;
  next = nxt;
}

void c_stack::initialize() { head = 0; }

void c_stack::push(void *data)
{
  link * new_link = new link;
  new_link->initialize(data,head);
  head = new_link;
}


Это куски из примера ). В чем тут прикол, а то что заказчик лишен возможности видеть хоть какую-либо реализацию!!! Даже структура скрыта в файле-реализации. В случае private части класса, все логично и понятно!
А что делать если это касается protected части базового класса ? Ведь потомок захочет вдруг воспользоваться тем что в protected части, а оно если следовать методике p-to-impl скрыто! Как быть ?

Открывать ? Тогда : 1) заказчик больше получит информацию о строении либы 2) при изменении хоть чего-то в реализации, это еще одна перекомпиляция проекта, а так сделал интерфейс и меняй себе реализацию скольо хочешь!

Как быть с protected частью базового, чтобы было доступно потомкам его и при этом скрыто от заказчика библиотеки классов ? )

Автор: mes 19.1.2010, 02:05
Цитата(EvilsInterrupt @  19.1.2010,  00:38 Найти цитируемый пост)
Вероятно все сталкивались с методикой pointer to impl,

ну тут PImpl`ом и не пахнет .. простое АТД.
Цитата(EvilsInterrupt @  19.1.2010,  00:38 Найти цитируемый пост)
А что делать если это касается protected части базового класса ? Ведь потомок захочет вдруг воспользоваться тем что в protected части, а оно если следовать методике p-to-impl скрыто! Как быть ?

а вот тут не понятно в чем проблема. Хотите чтоб было доступно ? ну так сделайте.. не хотите не делайте smile

Добавлено через 1 минуту и 18 секунд
чего то я в примере не найду ничего protected`ного..

Автор: kemiisto 19.1.2010, 11:07
Цитата(mes @  19.1.2010,  03:05 Найти цитируемый пост)
ну тут PImpl`ом и не пахнет .. простое АТД.

+1 Только таки простой АТД. ;-)

Цитата(EvilsInterrupt @  19.1.2010,  02:38 Найти цитируемый пост)
А что делать если это касается protected части базового класса ?

Он ты как захотел. smile Смотри, что умные дядьки http://gotw.ca/gotw/024.htm.
Цитата
 - put all private and protected members into XImpl;

Taking this extra step is actually wrong. Protected members should never go into a pimpl, since putting them there just emasculates them. After all, protected members exist specifically to be seen and used by derived classes, and so aren't nearly as useful if derived classes can't see or use them.


Тут ведь в чём дело. С точки зрения современного ОО проектирования в вершине иерархии классов должен стоять либо интерфейс, либо абстрактный класс. Строго говоря, в Це++ нет ни того, ни другого. smile Но мы тут не об этом. Так вот у этого чисто абстрактного класса нет реализации по определению. И тут уже pimpl идёт лесом.

Ну и дальше там будет неувязочки
Цитата
I believe the main drawback is that the pimpl object cannot call a virtual method of the main object without a back pointer. Since the objective of this approach is to avoid the use of a back pointer, it cannot be very useful if your class has virtual methods that need to be called in its implementation (which is usually the case with inheritance) thereby requiring a back pointer.


А всё почему? Прально! smile Ну нет модулей в языке! Нет и не будет. smile 

Автор: baldina 19.1.2010, 13:18
EvilsInterrupt, обобщая сказанное mes и kemiisto: можно впихнуть невпихуемое и совместить pimpl с абстракцией. но они будут на разных уровнях иерархии.

Например, вверху иерархии абстрактный стек:

Код

// stack.h

template <typename T>
class stack {
  
  protected:
    virtual size_t size () const = 0;
    virtual size_t capacity () const = 0;
    virtual void reserve (size_t) = 0;
    virtual void add (const T&) = 0;
    virtual void remove () = 0;
    virtual T get () const = 0;

  public:
    T top () const { return get(); }
    void pop() { remove(); }
    void push (const T& x) { add(x); }
    bool is_empty () const { return size() == 0; }
    virtual ~stack() {}
  
};

// конкретная реализация может переопределять защищенные члены и использовать pimpl
// vstack.h
#include <memory>
#include "stack.h"

template <typename T>
class vstack: public stack<T> {
  private:
    class vector;
    std::auto_ptr<vector> p_impl;

  protected:
    virtual size_t size () const;
    virtual size_t capacity () const;
    virtual void reserve (size_t size);
    virtual void add (const T& x);
    virtual void remove ();
    virtual T get () const;
  
  public:
    vstack ();
    ~vstack ();
};


клиентский код видит и использует только то, что ему положено:

Код

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

template <typename T>
void print_stack (stack<T>& s)
{
  while (!s.is_empty ())
  {
    std::cout << s.top () << ' ';
    s.pop ();
  }
}

int main ()
{
  vstack <int> s;
  for (int i=0; i < 10; ++i)
    s.push (i);
  print_stack (s);
}


реализация произвольна, например стандартный вектор

Код

// vstack_impl.h
#include <vector>
#include "vstack.h"

template <typename T>
class vstack<T>::vector : public std::vector<T> {};

template <typename T>
vstack<T>::vstack () : p_impl( new vstack<T>::vector ) {}

template <typename T>
vstack<T>::~vstack () {}

template <typename T>
size_t vstack<T>::size () const 

  return p_impl->size(); 
}

template <typename T>
size_t vstack<T>::capacity () const

  return p_impl->capacity(); 
}

template <typename T>
void vstack<T>::reserve (size_t size)

  p_impl->reserve (size); 
}

template <typename T>
void vstack<T>::add (const T& x)

  p_impl->push_back (x); 
}

template <typename T>
void vstack<T>::remove ()

  p_impl->pop_back(); 
}

template <typename T>
T vstack<T>::get () const

  return *--p_impl->end(); 
}


ЗЫ: если где нить инстанциировать шаблон, оно даже возможно заработает  smile 
типа
Код

#include "st_impl.h"
template vstack <int>;

Автор: EvilsInterrupt 19.1.2010, 19:23
И тем не менее, то что я привел называется Pimpl, вот по этому паттерну http://habrahabr.ru/blogs/qt_software/76248/
или http://www.insidecpp.ru/patterns/pimpl_idiom/ .
В том что я привел в хидере объявляется указатель на структуру, которая всего-лишь объявлена, но определена в файле реализации! Это вполне удовлетворяет идее паттерна!

Почему собственно возникла такая ситуация ? 
1) Дело в том, что пользователя должно заботить только то что в паблик части, он это хотел, он это получил!
2) Проект может быть связан с системами компьютерной безопасности, зачем пользователю показаывать лишний раз детали, которые его абсолютно не касаются?
3) Если хидер описан умело, то не изменяя его, а только файлы реализации, то можно не боятся, что придется весь проект перекомпиливать !
4) В среде разработке, когда пишешь код с применением code complition в комбобоксе также показывается и приват и протектед часть, нафига она мне пользователю класса нужна ?

С private часть все понятно, но с protected тоже не мешало бы скрыть от юзера, как  это сделать ?

Автор: zim22 19.1.2010, 19:33
Цитата(EvilsInterrupt @  19.1.2010,  18:23 Найти цитируемый пост)
С private часть все понятно, но с protected тоже не мешало бы скрыть от юзера, как  это сделать ?

мда... kemiisto вроде всё сказал уже:
Цитата(kemiisto @  19.1.2010,  10:07 Найти цитируемый пост)
 - put all private and protected members into XImpl;

Taking this extra step is actually wrong. Protected members should never go into a pimpl, since putting them there just emasculates them. After all, protected members exist specifically to be seen and used by derived classes, and so aren't nearly as useful if derived classes can't see or use them.


 

Автор: baldina 19.1.2010, 19:46
Цитата

1) заказчик больше получит информацию о строении либы

Если заказчик будет наследовать классам либы, то ему, возможно, понадобится дополнительно знание о её строении. Тут все ок.
Если заказчик НЕ  будет наследовать классам либы, то protected данные в классах, видимых заказчику, вообще не нужны.


Автор: mes 19.1.2010, 19:58
Цитата(EvilsInterrupt @  19.1.2010,  18:23 Найти цитируемый пост)
И тем не менее, то что я привел называется Pimpl, вот по этому паттерну статья
или еще одна .

ну вот цитата из Вашей статьи :
Цитата

Каждый метод, который содержит доступ к приватной реализации добавляет плюс одну экстра инструкцию.

можете показать где такое в Вашем примере ? 
 smile 

Цитата(EvilsInterrupt @  19.1.2010,  18:23 Найти цитируемый пост)
В том что я привел в хидере объявляется указатель на структуру, которая всего-лишь объявлена, но определена в файле реализации! Это вполне удовлетворяет идее паттерна!

удовлетворяет - не значит достаточно smile 

Ну а по теме: все зависит от Ваших условий - сформулируйте их для себя.. что позволено клиенту и до какой степени и это будет ответом на вопрос какую из идеологий  (или какой диалект идиологии) выбрать для Вашего случая.


Автор: EvilsInterrupt 19.1.2010, 20:33
Цитата

Если заказчик НЕ  будет наследовать классам либы, то protected данные в классах, видимых заказчику, вообще не нужны.

Вот и я о том, но возьмем к примеру ситуацию :

0) животное - базовый класс
1.1) Тигр от животного
1.2) Собака от животного

Казалось бы заказчик заказывал только два животных - тигра и собаку, ну мало ли че, вдруг у него такая компьютерная игра не хитрая. Также требовал реализации того чтобы каждое животное умело прыгать, кусаться и лапу поднимать!
А программист взял в базовом в защищенной(протектед) части определил - Раскрыть пасть, подвинуть голову, сомкнуть пасть, а в потомках тигре и собаке их реализовал.

Казалось бы чего такого? Обычная задачка, но ! Это же надо писать в декларации классса в протектед части, а заказчик этот инклуд будет подключать для того чтобы можно было заюзать lib файл. Но скажите, на кой икс ему эти протектед части нужны ? Он и порождаться то не хочет. 

Как быть-то программеру ? Как спрятать защищенные методы вынесенные в базовый? Может их  тупо убрать и кинуть в потомки?! Однако это не следование ООП-мысли !!! ;)

Автор: zim22 19.1.2010, 21:00
Цитата(EvilsInterrupt @  19.1.2010,  19:33 Найти цитируемый пост)
Однако это не следование ООП-мысли !!! ;)

ООП-мысль - это что?

кстати, наследование нарушает инкапсуляцию. (GoF)

Автор: mes 20.1.2010, 00:23
Цитата(EvilsInterrupt @  19.1.2010,  19:33 Найти цитируемый пост)
Как быть-то программеру ? Как спрятать защищенные методы вынесенные в базовый? Может их  тупо убрать и кинуть в потомки?! Однако это не следование ООП-мысли !!! ;) 

1. Не делать защищенные методы в классах не предназначенных для наследования
2. Не давать пользователю, то что ему не нужно
3. Не искать прямых путей - обходной может быть короче.

Итого пользователю нужно :
1. интерфейс животного -> абстрактный класс с открытыми методами.
2. Два наследника этого -> два класса реализующих этот интерфейс с отсутсtвием лишних деталей.
Разработчик реализует :
1. Описание ползовательских классов (include/animals.h)
2. Набор функций/ классов реализующих общее и индивидуальное поведение  (source/animals_detail.h)
3. Связь пользовательских классов с функциональностью (source/animals.cpp) 

smile



Автор: bsa 20.1.2010, 12:20
EvilsInterrupt, как вариант можно сделать два класса-обертки, конструкторы/деструкторы которых будут создавать/уничтожать реализацию, которая, в свою очередь, может быть какой угодно (хоть все методы виртуальные и открытые). Вот в этом случае и будет настоящий PIMPL - класс "тигр" будет содержать только указатель на класс TigerImpl, реализация которого будет исключительно внутри библиотеки, и доступные методы. smile

Автор: baldina 20.1.2010, 12:40
EvilsInterrupt
Цитата

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

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

Автор: EvilsInterrupt 20.1.2010, 18:21
2 baldina :
>>Вы неправильно ставите задачу. 
Ну неужели я должен тут постить ситуацию заказчика ? Все его секреты и заскоки ? ИМХО, не имею права! Потому и придумываю ситуацию похоже на его!

Ситуация проста, он хочет получить интерфейс и файл библиотеки, а я нехочу чтобы он хоть чтото знал о том как реализована. Малейшая мысль о том как сделано, сведет на нет работу, а lib-файл покрою протом, это же обыкновенный x86-код и формат не шибко отличается от тех PE-заморочек в привычных нам exe, dll, sys файлах!!!

Пока пришел к выводу:
1) Создаю в инклуд базовый класс и + инструкцию которая в аргументе возвращает созданный объект потомка, ну и bool, как возвращаемое значение ф-ции.
2) В файлах реализации создаю потомки этого базового класса, ну и там же наследуюсь в еще один класс промежуточный class c_wrapper_animal : public c_basic_animal
3) В промежуточном  c_wrapper_animal создаю все протектед методы, которые переопределяю и реализовываю в потомках
4) Реальные объекты создаются в зависимости от аргумента "at" функции bool create_animal(c_basic_animal * animal, animal_type at) , именно существование этой ф-ции упомяналось в п.1.

Дальше компилю, протечу(защищаю протектором) и высылаю animal.h + animal.lib , на этом пока мои мысли завершены.

Собственно пока на этом все!

Автор: maxim1000 20.1.2010, 18:37
если у нас есть три набора информации, у каждой из которых разные пользователи, логично поместить её в три файла:

в основном h-файле - информация для всех пользователей: описание класса, указатель на структуру с private-данными, указатель на структуру с protected-данными

в дополнительном h-файле - информация для тех, кто захочет наследоваться: описание структуры с protected-данными основного класса

в третьем файле (cpp или ещё один h) - информация для внутренней реализации класса: описание структуры с private-данными основного класса

тогда каждый подключает тот файл, который ему нужен

Автор: EvilsInterrupt 20.1.2010, 19:29
2 maxim1000 :
Именно так и сделал! ;) Только допер до этого совсем не тогда когда тему создавал, а мысль на эту хрень натолкнулпост от mes сделанный в "20.1.2010, 01:23"

Автор: olen 20.1.2010, 19:29
sgfsdfsdf

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)