Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > pimpl с применением std::auto_ptr не работает


Автор: EvilsInterrupt 24.6.2010, 09:03
Для того чтобы пояснить какая у меня в основном проект, я создал поменьше "школьный" вариант.

Файл header1.h содержит:
Код

#ifndef INCLUDE_HEADER1_H
#define INCLUDE_HEADER1_H

#include <memory>

namespace hdr {

  class CMain1 {
  public:
    CMain1();
  private:
    class CPrivate1;
    std::auto_ptr<CPrivate1> m_Private;
  };

} /* namespace hdr { */

#endif /*INCLUDE_HEADER1_H*/


Файл implement1.cpp содержит:
Код

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

#define M_MSG(N, S)  (std::cout << N << " : " << S << std::endl)

namespace hdr {

  class CMain1::CPrivate1 {
  public:
    CPrivate1() : m_val(0)  {
      M_MSG(m_val,"CPrivate1 constructor");
      m_val++;
    }
    ~CPrivate1()  {
      M_MSG(m_val,"CPrivate1 destructor");
      m_val++;
    }
  private:
    int m_val;
  };

  CMain1::CMain1() : m_Private(new CPrivate1)
  {
  }

} /* namespace hdr { */


В результате получаю:
Цитата

1>c:\program files (x86)\microsoft visual studio 9.0\vc\include\memory(718) : warning C4150: deletion of pointer to incomplete type 'hdr::CMain1::CPrivate1'; no destructor called
1>        e:\source\proba\header1.h(13) : see declaration of 'hdr::CMain1::CPrivate1'
1>        c:\program files (x86)\microsoft visual studio 9.0\vc\include\memory(716) : while compiling class template member function 'std::auto_ptr<_Ty>::~auto_ptr(void)'
1>        with


инклуд memory подключен! Самое прикольно создается exe-файл и при отладке отрабатывает конструктор, а деструктор не пашед и я вижу строчку на  экране:

0 : CPrivate1 constructor

т.е. компилятор класс как бы находит, однако почему же он деструктор не осилил ?

подобное можно  найти и http://www.insidecpp.ru/patterns/pimpl_idiom

Автор: mes 24.6.2010, 09:06
auto_ptr должно знать полный класс (определение), а не только его объявление.. 

Автор: EvilsInterrupt 24.6.2010, 09:07
mes, Ну  как бы директива препроцессора #include вставляет файл и полная единица компиляции. Чего не так-то ? Форвард-декларация должна быть форвардом, а не муляжом!

Автор: Abyx 24.6.2010, 09:13
EvilsInterrupt, прямо скажем ничего она вам не должна.
Есть есть сомнения в ее поведении - почитайте стандарт.
Попробуйте написать тот же код, без include, руками собрав все в 1 файл

Автор: mes 24.6.2010, 09:27
Цитата(EvilsInterrupt @  24.6.2010,  08:07 Найти цитируемый пост)
Форвард-декларация должна быть форвардом, а не муляжом!

так он и есть форвард..  smile

Цитата(EvilsInterrupt @  24.6.2010,  08:03 Найти цитируемый пост)
 deletion of pointer to incomplete type 

сравните ситуации 
Код

class C *c;
delete  c;
class C {
};

Код

class C *c;
class C {
};
delete  c;

smile

Добавлено @ 09:29
Цитата(EvilsInterrupt @  24.6.2010,  08:07 Найти цитируемый пост)
 Ну  как бы директива препроцессора #include

#include тут вообще сбоку..  речь о наличии определения в нужный момент.

Автор: EvilsInterrupt 24.6.2010, 09:43
>>сравните ситуации
При таких , согласен! ;)

Но, давайте взглянем на std::auto_ptr:
Код

private:
    _Ty *_Myptr;    // the wrapped object pointer
    };

Напишем с указателем в явном виде:
Код

  private:
    class CPrivate1;
    CPrivate1 * m_Private;


Почему-то сейчас уже деструктор находится! А код не изменился,разве что в деструктор ~CMain() добавил удаление m_Private

Автор: Alexeis 24.6.2010, 09:47
Цитата(EvilsInterrupt @  24.6.2010,  08:43 Найти цитируемый пост)
Почему-то сейчас уже деструктор находится! А код не изменился,разве что в деструктор ~CMain() добавил удаление m_Private 

  У тебя же реализация в cpp. В этом случае он уже "знает" деструктор.

Автор: mes 24.6.2010, 09:49
Цитата(EvilsInterrupt @  24.6.2010,  08:43 Найти цитируемый пост)
Но, давайте взглянем на std::auto_ptr:

ну а теперь подумайте, где в первом примере формируется деструктор у auto_ptr в котором вызыватся delete содержимого.. 
smile

Добавлено @ 09:52
Цитата(EvilsInterrupt @  24.6.2010,  08:43 Найти цитируемый пост)
разве что в деструктор ~CMain() добавил удаление m_Private

во во.. ) а теперь перенесите его внутрь класса  
smile

Автор: EvilsInterrupt 24.6.2010, 09:56
вроде понял ) Но тогда получается pimpl не возможен на С++ с применением std::auto_ptr .

Просто меня очень бесит,когда инклуд содержит что-то относящееся к информации о реализации. На сях, всегда делал "чистые" инклуды, пользователь библиотеки знал только то что необходимо для использования библиотеки. А тут,  разве что на чистых указателях можно ;)

Автор: mes 24.6.2010, 09:59
Цитата(EvilsInterrupt @  24.6.2010,  08:56 Найти цитируемый пост)
вроде понял ) Но тогда получается pimpl не возможен на С++ с применением std::auto_ptr .

попробуйте пустой деструктор с определением в cpp добавить... 

Автор: EvilsInterrupt 24.6.2010, 10:12
mes, пашед! Но тогда где освобождать ресурсы если не  в деструкторе ?

Автор: mes 24.6.2010, 10:22
Цитата(EvilsInterrupt @  24.6.2010,  09:12 Найти цитируемый пост)
 Но тогда где освобождать ресурсы если не  в деструкторе ? 

не понял

Добавлено через 2 минуты и 26 секунд
или это Вы про  
Цитата(mes @  24.6.2010,  08:59 Найти цитируемый пост)
пустой деструктор

?
так можете его полным сделать... суть не в этом.. главное чтоб определение деструктура было в точке, в которой все необходимое определено.

Добавлено через 4 минуты и 5 секунд
и еще раз внимательно перечитайте http://forum.vingrad.ru/index.php?showtopic=304001&view=findpost&p=2174264
до понимания происходящего.
smile

Автор: EvilsInterrupt 24.6.2010, 10:26
Я к тому что, если в коде ниже, раскоментить, то ошибка :
Код

    ~CPrivate1()  { /*
      M_MSG(m_val,"CPrivate1 destructor"); 
      m_val++; */
    }

А тело пустым, то все ок! Получается что в конструкторе получил ресурс, а в деструкторе его освобождение не могу прописать ;(


нашел про pimpl в http://habrahabr.ru/blogs/qt_software/76248/

Автор: mes 24.6.2010, 10:29
я говорил про деструктор для CMain1.

Добавлено @ 10:30
деструктор Привателя к нашей задаче не относится..

Добавлено @ 10:34
кстати о auto_ptr .. Вы уверены что Вам нужна именно это стратегия хранения ?
так как после этого объект класса, становится не copyable и его нельзя будет добавить например в вектор.

Автор: EvilsInterrupt 24.6.2010, 10:43
mes, Ну  по теме с конструктором, получил рекомендации создать класс хэндла, который будет создаваться в качестве приватного члена основного класса. Однако я не хочу,эту детализацию выкидывать в хидер, даже в виде декларации класса! Поэтому хочу создать pimpl и уже в файле реализации привести полную декларацию. У меня получается с указателями на форвард класс и дальнейшей его декларацией в файле реализации, но не получается с использованием std::auto_ptr.

>>я говорил про деструктор для CMain1.
сделал. но та же самая ошибка! Да и цель-то, сделать открытие в конструкторе, а освобождение в деструкторе и при этом не вынося декларацию класса CPrivate1 в основной инклуд

Автор: mes 24.6.2010, 10:46
Цитата(EvilsInterrupt @  24.6.2010,  09:43 Найти цитируемый пост)
Да и цель-то, сделать открытие в конструкторе, а освобождение в деструкторе и при этом не вынося декларацию класса CPrivate1 в основной инклуд

Покажите что сделали.. а то что то не вяжется smile

Автор: EvilsInterrupt 24.6.2010, 11:02
>>Покажите что сделали.. а то что то не вяжется
В хидере:
Код

namespace hdr {

  class CMain1 {
  public:
    CMain1();
    ~CMain1() {}
  private:
    std::auto_ptr<class CPrivate1> m_Ptr;
  };

} /* namespace hdr { */


в файле-реализации:
Код

  class CMain1::CPrivate1 {
  public:
    CPrivate1() : m_val(0)  { 
      M_MSG(m_val,"CPrivate1 constructor");
      m_val++;
    }
    ~CPrivate1()  {
      M_MSG(m_val,"CPrivate1 destructor"); 
      m_val++;
    }
  private:
    int m_val;
  };

  CMain1::CMain1() : m_Ptr(new CPrivate1)
  {
  }


В реальном проекте, не  в этом пустышке заместо "m_val(0)" идет открытие OpenSCManagerA, а в деструкторе CloseServiceHandle(m_hSCMngr).

Автор: mes 24.6.2010, 11:10
Цитата(mes @  24.6.2010,  08:59 Найти цитируемый пост)
попробуйте пустой деструктор с определением в cpp добавить... 


Цитата(EvilsInterrupt @  24.6.2010,  10:02 Найти цитируемый пост)
  ~CMain1() {}

 smile  smile  smile 

Автор: EvilsInterrupt 24.6.2010, 11:15
>>~CMain1() {}
почему вызвало удивление ?

Сейчас перенес в реализацию, думал роли не играет, и все ок. но  почему?

Автор: mes 24.6.2010, 11:19
Цитата(mes @  24.6.2010,  09:22 Найти цитируемый пост)
и еще раз внимательно перечитайте http://forum.vingrad.ru/index.php?showtopic=304001&view=findpost&p=2174264
до понимания происходящего.


P.S. чего то ссылка не работает..
имелся ввиду этот пост :
Цитата

Цитата(EvilsInterrupt)

Но, давайте взглянем на std::auto_ptr:

ну а теперь подумайте, где в первом примере формируется деструктор у auto_ptr в котором вызыватся delete содержимого.. 


Добавлено @ 08:52
Цитата(EvilsInterrupt)

разве что в деструктор ~CMain() добавил удаление m_Private


во во.. ) а теперь перенесите его внутрь класса  


Автор: baldina 24.6.2010, 11:21
потому что именно в деструкторе ~CMain1() вызывается деструктор auto_ptr и вот в этой точке нужно полное определение CPrivate1, что бы правильно вызвать его деструктор.
т.е. CPrivate1 не надо показывать всем, просто функции, его использующие (в т.ч. ~CMain1()) должны быть в единице компиляции, где он известен

Автор: EvilsInterrupt 24.6.2010, 11:24
baldina, Теперь понял, млин,  действительно все логично и никакого колдовства. А я уж было пожалел, что забыл бубен в отчем доме ;)))

Автор: mes 24.6.2010, 11:29
baldina, ну вот, не дали человеку немного подумать и самому разобраться smile преподнесли на блюдечке ..
 smile  smile 

Автор: EvilsInterrupt 24.6.2010, 11:31
mes, ;) Вредный ты ;) Но  спасибо огромное )

Автор: baldina 24.6.2010, 11:59
mes, ну вопрос то не такой простой, как кажется, когда уже знаешь ответ...
а то еще подумает человек, что в С++ все через ж)))

Автор: mes 24.6.2010, 12:26
Цитата(EvilsInterrupt @  24.6.2010,  10:31 Найти цитируемый пост)
mes, ;) Вредный ты ;)

так я как лучше хотел 
smile
Цитата(baldina @  24.6.2010,  10:59 Найти цитируемый пост)
, ну вопрос то не такой простой, как кажется,

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

Цитата(baldina @  24.6.2010,  10:59 Найти цитируемый пост)
а то еще подумает человек, что в С++ все через ж))) 

ну да, пусть знает, что не всё, однако ..
 smile 

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