Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Разработка многопоточных объектно-ориентированных, совместимы ли ООП и многопоточность?  
:(
    Опции темы
multikoder
Дата 2.4.2014, 10:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 3
Регистрация: 2.4.2014

Репутация: нет
Всего: нет



Почему то редко встречается такое сочетание. 
Обычно пишут по отдельности или многопоточных или объектно-ориентированных.
Какие принципы ООП нарушаются в многопоточных программах?
Можно ли писать многопоточные программы полностью соответствующие ООП?  
PM MAIL   Вверх
Cheloveck
Дата 2.4.2014, 10:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

Репутация: 3
Всего: 32



Цитата(multikoder @  2.4.2014,  11:32 Найти цитируемый пост)
Обычно пишут по отдельности или многопоточных или объектно-ориентированных.

 smile Что? Откуда такая бредовая информация?


--------------------
user posted image
PM Jabber   Вверх
Romikgy
Дата 2.4.2014, 10:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Любитель-программер
****


Профиль
Группа: Участник Клуба
Сообщений: 7326
Регистрация: 11.5.2005
Где: Porto Franco Odes sa

Репутация: 8
Всего: 146



грубо говоря поток это упрощенный вариант процесса , в процессах никто не запрещает ООП....


--------------------
Владение русской орфографией это как владение кунг-фу — истинные мастера не применяют его без надобности. 
smile

PM   Вверх
multikoder
Дата 2.4.2014, 11:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 3
Регистрация: 2.4.2014

Репутация: нет
Всего: нет



Процессы это отдельные программы, которые ничего друг про друга не знают.
У потоков могут быть общие переменные, через которые они взаимодействуют. И это похоже на нарушение инкапсуляции.
PM MAIL   Вверх
Alexeis
Дата 2.4.2014, 11:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 12
Всего: 459



  ООП и многопоточность как соленое и серое. Но с точки зрения С++ многопоточность усложняет написание ООП программ. Считается, что объект каждый момент времени находиться в некотором определенном состоянии, а переход объекта из одного состояния в другое осуществляется мгновенно путем отсылки ему сообщения. Состояние объекта, грубо говоря, это значение всех его полей. На практике на время вызова функции (обработка сообщения) объект находиться в некотором неопределенном состоянии (невозможно одновременно изменить состояние множества полей). Для однопоточной программы нахождение в неопределенном состоянии не проблема, поскольку пока функция не выполнилась до конца другая не может исполнятся. В многопоточной же программе объект может начать обработку сообщения (вызов функции) в тот момент когда он (объект) еще находится в неопределенном состоянии. Это и станет нарушением концепции ООП. Поэтому многопоточные ООП программы должны использовать мьютексы для того, чтобы исключить неопределенное состояние. Т.е. пока не будет произведен захват мьютекса нельзя будет менять состояние объекта. 


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
Cheloveck
Дата 2.4.2014, 12:18 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1578
Регистрация: 26.7.2008
Где: Тула

Репутация: 3
Всего: 32



Alexeis, всё это верно для любой структуры данных, даже для массива. Так что ООП тут не при чём.


--------------------
user posted image
PM Jabber   Вверх
Alexeis
Дата 2.4.2014, 14:05 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 12
Всего: 459



Цитата(Cheloveck @  2.4.2014,  13:18 Найти цитируемый пост)
Alexeis, всё это верно для любой структуры данных, даже для массива. Так что ООП тут не при чём. 

  Несмотря на то, что ООП и многопоточность с точки зрения теории это как серое и соленое. Т.е. понятия несвязанные, но тем не менее, на практике, в частности в реализации С++ многопоточность рушит типичный механизм изменения состояния объекта (невозможно записывать данные в поля напрямую). В тоже время как любая произвольно выбранная структура или массив не описывает состояние целиком, поэтому вообщем случае нельзя говорить о неопределенности состояния в случае простых структур данных. У меня, например, есть класс массива, который без блокировки может безопасно производить одновременно 2е операции записи в массив. Именно по той причине, что один массив это еще не состояние. Проблемы начинаются у тебя как у программиста, когда ты у себя в голове рассматриваешь массив как объект подобный объекту stl vector. Но на самом массив это лишь последовательность элементов объединенная одним именем. 
  Я могу рассматривать любую последовательность элементов как некоторое состояние массива. В этом случае, массив в любой момент времени находиться в некотором определенном состоянии. Следовательно, любые операции с ним из любых потоков также будут корректно менять его состояние. 



--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
baldina
Дата 2.4.2014, 14:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



multikoder, они ортогональны, одно другому не мешает. темы сильно разные (разного уровня).
можно использовать совместно, можно раздельно, можно вообще не использовать))))

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

PM MAIL   Вверх
Alexeis
Дата 2.4.2014, 18:11 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 12
Всего: 459



Цитата(baldina @  2.4.2014,  15:55 Найти цитируемый пост)
Alexeis, ООП и многопоточность не противопоставлены друг другу, не надо выдумывать про практику. многопоточность изменяет подход к алгоритмизации в целом (независимо от использования ООП), требует обеспечения определенных условий, но и только.

  Меняет ли что-то многопоточность если задача решена при помощи чистых функций? 

Цитата(baldina @  2.4.2014,  15:55 Найти цитируемый пост)
модель, в которой объект одновременно занимается двумя задачами, вполне реальна. есть и не связанные с многопоточностью условия, которые мы должны соблюдать (в т.ч. используя ООП), просто они настолько привычны, что их выполнение нам кажется обычным делом. распределение памяти, например.

  Модель в которой объект занимается решением задач, а тем более 2х сразу, вообще не ООП модель. Задачи решают функции. Объект концептуально не решает задачи как таковые, он лишь реагирует на сообщения. Задача, процесс и т.д. это не  про ООП. ООП - это сообщения, события (сигналы), состояния. 
  Менеджер памяти 
Состояние №1 - 25Мб выделенной памяти, 47Мб резерва
Поток №1 --> Сообщение №1 запрос 1Мб памяти.
Поток №2 --> Сообщение №2 запрос 2Мб памяти.
Состояние №2 - 26Мб выделенной памяти, 46Мб резерва
Состояние №3 - 28Мб выделенной памяти, 44Мб резерва
  Хотя объект получил 2 сообщения одновременно, он все равно последовательно перешел сначала в состояние №2, затем в состояние №3 . Это совсем плохой пример, поскольку здесь параллельные потоки выполняются по очереди и еще медленнее чем это было бы в однопоточном режиме. 

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

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



--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
baldina
Дата 2.4.2014, 22:10 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



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

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

Цитата(Alexeis @  2.4.2014,  18:11 Найти цитируемый пост)
Модель в которой объект занимается решением задач, а тем более 2х сразу, вообще не ООП модель.

ок, реагирует на следующее сообщение, не закончив обрабатывать предыдущее. стало легче от такой формулировки?  smile 
вот модель. представим объект, решающий систему уравнений. метод solve() меняет состояние объекта: после изменения состояния система решена. такое изменение состояния занимает значительное время. также объект умеет реагировать на сообщение estimate_time(), в качестве реакции на которое он должен вернуть значение времени, через которое система будет решена.

Цитата(Alexeis @  2.4.2014,  18:11 Найти цитируемый пост)
Синтаксис С++ не имеет языковых конструкций позволяющих указать, что группа полей должна изменять свои значения одной атомарной операцией.

да это и не нужно, многопоточность - не часть языка, а часть стандартной библиотеки.
GC тоже нет, неаккуратные указатели могут порушить программу, но никому не приходит в голову говорить, что использование динамической памяти плохо согласуется с ООП.

Добавлено через 5 минут и 48 секунд
Цитата(Alexeis @  2.4.2014,  18:11 Найти цитируемый пост)
Хотя объект получил 2 сообщения одновременно, он все равно последовательно перешел сначала в состояние №2, затем в состояние №3 .

Это совсем плохой пример, поскольку представляет объект как state machine. Это слишком узко. Объект это обобщение SM, а не наоборот. Реальные объекты настолько сложны, что говорить о перечне состояний не приходится. Вместо этого оперируют инвариантами.
PM MAIL   Вверх
Lukkoye
Дата 2.4.2014, 22:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(Alexeis @  2.4.2014,  18:11 Найти цитируемый пост)
В языке С++ для реализации ООП принято, что состояние определяется набором переменных-членов класса. Посылка сообщения реализуется через вызов функции. Синтаксис С++ не имеет языковых конструкций позволяющих указать, что группа полей должна изменять свои значения одной атомарной операцией. 


Плюсы - язык, расширяемый за счет собственных библиотек.
То бишь отсутствие "синтаксического сахара" от компилятора не означает, что на плюсах нельзя пилить атомарные операции.

В частности см:

Стандартная поддержка атомарных операций:
http://en.cppreference.com/w/cpp/atomic/atomic

Стандартные средства для поддержки многопоточного программирования:
http://en.cppreference.com/w/cpp/thread/unique_lock
http://en.cppreference.com/w/cpp/thread/mutex

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

Цитата(Alexeis @  2.4.2014,  18:11 Найти цитируемый пост)

Параллелизм С++ ООП реализуется не проще чем на языке С , т.е. отсутствует напрочь.


Неправда.
Стандартные средства многопоточного программирования предлагают использовать технологию raii

Например, вот так можно обеспечить "простой потока" пока не другой поток не завершит какую то свою работу:

Код

//где-то внутри функции
...

// <--- остановка на мьютексе и ожидание
// при этом время ожидающего потока будет рационально распределено
// между остальными действующими потоками

std::unique_lock<std::mutex> lock(mGuard); // std::mutex mGuard;
mRecieved.wait(lock);                                     // std::condition_variable;

//<--- вот здесь поток активизировался и продолжил работу




Поток пробудил ото сна другой поток, который выполнил:

Код

// соседний поток

...
//что-то сделал, и послал сигнал всем спящим-но-заинтересованным
//что пора просыпаться:

mRecieved.notify_all(); // std::condition_variable;

...



Это лишь краткий пример автоматики, которая возможно благодаря raii, который возможен благодаря ООП.
На pure С сделать что-то подобное невозможно.

Цитата(Alexeis @  2.4.2014,  18:11 Найти цитируемый пост)

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


Qt умеет это так же за счет библиотек языка, а не за счет ядра языка с++, на котором он базируется.

Кютешечный мьютекс, или стандартный мьютекс - это всего лишь деталь библиотеки.




Это сообщение отредактировал(а) Lukkoye - 2.4.2014, 22:37
PM MAIL   Вверх
baldina
Дата 3.4.2014, 00:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(Lukkoye @  2.4.2014,  22:33 Найти цитируемый пост)
На pure С сделать что-то подобное невозможно.

на С все возможно, только многословнее. raii особенно удобен в среде с исключениями, но в С нет исключений, и в этом смысле проблем меньше.
raii часто ошибочно связывают с ООП, но фактически к ООП он не относится. просто некоторые средства некоторых языков с поддержкой ООП позволяют это изящно реализовать.
PM MAIL   Вверх
Alexeis
Дата 3.4.2014, 16:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 12
Всего: 459



Цитата(baldina @  2.4.2014,  23:10 Найти цитируемый пост)
за семантику и согласованность операций класса с++ отвечает программист, и неважно, в одно- или многопоточной среде это реализуется. в с++ нет типичного механизма изменения состояния объекта, он должен быть разработан для конкретного класса.
поэтому говорить, что некий типичный механизм что-то там рушит - неверно.
ООП это действительно объекты, имеющие состояние и обменивающиеся сообщениями. а раз в концепции нет "решения задач", то разговор о свойстве решения задач, в т.ч. многопоточности, не имеет смысла. алгоритмы и их реализация не пересекаются с ООП.

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

struct A
{
  int a;
};

struct B
{
  A m_a;
  int b;
};

Объект B унаследовал все из объекта А, и дополнил его новым полем b;
Теперь возьмем язык С++ .
Код

class A
{ public:
  int a;
};

class B : public A
{ public:
  int b;
};

  С точки зрения концепции ООП оба способа реализуют наследование, однако язык С++ указывает нам на то что наследование следует реализовывать именно 2м способом, а не первым. Теперь подумаем как можно сохранить состояние объекта. Инкапсуляция. Можно это сделать так.
Код

struct A
{
  char wndsize[16]; // bytes 0..3 top, bytes 4..7 left, 8..11, right 12..15 bottom
};

А можно сделать это так. 
Код

class A
{
protected: 
  RECT wndsize;
public:
  void Getwndsize(RECT &r){r = wndsize;};
  void Setwndsize(const RECT &r){wndsize = r;};
};

И тот и другой способ хранит состояние внутри объекта. Но в одном случае это некоторый набор байтов, который можно испортить неправильным кодом. Во втором случае состояние хранит вложенный объект, доступ к которому ограничивается специальным средством языка - модификатором protected . Модификатор protected/private - это типичный способ реализации инкапсуляции состояния объекта в С++ . Его использование подразумевает то, что поля будут описываться следом за ним. Он защищает поля от посягательства других объектов. Использование 1го варианта кода допустимо с точки зрения С++, но не типично для этого ЯП.

И 3я ситуация. Многопоточное ООП.
Код

class A
{
protected: 
  RECT wndsize;
  mutex mtx_wndsize;
public:
  void Getwndsize(RECT &r)
{
  mtx_wndsize.lock();
    r = wndsize;
  mtx_wndsize.unlock();
};
  void Setwndsize(const RECT &r)
{
  mtx_wndsize.lock();
   wndsize = r;
  mtx_wndsize.unlock();
};
};

2й правильный вариант ????


Сохраняется ли правильное состояние объекта А при любых операциях? Да! Защищает ли хоть как-то средство С++ состояние объекта А от одновременно полученных сообщений? Нет!
С точки зрения потокозащищенности состояния объекта данный код ничем не безопаснее чем.

Код

struct A
{
  RECT wndsize;
  STRUCT_MUTEX mtx_wndsize;
};

A va;

void InitA(A *a)
{
   memset(a, 0, sizeof(A));
   Init_Mutex(a->mtx_wndsize);
}

void Getwndsize(A *a, RECT *r)
{
    LockMutex(a->mtx_wndsize);
   *r = *a->wndsize;
    UnLock_Mutex(a->mtx_wndsize);
}

void Setwndsize(A *a, RECT *r)
{
   LockMutex(a->mtx_wndsize);
   *a->wndsize = *r;
   UnLock_Mutex(a->mtx_wndsize);
}


  Т.е. если я захочу в иной функции написать *a->wndsize = *r; я это сделаю с одинаковым успехом, что в варианте кода С++, что в варианте С . И в обоих случаях у меня появилось лишнее поле mtx_wndsize, которое никак не определяет состояние объекта. В С++ есть средства для реализации только однопоточного ООП.

  Я бы хотел видеть 2й вариант реализации многопоточного ООП на подобии такого :
Код

class RECT
{
public Atom readwrite lock:
  int left;
  int rigth;
  int top;
  int bottom;
public Atom unlock:
};

class A
{
  int     wndcolor;
protected Atom write lock: 
  state wndstate;
  RECT wndsize;
protected Atom unlock:
public:
  void Getwndsize(RECT &r) #allowthreading //без флага - ошибка при вызове в чужом потоке.
  {
    r = wndsize;
  };
  void Setwndsize(const RECT &r) #allowthreading
  {
    wndstate += is_moving;
    wndsize = r;
    view->send(updatepos);
    wndstate -=  is_moving;
  };
};


Все поля объекта хранят исключительно состояние объекта. Правильность состояния объекта гарантируют средства языка. 
 

Это сообщение отредактировал(а) Alexeis - 3.4.2014, 17:09


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
baldina
Дата 3.4.2014, 17:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Alexeis, букв много но ни слова про "типичный способ изменения состояния объекта" (понятно почему - его просто нет. можно с этим не соглашаться, но тогда плиз ссылку на конструкцию, стандарт или хотя бы best practices от известного специалиста)

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

Цитата(Alexeis @  3.4.2014,  16:59 Найти цитируемый пост)
Я бы хотел видеть 2й вариант реализации многопоточного ООП

многопоточное ООП это что-то новое)))) 
правда существует концепция актеров (Actors) - объектов, работающих асинхронно, её можно реализовать многопоточно или кооперативно. но это скорее тема для библиотечного расширения, т.к. представляет частный случай.

кстати есть std::atomic<>

Alexeis, способов выстрелить в ногу в с++ предостаточно и без многопоточности. но это уже было
Цитата(baldina @  2.4.2014,  22:10 Найти цитируемый пост)
GC тоже нет, неаккуратные указатели могут порушить программу, но никому не приходит в голову говорить, что использование динамической памяти плохо согласуется с ООП


PM MAIL   Вверх
k0rvin
Дата 3.4.2014, 18:54 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 442
Регистрация: 24.1.2010

Репутация: 1
Всего: 5



Цитата(Alexeis @  3.4.2014,  16:59 Найти цитируемый пост)
С точки зрения концепции ООП оба способа реализуют наследование

Пример на С реализует агрегирование, а не наследование.

Цитата(Alexeis @  3.4.2014,  16:59 Найти цитируемый пост)
Теперь подумаем как можно сохранить состояние объекта. Инкапсуляция. Можно это сделать так.

В примере нет никакой инкапсуляции. Инкапсуляция в Си делается например так:

counter.h:
Код

typedef struct Counter Counter;

Counter*    Cnew(int init);
void        Cfree(Counter *);
void        Cinc(Counter *);
int         Cget(Counter *);


counter.c:
Код

#include <u.h>
#include <libc.h>
#include "counter.h"

struct Counter
{
    int value;
};

Counter *
Cnew(int init)
{
    Counter *c;
    c = malloc(sizeof(Counter));
    if (c != nil)
        c->value = init;
    return c;
}

void
Cfree(Counter *c)
{
    free(c);
}

void
Cinc(Counter *c)
{
    c->value++;
}

int
Cget(Counter *c)
{
    return c->value;
}


main.c:
Код

#include <u.h>
#include <libc.h>
#include "counter.h"

void
main(void)
{
    Counter *c;

    c = Cnew(0);
    if (c == nil)
        exits("Cnew");

    print("%d -> ", Cget(c));
    Cinc(c);
    Cinc(c);
    print("%d\n", Cget(c));

    // print("%d\n", c->value); // -> error: incomplete definition of type 'struct Counter'

    Cfree(c);
    exits(0);
}


Код

~/prog/c/encaps $ mk encaps
9c main.c
9c counter.c
9l -o encaps main.o counter.o
~/prog/c/encaps $ ./encaps
0 -> 2
~/prog/c/encaps $ 



--------------------
“Object-oriented design is the roman numerals of computing.” — Rob Pike
All software sucks
PM MAIL   Вверх
Lukkoye
Дата 3.4.2014, 22:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(baldina @  3.4.2014,  00:28 Найти цитируемый пост)
на С все возможно, только многословнее. raii особенно удобен в среде с исключениями, но в С нет исключений, и в этом смысле проблем меньше.
raii часто ошибочно связывают с ООП, но фактически к ООП он не относится. просто некоторые средства некоторых языков с поддержкой ООП позволяют это изящно реализовать. 


На сях можно написать язык, который сможет блекджек и компанию.
Но на самих сях нельзя реализовать автоматику raii.

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

И если вы сами явно где нибудь не дернете free, сишный мьютекс так и не пустит поток.




PM MAIL   Вверх
Фантом
Дата 3.4.2014, 22:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Вы это прекратите!
***


Профиль
Группа: Участник Клуба
Сообщений: 1516
Регистрация: 23.3.2008

Репутация: нет
Всего: 49



Цитата(Lukkoye @  3.4.2014,  23:31 Найти цитируемый пост)
Грубо говоря, на сях не получится сделать автоматику, которая самостоятельно освободит указатель, как только закончится его область видимости.

У С все хорошо с полнотой языка, поэтому сделать на нем получится все. Другой вопрос, какой ценой.
PM   Вверх
Lukkoye
Дата 3.4.2014, 22:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(Фантом @  3.4.2014,  22:32 Найти цитируемый пост)
У С все хорошо с полнотой языка, поэтому сделать на нем получится все. Другой вопрос, какой ценой. 


Ну покажите мне, как на pure C реализовать автоматический захват ресурса - допустим, выделить память под структуру.
А потом, когда закончится время жизни указателя - автоматически освободит ресурс.





PM MAIL   Вверх
baldina
Дата 3.4.2014, 22:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Lukkoye, если трактовать raii как паттерн, исключительно основанный на автоматическом вызове конструктора/деструктора, то да. ближе к ООП он от этого не становится
PM MAIL   Вверх
Lukkoye
Дата 3.4.2014, 22:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(Alexeis @  3.4.2014,  16:59 Найти цитируемый пост)
Объект B унаследовал все из объекта А, и дополнил его новым полем b;


Вы путаете наследование и агрегирование.
Наследования на pure C не существует.


PM MAIL   Вверх
baldina
Дата 3.4.2014, 22:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(Lukkoye @  3.4.2014,  22:38 Найти цитируемый пост)
Ну покажите мне, как на pure C реализовать автоматический захват ресурса - допустим, выделить память под структуру.
А потом, когда закончится время жизни указателя - автоматически освободит ресурс

надо просто создать дополнительный контекст, владеющий ресурсом. с точки зрения внутренней функции use() все происходит автоматически 
Код

Type* construct () {
    Type *obj = malloc (sizeof(Type));
        ...
    return obj;
}

void destruct (Type *obj) {
  ...
  free (obj);
}

void use (Type* obj) {
    // do something with obj
}

void own () {
    Type *obj = construct ();
    use (obj);
    destruct (obj);
}


Добавлено через 3 минуты и 12 секунд
естественно, можно написать более изощренный пример, позволяющий use() влиять на создание объекта. только это многословней

Это сообщение отредактировал(а) baldina - 3.4.2014, 22:56
PM MAIL   Вверх
Lukkoye
Дата 3.4.2014, 23:06 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(baldina @  3.4.2014,  22:40 Найти цитируемый пост)
если трактовать raii как паттерн, исключительно основанный на автоматическом вызове конструктора/деструктора, то да. ближе к ООП он от этого не становится 


Я не совсем понимаю фразу "трактовать raii как паттерн".

Слово "паттерн" я понимаю, как "решение", которое наиболее практичным образом решает некоторую проблему.

raii (Resource Acquisition Is Initialization) я понимаю, как идеому: получение ресурса - есть инициализация.

Суть которой - автоматический захват ресурса, и автоматическое же его освобождение.

Из этого вытекает важнейшее следствие: раии-механизмы позволяют простым способом контролировать вопросы владения и времени жизни объектов.

Собственно это главное и единственное, что вообще требуется от raii.

При этом raii - это не только автоматические вызовы конструкторов/диструкторов.

У этой концепции есть более широкая область применения.

Например: есть система и пользователи. Пользователи запрашивают ресурсы для пользования.
Система выдает ресурсы. Но ей критично контролировать время жизни ресурсов ( что бы их не грохнули без её ведома). И при этом критично знать - а они вообще ещё нужны, или их уже никто не использует.

Для этого ресурсы системы создаются и хранятся по факту внутри самой системы.
Только система знает как создать и как прибить ресурс.

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

Таким образом, ресурсы по факту живут внутри системы, где она может гарантировать их целостность.
И ей не нужно самостоятельно пасти - использует ли их кто ещё снаружи.

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


Резюмируя: идеома raii - это идея, как можно очень легко и просто контролировать вопросы владения и времени жизни объектов.

Добавлено через 1 минуту и 29 секунд
Цитата(baldina @  3.4.2014,  22:55 Найти цитируемый пост)
надо просто создать дополнительный контекст, владеющий ресурсом. с точки зрения внутренней функции use() все происходит автоматически 


Я просил автоматику.

Меня не интересует точка зрения функции use
Меня интересует точка зрения программиста, которому нужно вручную дергать 

construct (obj);
destruct (obj);

И не дай боже этого забывать.

Это ни разу не автоматика.
PM MAIL   Вверх
baldina
Дата 3.4.2014, 23:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(Lukkoye @  3.4.2014,  23:06 Найти цитируемый пост)
Меня не интересует точка зрения функции use
Меня интересует точка зрения программиста, которому нужно вручную дергать 

программист пишет только use(). внешнюю функцию написали за него и для него (или он сам, в рамках решения не прикладной задачи, а подготовки низкоуровневого слоя). ясно, что в С где-то придется вызвать вручную.

название "raii" это идиома. raii в отношении к с++ это паттерн. в отношении к любым языкам - это концепция единственного владельца ресурса. и вот эту концепцию можно реализовать в любом языке

Это сообщение отредактировал(а) baldina - 3.4.2014, 23:22
PM MAIL   Вверх
Lukkoye
Дата 3.4.2014, 23:44 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(baldina @  3.4.2014,  23:21 Найти цитируемый пост)
программист пишет только use(). внешнюю функцию написали за него и для него (или он сам, в рамках решения не прикладной задачи, а подготовки низкоуровневого слоя). ясно, что в С где-то придется вызвать вручную.


У вас последнее предложение противоречит первому.
Кто-то вручную должен написать construct и destruct.

Это уже не автоматика. 
И если ближе к реальной практике, то весь pure C - это один сплошной низкоуровневый слой.

Для сишника - выделить память, чем то её заполнить, а потом освободить - это обычная рутинная работа.
И сишник знает - кроме него самого, никто это это за него не сделает.
И ни одна сишная программа без этого не обходится.


Цитата(baldina @  3.4.2014,  23:21 Найти цитируемый пост)

название "raii" это идиома. raii в отношении к с++ это паттерн. в отношении к любым языкам - это концепция единственного владельца ресурса. и вот эту концепцию можно реализовать в любом языке


Вот мне не понятно: в отношении к с++, как понять "raii -это паттерн" ?
PM MAIL   Вверх
k0rvin
Дата 4.4.2014, 07:12 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 442
Регистрация: 24.1.2010

Репутация: 1
Всего: 5



Цитата(baldina @  3.4.2014,  22:55 Найти цитируемый пост)
надо просто создать дополнительный контекст, владеющий ресурсом. с точки зрения внутренней функции use() все происходит автоматически

Как вариант:

Код

#include <u.h>
#include <libc.h>

typedef int T;

T *
Tnew(T init)
{
    T *v;
    v = malloc(sizeof(T));
    if (v == nil)
        return nil;
    *v = init;
    print("created: %d\n", init);
    return v;
}

void
Tdestroy(T *v)
{
    print("destroying: %d\n", *v);
    free(v);
}

#define WITH(type, var, init, body) {\
    type * var = type##init;\
    if (var != nil) body;\
    type##destroy(v); }

void
main(void)
{
    WITH(T, v, new(1), {
        print("Hello ");
        print("%d\n", *v);
    });
    exits(0);
}


Код

~/prog/c $ ./with
created: 1
Hello 1
destroying: 1
~/prog/c $ 


Только 1) нужно придерживаться определенного соглашения и 2) при возникновении аварийной ситуации (сегфолт например) внутри body, деструктор не вызовется (насколько я понимаю).


--------------------
“Object-oriented design is the roman numerals of computing.” — Rob Pike
All software sucks
PM MAIL   Вверх
borisbn
Дата 4.4.2014, 10:24 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 4875
Регистрация: 6.2.2010
Где: Ростов-на-Дону

Репутация: 22
Всего: 135



k0rvin, awesome !
Лямбда ни Си. ЗдОрово !

Это сообщение отредактировал(а) borisbn - 4.4.2014, 10:25


--------------------
Женщины отличаются от программистов тем, что у них чары состоят из стрингов
PM MAIL Jabber   Вверх
Фантом
Дата 4.4.2014, 10:29 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Вы это прекратите!
***


Профиль
Группа: Участник Клуба
Сообщений: 1516
Регистрация: 23.3.2008

Репутация: нет
Всего: 49



Цитата(Lukkoye @  3.4.2014,  23:38 Найти цитируемый пост)

Ну покажите мне, как на pure C реализовать автоматический захват ресурса - допустим, выделить память под структуру.


Цитата(Lukkoye @  4.4.2014,  00:06 Найти цитируемый пост)

Я просил автоматику.

Меня не интересует точка зрения функции use
Меня интересует точка зрения программиста, которому нужно вручную дергать 

Ну что ж, в таком случае есть универсальный ответ: на C можно написать компилятор C++. Исходные коды приводить не будем ввиду их объемности.  smile 
PM   Вверх
k0rvin
Дата 4.4.2014, 10:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 442
Регистрация: 24.1.2010

Репутация: 1
Всего: 5



Цитата(Фантом @  4.4.2014,  10:29 Найти цитируемый пост)
Ну что ж, в таком случае есть универсальный ответ: на C можно написать компилятор C++.

О чем Lukkoye уже сказал здесь.

Это сообщение отредактировал(а) k0rvin - 4.4.2014, 10:42


--------------------
“Object-oriented design is the roman numerals of computing.” — Rob Pike
All software sucks
PM MAIL   Вверх
Фантом
Дата 4.4.2014, 11:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Вы это прекратите!
***


Профиль
Группа: Участник Клуба
Сообщений: 1516
Регистрация: 23.3.2008

Репутация: нет
Всего: 49



Цитата(k0rvin @  4.4.2014,  11:41 Найти цитируемый пост)

О чем Lukkoye уже сказал здесь.


Ага. Но вот где проходит граница между "можно реализовать на самом языке" и "нельзя реализовать на самом языке" - вопрос открытый. 

Очевидно, что если можно написать компилятор, то можно и интерпретатор, а это означает, что все те же средства принципиально возможно приделать и к программе на C. Следовательно, имеет смысл обсуждать только вопрос о том, насколько это удобно.
PM   Вверх
Lukkoye
Дата 4.4.2014, 13:41 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(Фантом @  4.4.2014,  11:21 Найти цитируемый пост)

Ага. Но вот где проходит граница между "можно реализовать на самом языке" и "нельзя реализовать на самом языке" - вопрос открытый.


На самом языке - на уровне исходного текста программ, которые пишутся на этом языке


Пример:

с++2003 стандарт не поддерживает оператор decltype. В коробке с гцц идет в поставке оператор typeof, но лишь, как расширение от компилятора.
Стандартного средства не существовало.

Реализовать самодельный typeof кросскомпиляторно, который предоставлял бы полную автоматику (как decltype) было не возможно.

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

Код

...
struct some 
{ 

    enum { eTYPEOF = 1 }; //уникальный идентификатор типа, 
     //который нужно писать в ручную. И в ручную же пасти его уникальность.
... 

};

...
REGISTER(some); // <--- макрос, который использует шаблоны, для ассоциации числового идентификатора с типом
...

some obj1;

typeof(obj2) obj1; // <--- макрос, который вытряхнет из объекта идентификатор,
 //по которому можно будет построить шаблон, который вернет результат typedef на тип obj1

// эквивалентная запись получится:
//
// some obj2;

...


Ну так вот, с++11 decltype - это технология новых компиляторов.
Программист может пользоваться и не геммороится о том, кто и как ему эту возможность обеспечивает.

А костыльный typeof с дикими лагами и ограничениями - это уже продукт самого программиста, которому пришлось задалбливаться на диких шаблонах и макросах, что бы обеспечить хоть какое то подобие нужного поведения.

Итого: 
с++03 не поддерживает технологию decltype , и реализовать её в полноценном виде на языке невозможно. Можно сделать лишь сильно ограниченные костыли-велосипеды.
с++11 поддерживает технологию из коробки. Программист может просто пользоваться и не париться.



Это сообщение отредактировал(а) Lukkoye - 4.4.2014, 13:42
PM MAIL   Вверх
baldina
Дата 4.4.2014, 15:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



k0rvin, отлично! 

Цитата(Lukkoye @  3.4.2014,  23:44 Найти цитируемый пост)
Вот мне не понятно: в отношении к с++, как понять "raii -это паттерн"

чего тут непонятного. 
Цитата(Lukkoye @  3.4.2014,  23:06 Найти цитируемый пост)
Слово "паттерн" я понимаю, как "решение", которое наиболее практичным образом решает некоторую проблему.

вот так и следует понимать: raii можно реализовать известным образом встроенными средствами c++ для решения проблемы управления ресурсами. собственно скелет паттерна выглядит так
Код

struct Foo {
   Foo() { /* захватить ресурс */ }
   ~Foo() { /* освободить ресурс */  }
};

void bar () {
  Foo foo; // <-- захват ресурса
  do_something();
} // <-- автоматическое освобождение ресурса, даже в случае исключения

что касается слов "raii это идиома", их следует понимать так: raii - это устойчивое выражение, имеющее смысл обсуждаемой технологии управления ресурсами. не следует искать смысл в словах самого выражения Resource acquisition is initialization, они условны. "идиома" - лингвистическое понятие, а не техническое.

Это сообщение отредактировал(а) baldina - 4.4.2014, 15:44
PM MAIL   Вверх
vinter
Дата 4.4.2014, 19:31 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Explorer
****


Профиль
Группа: Завсегдатай
Сообщений: 2735
Регистрация: 1.4.2006
Где: Н.Новгород

Репутация: 13
Всего: 56



Цитата(baldina @  2.4.2014,  23:10 Найти цитируемый пост)
да это и не нужно, многопоточность - не часть языка, а часть стандартной библиотеки.

Видимо нужно: сейчас очень активно "пилят" транзакционную модель памяти, может к 17 году и допилят

Цитата(baldina @  2.4.2014,  23:10 Найти цитируемый пост)
GC тоже нет, 

GC есть в C++, но "необязательный"


--------------------
Мой блог
PM MAIL WWW   Вверх
Lukkoye
Дата 4.4.2014, 21:13 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(baldina @  4.4.2014,  15:36 Найти цитируемый пост)
вот так и следует понимать: raii можно реализовать известным образом встроенными средствами c++ для решения проблемы управления ресурсами.


Ааа... понял о чем вы.

Только вот вы слегка путаетесь в понятиях.

Раии - идеома. 
Вот "реализация идеомы" - это уже технология, которая реализована за счет компиляторов.

То, что вы показали выше  - это не пример реализация раии. Реализация раии, грубо говоря внутри компилятора.

То, что вы показали - это пример одного из возможных использований особенностей раии. 
А сам паттерн, если не ошибаюсь, называется "scope guard".

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





PM MAIL   Вверх
baldina
Дата 5.4.2014, 14:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата(Lukkoye @  4.4.2014,  21:13 Найти цитируемый пост)
 вы слегка путаетесь в понятиях

идиома
паттерн
raii

вроде бы не я путаюсь. "raii зашит в компилятор" - это забавная точка зрения. отчего тогда в программах на с++ возникают утечки ресурсов?

Добавлено через 6 минут и 56 секунд
сейчас вы будете мне рассказывать, что компилятор вставляет вызовы конструкторов и деструкторов, и они вызываются автоматически, только это семантика не raii, а конструкторов и деструкторов. с помощью которых мы можем следовать raii или наплевать на него.
Код

struct Foo () {
   int *p;
   Foo () : p_(new int){}
};

void bar () {
  Foo foo;
} // <-- утечка


Это сообщение отредактировал(а) baldina - 5.4.2014, 15:02
PM MAIL   Вверх
baldina
Дата 5.4.2014, 15:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



PM MAIL   Вверх
Lukkoye
Дата 6.4.2014, 13:21 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Шустрый
*


Профиль
Группа: Участник
Сообщений: 86
Регистрация: 23.3.2013

Репутация: 1
Всего: 1



Цитата(baldina @  5.4.2014,  14:59 Найти цитируемый пост)
вроде бы не я путаюсь. "raii зашит в компилятор" - это забавная точка зрения. отчего тогда в программах на с++ возникают утечки ресурсов?


Потому что язык с++  - это мультипарадигменный высокоуровневый ассемблер с прямым доступом к памяти.

Идеома raii в нем реализована за счет автоматических экземпляров классов - объектов, для которых конструктор и диструктор вызываются автоматически. 

Однако, язык не заставляет обязательно использовать автоматические экземпляры классов, равно как и вообще писать в оо-стиле. На этом языке вы можете писать в самых разных стилях, например: в стиле си.

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


Это сообщение отредактировал(а) Lukkoye - 6.4.2014, 13:33
PM MAIL   Вверх
xvr
Дата 7.4.2014, 14:26 (ссылка) |    (голосов:3) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Комодератор
Сообщений: 7046
Регистрация: 28.8.2007
Где: Дублин, Ирландия

Репутация: 60
Всего: 223



1 троль + 1 провокационный вопрос = 3 страницы переливания из пустого в порожнее, местами переходящее во флейм  smile 

PM MAIL   Вверх
Страницы: (3) [Все] 1 2 3 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.1033 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.