Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Ownership smart pointers

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 16. Boost.Interprocess

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

Пользователи C++ знают важность умных указателей собственности при работе с ресурсами. Широкий ассортимент таких указателей:<intrusive_ptr<>>,<scoped_ptr<>>,<shared_ptr<>>...

При создании сложных структур файлов с общей памятью / памятью программисты хотели бы использовать преимущества этих интеллектуальных указателей. Проблема в том, что умные указатели Boost и C++ TR1 не готовы к использованию для общей памяти. Причина в том, что эти умные указатели содержат необработанные указатели и используют виртуальные функции, что невозможно, если вы хотите разместить свои данные в общей памяти. Ограничение виртуальной функции делает даже невозможным достижение того же уровня функциональности Boost и TR1 сBoost.Interprocess.Умные указатели.

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

<boost::interprocess::intrusive_ptr>является обобщением<boost::intrusive_ptr<>>, чтобы позволить несырьевые указатели в качестве навязчивых указателей. Как известно<boost::intrusive_ptr>, мы должны указать тип указателя, но мы также должны указать тип указателя, который будет храниться в intrusive_ptr:

//!The intrusive_ptr class template stores a pointer to an object
//!with an embedded reference count. intrusive_ptr is parameterized on
//!T (the type of the object pointed to) and VoidPointer(a void pointer type
//!that defines the type of pointer that intrusive_ptr will store).
//!intrusive_ptr<T, void *> defines a class with a T* member whereas
//!intrusive_ptr<T, offset_ptr<void> > defines a class with a offset_ptr<T> member.
//!Relies on unqualified calls to:
//!
//!void intrusive_ptr_add_ref(T * p);
//!void intrusive_ptr_release(T * p);
//!
//!with (p != 0)
//!
//!The object is responsible for destroying itself.
template<class T, class VoidPointer>
class intrusive_ptr;

<boost::interprocess::intrusive_ptr<MyClass,void*>>равнозначно<boost::intrusive_ptr<MyClass>>. Но если мы хотим поместить intrusive_ptr в общую память, мы должны указать относительный тип указателя, как<boost::interprocess::intrusive_ptr<MyClass,boost::interprocess::offset_ptr<void> >>.

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/smart_ptr/intrusive_ptr.hpp>
using namespace boost::interprocess;
namespace N {
//A class that has an internal reference count
class reference_counted_class
{
   private:
   //Non-copyable
   reference_counted_class(const reference_counted_class  &);
   //Non-assignable
   reference_counted_class & operator=(const reference_counted_class &);
   //A typedef to save typing
   typedef managed_shared_memory::segment_manager segment_manager;
   //This is the reference count
   unsigned int m_use_count;
   //The segment manager allows deletion from shared memory segment
   offset_ptr<segment_manager> mp_segment_manager;
   public:
   //Constructor
   reference_counted_class(segment_manager *s_mngr)
   : m_use_count(0), mp_segment_manager(s_mngr){}
   //Destructor
   ~reference_counted_class(){}
   public:
   //Returns the reference count
   unsigned int use_count() const
   {  return m_use_count;   }
   //Adds a reference
   inline friend void intrusive_ptr_add_ref(reference_counted_class * p)
   {  ++p->m_use_count; }
   //Releases a reference
   inline friend void intrusive_ptr_release(reference_counted_class * p)
   {  if(--p->m_use_count == 0)  p->mp_segment_manager->destroy_ptr(p); }
};
}  //namespace N {
//A class that has an intrusive pointer to reference_counted_class
class intrusive_ptr_owner
{
   typedef intrusive_ptr<N::reference_counted_class,
                           offset_ptr<void> > intrusive_ptr_t;
   intrusive_ptr_t m_intrusive_ptr;
   public:
   //Takes a pointer to the reference counted class
   intrusive_ptr_owner(N::reference_counted_class *ptr)
      : m_intrusive_ptr(ptr){}
};
int main()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory shmem(create_only, "MySharedMemory", 10000);
   //Create the unique reference counted object in shared memory
   N::reference_counted_class *ref_counted =
      shmem.construct<N::reference_counted_class>
         ("ref_counted")(shmem.get_segment_manager());
   //Create an array of ten intrusive pointer owners in shared memory
   intrusive_ptr_owner *intrusive_owner_array =
      shmem.construct<intrusive_ptr_owner>
         (anonymous_instance)[10](ref_counted);
   //Now test that reference count is ten
   if(ref_counted->use_count() != 10)
      return 1;
   //Now destroy the array of intrusive pointer owners
   //This should destroy every intrusive_ptr and because of
   //that reference_counted_class will be destroyed
   shmem.destroy_ptr(intrusive_owner_array);
   //Now the reference counted object should have been destroyed
   if(shmem.find<intrusive_ptr_owner>("ref_counted").first)
      return 1;
   //Success!
   return 0;
}

<boost::interprocess::scoped_ptr<>>является старшим братом<boost::scoped_ptr<>>, который добавляет пользовательский удалитель, чтобы указать, как указатель, переданный в scoped_ptr, должен быть уничтожен. Кроме того,<pointer>typedef удалителя будет указывать тип указателя, сохраненный посредством scoped_ptr.

//!scoped_ptr stores a pointer to a dynamically allocated object.
//!The object pointed to is guaranteed to be deleted, either on destruction
//!of the scoped_ptr, or via an explicit reset. The user can avoid this
//!deletion using release().
//!scoped_ptr is parameterized on T (the type of the object pointed to) and
//!Deleter (the functor to be executed to delete the internal pointer).
//!The internal pointer will be of the same pointer type as typename
//!Deleter::pointer type (that is, if typename Deleter::pointer is
//!offset_ptr<void>, the internal pointer will be offset_ptr<T>).
template<class T, class Deleter>
class scoped_ptr;

<scoped_ptr<>>пригодится для реализацииоткатовс исключениями: если отбрасывается исключение или мы называем<return>в рамках<scoped_ptr<>>, то автоматически вызывается удаляющий, так чтоудаляющий может рассматриваться как функция отката. Если все пойдет хорошо, мы вызовем<release()>функцию члена, чтобы избежать отката, когда<scoped_ptr>выходит из области применения.

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/smart_ptr/scoped_ptr.hpp>
using namespace boost::interprocess;
class my_class
{};
class my_exception
{};
//A functor that destroys the shared memory object
template<class T>
class my_deleter
{
   private:
   //A typedef to save typing
   typedef managed_shared_memory::segment_manager segment_manager;
   //This my_deleter is created in the stack, not in shared memory,
   //so we can use raw pointers
   segment_manager *mp_segment_manager;
   public:
   //This typedef will specify the pointer type that
   //scoped_ptr will store
   typedef T *pointer;
   //Constructor
   my_deleter(segment_manager *s_mngr)
   : mp_segment_manager(s_mngr){}
   void operator()(pointer object_to_delete)
   {  mp_segment_manager->destroy_ptr(object_to_delete);  }
};
int main ()
{
   //Create shared memory
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   managed_shared_memory shmem(create_only, "MySharedMemory", 10000);
   //In the first try, there will be no exceptions
   //in the second try we will throw an exception
   for(int i = 0; i < 2; ++i){
      //Create an object in shared memory
      my_class * my_object = shmem.construct<my_class>("my_object")();
      my_class * my_object2 = shmem.construct<my_class>(anonymous_instance)();
      shmem.destroy_ptr(my_object2);
      //Since the next shared memory allocation can throw
      //assign it to a scoped_ptr so that if an exception occurs
      //we destroy the object automatically
      my_deleter<my_class> d(shmem.get_segment_manager());
      try{
         scoped_ptr<my_class, my_deleter<my_class> > s_ptr(my_object, d);
         //Let's emulate a exception capable operation
         //In the second try, throw an exception
         if(i == 1){
            throw(my_exception());
         }
         //If we have passed the dangerous zone
         //we can release the scoped pointer
         //to avoid destruction
         s_ptr.release();
      }
      catch(const my_exception &){}
      //Here, scoped_ptr is destroyed
      //so it we haven't thrown an exception
      //the object should be there, otherwise, destroyed
      if(i == 0){
         //Make sure the object is alive
         if(!shmem.find<my_class>("my_object").first){
            return 1;
         }
         //Now we can use it and delete it manually
         shmem.destroy<my_class>("my_object");
      }
      else{
         //Make sure the object has been deleted
         if(shmem.find<my_class>("my_object").first){
            return 1;
         }
      }
   }
   return 0;
}

Boost.Interprocessтакже предлагает возможность создания неинтрузивных объектов с учётом ссылок в управляемой совместно используемой памяти или отображенных файлах.

В отличие отboost::shared_ptr, из-за ограничений отображенных сегментов<boost::interprocess::shared_ptr>не может воспользоваться виртуальными функциями для поддержания одного и того же типа общих указателей, обеспечивая при этом определяемые пользователем распределители и удаляющие устройства. Аллокатор и удалятель являются шаблонными параметрами общего указателя.

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

Вот ссылка<shared_ptr>:

template<class T, class VoidAllocator, class Deleter>
class shared_ptr;
  • Т — тип заостренного типа.
  • VoidAllocator - это распределитель, который используется для выделения вспомогательных элементов, таких как количество ссылок, удаляющий элемент. Внутренний<pointer>типдеф распределителя будет определять тип указателя, который будет использоваться внутри Shared_ptr, поэтому распределители, определяющие<pointer>как<offset_ptr<void>>, сделают все внутренние указатели, используемые<shared_ptr>, также относительными указателями. См.<boost::interprocess::allocator>для рабочего распределителя.
  • Удаление - это функциональный объект, который будет использоваться для уничтожения заостренного объекта при уничтожении последней ссылки на объект. Удаляющий функтор принимает указатель на T той же категории, что и указатель пустоты, определенный<VoidAllocator::pointer>. См.<boost::interprocess::deleter>для общего удаляющего устройства, которое стирает объект из управляемого сегмента.

При правильно заданных параметрахBoost.Interprocessпользователи могут создавать объекты в общей памяти, которые содержат общие указатели, указывающие на другие объекты также в общей памяти, получая преимущества подсчета ссылок. Давайте посмотрим, как создать общий указатель в управляемой общей памяти:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/smart_ptr/deleter.hpp>
#include <cassert>
using namespace boost::interprocess;
//This is type of the object we want to share
class MyType
{};
typedef managed_shared_memory::segment_manager segment_manager_type;
typedef allocator<void, segment_manager_type>  void_allocator_type;
typedef deleter<MyType, segment_manager_type>  deleter_type;
typedef shared_ptr<MyType, void_allocator_type, deleter_type> my_shared_ptr;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   managed_shared_memory segment(create_only, "MySharedMemory", 4096);
   //Create a shared pointer in shared memory
   //pointing to a newly created object in the segment
   my_shared_ptr &shared_ptr_instance =
      *segment.construct<my_shared_ptr>("shared ptr")
         //Arguments to construct the shared pointer
         ( segment.construct<MyType>("object to share")()      //object to own
         , void_allocator_type(segment.get_segment_manager())  //allocator
         , deleter_type(segment.get_segment_manager())         //deleter
         );
   assert(shared_ptr_instance.use_count() == 1);
   //Destroy "shared ptr". "object to share" will be automatically destroyed
   segment.destroy_ptr(&shared_ptr_instance);
   return 0;
}

<boost::interprocess::shared_ptr>очень гибкий и настраиваемый (мы можем указать, например, распределитель и удалитель), но, как показано, создание общего указателя в управляемых сегментах требует слишком большого набора текста.

Для упрощения этого использования заголовок<boost::interprocess::shared_ptr>предлагает общий класс помощников определения указателя (<managed_shared_ptr>) и функцию (<make_managed_shared_ptr>), чтобы легко построить общий указатель из типа, выделенного в управляемом сегменте, с распределителем, который будет распределять количество ссылок также в управляемом сегменте, и удаляющим устройством, которое будет стирать объект из сегмента.

Для этих целей используется.Boost.Interprocessallocator<boost::interprocess::allocator>и deleter<boost::interprocess::deleter>для выполнения своей работы. Определение предыдущего общего указателя можно упростить следующим образом:

typedef managed_shared_ptr<MyType, managed_shared_memory>::type my_shared_ptr;

И создание общего указателя можно упростить до этого:

my_shared_ptr sh_ptr = make_managed_shared_ptr
   (segment.construct<MyType>("object to share")(), segment);

Boost.Interprocessтакже предлагает слабый указатель<weak_ptr>(с его соответствующими<managed_weak_ptr>и<make_managed_weak_ptr>утилитами) для реализации несобственных наблюдателей объекта, принадлежащего<shared_ptr>.

#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/smart_ptr/weak_ptr.hpp>
#include <cassert>
using namespace boost::interprocess;
//This is type of the object we want to share
struct type_to_share
{};
//This is the type of a shared pointer to the previous type
//that will be built in the mapped file
typedef managed_shared_ptr<type_to_share, managed_mapped_file>::type shared_ptr_type;
typedef managed_weak_ptr<type_to_share, managed_mapped_file>::type   weak_ptr_type;
//This is a type holding a shared pointer
struct shared_ptr_owner
{
   shared_ptr_owner(const shared_ptr_type &other_shared_ptr)
      : shared_ptr_(other_shared_ptr)
   {}
   shared_ptr_owner(const shared_ptr_owner &other_owner)
      : shared_ptr_(other_owner.shared_ptr_)
   {}
   shared_ptr_type shared_ptr_;
   //...
};
int main ()
{
   //Define file names
   const char *MappedFile  = "MyMappedFile";
   //Destroy any previous file with the name to be used.
   struct file_remove
   {
      file_remove(const char *MappedFile)
         : MappedFile_(MappedFile) { file_mapping::remove(MappedFile_); }
      ~file_remove(){ file_mapping::remove(MappedFile_); }
      const char *MappedFile_;
   } remover(MappedFile);
   {
      managed_mapped_file file(create_only, MappedFile, 65536);
      //Construct the shared type in the file and
      //pass ownership to this local shared pointer
      shared_ptr_type local_shared_ptr = make_managed_shared_ptr
         (file.construct<type_to_share>("object to share")(), file);
      assert(local_shared_ptr.use_count() == 1);
      //Share ownership of the object between local_shared_ptr and a new "owner1"
      shared_ptr_owner *owner1 =
         file.construct<shared_ptr_owner>("owner1")(local_shared_ptr);
      assert(local_shared_ptr.use_count() == 2);
      //local_shared_ptr releases object ownership
      local_shared_ptr.reset();
      assert(local_shared_ptr.use_count() == 0);
      assert(owner1->shared_ptr_.use_count() == 1);
      //Share ownership of the object between "owner1" and a new "owner2"
      shared_ptr_owner *owner2 =
         file.construct<shared_ptr_owner>("owner2")(*owner1);
      assert(owner1->shared_ptr_.use_count() == 2);
      assert(owner2->shared_ptr_.use_count() == 2);
      assert(owner1->shared_ptr_.get() == owner2->shared_ptr_.get());
      //The mapped file is unmapped here. Objects have been flushed to disk
   }
   {
      //Reopen the mapped file and find again all owners
      managed_mapped_file file(open_only, MappedFile);
      shared_ptr_owner *owner1 = file.find<shared_ptr_owner>("owner1").first;
      shared_ptr_owner *owner2 = file.find<shared_ptr_owner>("owner2").first;
      assert(owner1 && owner2);
      //Check everything is as expected
      assert(file.find<type_to_share>("object to share").first != 0);
      assert(owner1->shared_ptr_.use_count() == 2);
      assert(owner2->shared_ptr_.use_count() == 2);
      assert(owner1->shared_ptr_.get() == owner2->shared_ptr_.get());
      //Now destroy one of the owners, the reference count drops.
      file.destroy_ptr(owner1);
      assert(owner2->shared_ptr_.use_count() == 1);
      //Create a weak pointer
      weak_ptr_type local_observer1(owner2->shared_ptr_);
      assert(local_observer1.use_count() == owner2->shared_ptr_.use_count());
      {  //Create a local shared pointer from the weak pointer
      shared_ptr_type local_shared_ptr = local_observer1.lock();
      assert(local_observer1.use_count() == owner2->shared_ptr_.use_count());
      assert(local_observer1.use_count() == 2);
      }
      //Now destroy the remaining owner. "object to share" will be destroyed
      file.destroy_ptr(owner2);
      assert(file.find<type_to_share>("object to share").first == 0);
      //Test observer
      assert(local_observer1.expired());
      assert(local_observer1.use_count() == 0);
      //The reference count will be deallocated when all weak pointers
      //disappear. After that, the file is unmapped.
   }
   return 0;
}

В целом, использованиеBoost.Interprocess<shared_ptr>и<weak_ptr>очень похоже на их аналогиboost::shared_ptrиboost::weak_ptr, но им нужно больше параметров шаблона и больше параметров времени выполнения в своих конструкторах.

Так же, какboost::shared_ptrможет храниться в STL-контейнере,<shared_ptr>также может храниться вBoost.Interprocessконтейнерах.

Если программист просто использует<shared_ptr>, чтобы иметь возможность вставлять динамически сконструированные объекты в контейнер, сконструированный в управляемом сегменте, но ему не нужно делить право собственности на этот объект с другими объектами<managed_unique_ptr>, это гораздо быстрее и проще использовать альтернативу.

Уникальные умные указатели собственности действительно полезны для свободных программистов от ручного освобождения ресурсов необщих объектов.Boost.Interprocess'<unique_ptr>очень похож на<scoped_ptr>, но онподвижныйи может быть легко вставлен вBoost.Interprocessконтейнеры. Интерпроцесс имел свою собственную реализацию<unique_ptr>, но от Boost 1.57Boost.Interprocessиспользует улучшенную и общую реализацию<boost::unique_ptr>. Вот декларация уникального класса указателей:

template <class T, class D>
class unique_ptr;
  • Т — тип объекта, на который указывает<unique_ptr>.
  • offset_ptr<T>Уникальный хелдон, относящийся к сырому сыру. многозначительныйunique_ptrunique_ptr, , , , , , , , .[ORIG_END] -->

<unique_ptr>может освободить право собственности на сохраненный указатель, поэтому его полезно также использовать в качестве функции отката. Одним из основных свойств класса является то, чтоне копируемый, а только подвижный. Когда уникальный указатель перемещается в другой, право собственности на указатель передается от уникального указателя источника к уникальному указателю цели. Если целевому уникальному указателю принадлежал объект, этот объект сначала удаляется, прежде чем он становится владельцем нового объекта.

Boost.Interprocessтакже предлагает вспомогательные типы для легкого определения и построения уникальных указателей, которые могут быть размещены в управляемых сегментах и будут правильно удалять принадлежащий объект из сегмента:<managed_unique_ptr>и<make_managed_unique_ptr>утилиты.

Здесь мы видим пример использования<unique_ptr>в том числе создания контейнеров таких объектов:

#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/smart_ptr/unique_ptr.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/list.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <cassert>
using namespace boost::interprocess;
//This is type of the object we'll allocate dynamically
struct MyType
{
   MyType(int number = 0)
      :  number_(number)
   {}
   int number_;
};
//This is the type of a unique pointer to the previous type
//that will be built in the mapped file
typedef managed_unique_ptr<MyType, managed_mapped_file>::type unique_ptr_type;
//Define containers of unique pointer. Unique pointer simplifies object management
typedef vector
   < unique_ptr_type
   , allocator<unique_ptr_type, managed_mapped_file::segment_manager>
   > unique_ptr_vector_t;
typedef list
   < unique_ptr_type
   , allocator<unique_ptr_type, managed_mapped_file::segment_manager>
   > unique_ptr_list_t;
int main ()
{
   //Define file names
   const char *MappedFile  = "MyMappedFile";
   //Destroy any previous file with the name to be used.
   struct file_remove
   {
      file_remove(const char *MappedFile)
         : MappedFile_(MappedFile) { file_mapping::remove(MappedFile_); }
      ~file_remove(){ file_mapping::remove(MappedFile_); }
      const char *MappedFile_;
   } remover(MappedFile);
   {
      managed_mapped_file file(create_only, MappedFile, 65536);
      //Construct an object in the file and
      //pass ownership to this local unique pointer
      unique_ptr_type local_unique_ptr (make_managed_unique_ptr
         (file.construct<MyType>("unique object")(), file));
      assert(local_unique_ptr.get() != 0);
      //Reset the unique pointer. The object is automatically destroyed
      local_unique_ptr.reset();
      assert(file.find<MyType>("unique object").first == 0);
      //Now create a vector of unique pointers
      unique_ptr_vector_t *unique_vector =
         file.construct<unique_ptr_vector_t>("unique vector")(file.get_segment_manager());
      //Speed optimization
      unique_vector->reserve(100);
      //Now insert all values
      for(int i = 0; i < 100; ++i){
         unique_ptr_type p(make_managed_unique_ptr(file.construct<MyType>(anonymous_instance)(i), file));
         unique_vector->push_back(boost::move(p));
         assert(unique_vector->back()->number_ == i);
      }
      //Now create a list of unique pointers
      unique_ptr_list_t *unique_list =
         file.construct<unique_ptr_list_t>("unique list")(file.get_segment_manager());
      //Pass ownership of all values to the list
      for(int i = 99; !unique_vector->empty(); --i){
         unique_list->push_front(boost::move(unique_vector->back()));
         //The unique ptr of the vector is now empty...
         assert(unique_vector->back() == 0);
         unique_vector->pop_back();
         //...and the list has taken ownership of the value
         assert(unique_list->front() != 0);
         assert(unique_list->front()->number_ == i);
      }
      assert(unique_list->size() == 100);
      //Now destroy the empty vector.
      file.destroy_ptr(unique_vector);
      //The mapped file is unmapped here. Objects have been flushed to disk
   }
   {
      //Reopen the mapped file and find again the list
      managed_mapped_file file(open_only, MappedFile);
      unique_ptr_list_t   *unique_list =
         file.find<unique_ptr_list_t>("unique list").first;
      assert(unique_list);
      assert(unique_list->size() == 100);
      unique_ptr_list_t::const_iterator list_it = unique_list->begin();
      for(int i = 0; i < 100; ++i, ++list_it){
         assert((*list_it)->number_ == i);
      }
      //Now destroy the list. All elements will be automatically deallocated.
      file.destroy_ptr(unique_list);
   }
   return 0;
}


PrevUpHomeNext

Статья Ownership smart pointers раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 16. Boost.Interprocess может быть полезна для разработчиков на c++ и boost.




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



:: Главная :: Chapter 16. Boost.Interprocess ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-04 23:03:36/0.011584997177124/0