Вопросы, часто задаваемые на собеседовании.
Вопросы по С++
Чем отличается ссылка от указателя ?Ссылки следует использовать: когда точно известно что объект существует - не надо проверять аргументы функций, когда точно известно что объект на который ссылаются будет одним и тем же, в некоторых операторах (напр [])
Три столпа OOП ?
Инкапсуляция - возможность скрыть детали реализации(private, protected, public)
Наследование - возможность повторного использования кода.
Полиморфизм - одинаковое обращение с похожими объектами(виртуальные функции + имея указатель(ссылку) на объект
производного класса, способность получить доступ к объектам базового класса)
Каждый класс программы, который объявляет или наследует виртуальные функции, имеет собственную виртуальную таблицу,
где элементы представляют собой указатели на реализацию виртуальных функций для этого класса. Если класс С2 наследует от С1,
переопределяет некоторые из наследуемых вирт функций и добавляет еще несколько своих, то элементы в вирт. таблице указывают на
функции, соответствующие типу его объектов. Эти элементы включают указатели на виртуальные функции класса С1, которые не
переопределяются в классе С2.
class C1
{
1. Ничего не имеет общего с понятием виртуальных функций. Это когда ты возвращаешь
указатель на базовый класс, а создаешь объект в зависимости от аргумента.
2. Для того чтобы создать класс тип которого ты точно не знаешь. В некоторых языках
он есть в прямой форме, а в C++ это название одного из производящих паттернов.
3. Дело в том, что в C++ таких конструкторов не существует. "Виртуальный конструктор"
- это скорее паттерн ООП, который легко реализовать на C++. Его ещё называют фабричным методом.
Виртуальный конструктор - это виртуальный метод, который определяет интерфейс для создания
некоторого объекта, но оставляет её реализацию классам-потомкам. Типичное применение этого
паттерна - клонирование объектов, тип которых неизвестен на этапе компиляции.
class A
{
public:
};
class B : public A
{
public:
С виртуальными деструкторами проще - они нужны, чтобы объект, адресуесый указателем на
базовый класс, корректно уничтожался. Например:
class A
{
public:
};
class B : public A
{
private:
public:
Ничем. A если - const char* ptr и char* const ptr
char str[] = "bla bla bla";
то const char* ptr - указатель на константу.
ptr[1] = 'a'; // - ошибка ptr указывает на константу.
ptr = str; // - ok
char* const ptr - константный указатель.
ptr[1] = 'a'; // - ok.
ptr = str; // - ошибка ptr является константой.
Если инициализация объекта завершается из-за возбуждения исключения и может быть найдена подходящая функция освобождения памяти, она вызывается для освобождения выделенной для размещения объекта памяти, а само исключение передается окружающему контексту. Если подходящая функция освобождения не может быть однозначно определена, освобождение выделенной памяти не производится (это удобно, когда функция выделения памяти на самом деле память не выделяет, если же память была выделена, то это, вероятно, приведет к утечке памяти).
class A
{
Метод const
int func() const; - этот метод не изменяет состояние класса членом которого он является.
( т.е. внутри него мы не можем изменить значение какой либо мембер переменной или вызвть не const метод).
Класс const.
const A; - говорит о том что объект не будет меняться после инициализации.
Нельзя вызывать не const методы.
mutable - позволяет модифицировать член класса в const-методе. (7.1.1, 7.1.5.1)
class X
{
public:
Виртуальная функция делается "чистой" при помощи инициализатора = 0.
Класс с одной или несколькимичисто виртуальными функциями называется "абстрактным" классом.
Невозможно создать объект абстрактного класса.
virtual Func() = 0;
virtual Func() PURE;
class A
{
Использовать автоматические переменные и smart указатели.
// Читаем Скотта Мейерса .....
Иногда нужно быть уверенным ,что для определенного класса не возникнут
утечки памяти, т.к. все его объекты расположены не в куче. Создать код
гарантирующий или запрещающий создание объектов в куче во многих случаях
можно но следует иметь ввиду , что понятие "объект находится в куче" часто
трактуется по разному.
Гарантированное размещение объектов в куче
- объекты не размещаемые в куче, обычно создаются в момент их определения и автоматически уничтожаются
в конце их существования, поэтому достаточно запретить эти явные создания и уничтжения.
Простейший способ - объявить конструкторы и деструкторы как "закрытые". Недостаток
такого подхода в том. что надо переопределять все конструкторы по умолчанию, поэтому
проще обьявить закрытым только деструктор т.к. он всего один.
class A
{
Cм. как запретить создание объектов в куче
Oбъявить конструктор как "закрытый".
UNDER CONSTRUCTION
class A{};
class B : public virtual A{};
class C : public virtual A{};
class D : public B, public C {};
Каждый виртуальный базовый класс в производном классе представлен одним и тем же (совместно
используемом) объектом. В графе наследования все виртуальные базовые классы с одним именем
будут представленны одним единственным объектом этого класса.
char* pStr = new char[512];
delete[] pStr;
class A{};
A a1;
A a2 = a1; // почленное копирование. Обычный конструктор не вызывается.
копирующий конструктор инициализирует "чистую"(неинициализированную) память, в то время
как копирующий оператор присваивания должен правильно работать с уже созданным объектом.
По умолчанию конструктор с одним аргументом определяет неявное преобразование.
class ClassA
{
Eсли она имеет конструктор не по умолчанию.
1. T.к. константы и ссылки должны быть проинициализированны, то класс содержащий члены,
являющиеся константами или ссылками, не может быть сконструирован по умолчанию, если
только программист не предоставил явно конструктор.
class A
{
1. Если локальный объект статический.
2. Если возвращаемое значение используется как аргумент функции.
Надо отметить, что автоматическая сборка мусора не включена в стандарт C++ по той простой причине, что программа, её использующая, будет всегда работать медленнее, чем если бы сборка мусора не использовалась вообще. Поэтому Бьёрном Страуструпом было предложено перепоручить обязанности сборки мусора внешним библиотекам, не затрагивая самого C++, что может позитивно сказаться на производительности приложений, поскольку программист сам может решить, где и когда ему стоит использовать автоматическое управление памятью. Это и является серьёзным отличием С++ от Java - при использовании Java у программистов просто нет выбора.
Принцип полиморфизма неразрывно связан с наследованием и гласит, что каждый класс наследник может обладать не только свойствами, унаследованными от предка, но и своими собственными. В частности, свойства предка могут быть перекрыты наследником - на место свойств предка могут быть подставлены свойства наследника. Компилятор и загрузчик гарантируют правильное соответствие между объектами и функциями, применяемыми к ним.
class A {/*...*/};
class B : public A {/*...*/}; // is-a (т.е. наследование это is-a отношение)
class A
{
Abstract Factory - Абстрактная фабрика паттерн, порождающий объекты
Adapter - Адаптер паттерн, структурирующий классы и объекты.
Bridge - Мост паттерн, структурирующий объекты.
Builder - Строитель паттерн, порождающий объекты
Chain Of Responsibility - Цепочка обязанностей паттерн поведения объектов.
Command - Команда паттерн поведения объектов
Composite - Компоновщик паттерн, структурирующий объекты.
Decorator - Декоратор паттерн, структурирующий объекты.
Facade - Фасад паттерн, структурирующий объекты.
Factory Method - Фабричный метод паттерн, порождающий классы
Flyweight - Приспособленец паттерн, структурирующий объекты.
Interpreter - Интерпретатор паттерн поведения классов.
Iterator - Итератор паттерн поведения объектов.
Mediator - Посредник паттерн поведения объектов.
Memento - Хранитель паттерн поведения объектов.
Observer - Наблюдатель паттерн поведения объектов.
Prototype - Прототип паттерн, порождающий объекты
Proxy - Заместитель паттерн, структурирующий объекты.
Singleton - Одиночка паттерн, порождающий объекты.
State - Состояние паттерн поведения объектов.
Strategy - Стратегия паттерн поведения объектов.
Template Method - Шаблонный метод паттерн поведения классов.
Visitor - Посетитель паттерн поведения объектов.
Member functions, including virtual functions (10.3), can be called during construction or destruction (12.6.2). When a virtual function is called directly or indirectly from a constructor (including from the mem-initializer for a data member) or from a destructor, and the object to which the call applies is the object under construction or destruction, the function called is the one defined in the constructor or destructor's own class or in one of its bases, but not a function overriding it in a class derived from the con-structor or destructor's class, or overriding it in one of the other base classes of the most derived object (1.8).
Именно поэтому я и упомянул о том, как компиляторы решают эту проблему. Например, MSVC++ для некоторого класса 'A'
генерирует два деструктора. Один из них - обычный деструктор, который называется 'A::~A'.
Он делает именно то, что ты в нем написал, а также вызывает такие же деструкторы базовых классов.
Никакого вызова функции освобождения памяти в нем нет.
Второй деструктор называется 'A::'scalar deleting destructor'. Этот деструктор сначала, вызывает обычный деструктор 'A::~A',
а затем вызывает функцию освобождения памяти 'operator delete'. Причем этот процесс еще и парметризован одним скрытым
булевским параметром: если передана 1, то 'operator delete' вызывается; если передан 0 - то не вызывается. Если деструктор
класса 'A' объявлен вируальным, то в таблицу виртуальных методов класса 'A', в ячейку деструктора будет помещен указатель
именно на этот деструктор.
Когда обычный (нединамический) объект типа 'A' выходит из области выдимости, компилятор генерирует прямой вызов обычного
деструктора 'A::~A'. Когда класс-наследник 'B' из своего деструктора вызывает деструктор подобъекта типа 'A', он тоже прямо
вызывает 'A::~A'
Когда ты явно вызываешь выртуальный деструктор объекта как 'p->~A()', то происходит виртуальный вызов и управление попадает
именно во второй деструктор 'A::'scalar deleting destructor' (или аналогичный деструктор класса-неследника). При этом при
выполнении такого вызова компилятор ставит скрытый параметр в 0, и поэтому функция 'operator delete' вызвана не будет.
Когда же ты удаляешь динамический объект через 'delete', компилятор генерирует прямой или косвенный (в случае виртуального
деструктора) вызов именно 'A::'scalar deleting destructor'. При этом при выполнении такого вызова компилятор ставит скрытый
параметр в 1, т.е. функция 'operator delete' вызвана будет.
Это вариант MSVC++. GCC, если я не ошибаюсь, два деструктора не генерирует, а везде обходится одним деструктором, со скрытым
параметром.
АТ>>Но если ты почитаешь пункты 12.5/4 и 12.5/7 стандарта языка, то ты поймешь, почему компиляторам проще и естественнее
вызывать функцию освобождения памяти именно из верхнего деструктора.
VD>Я не очень четко понимаю, что ты называешь верхним деструктором, но я прекрасно понимаю как вызывается процедура
освобождения памяти и деструкторы не читав стандарта вообще. :)
Не до конца понимаешь. Почитай, надешь много, интересного. В данном случае я имею в виду следуюшее "интересное":
class A
{
public:
Область видимости глобальных статических функций - все пространство имен, а
статических функций, членов классов - класс, в котором обьявлены эти фунции.
Oни все копируют члены класса.
Oтличаются синтаксисом :-)
class CMyErr {...};
void F()
{
При указании непосредственно типа исключения мы будем терять информацию, если исключение пришло из производного класса(при вызове методов).
Результатом throw является раскручивание стека до тех пор, пока не будет обнаружен
подходящий tch. После обработки исключения продолжаем выполнение, если обработчика не нашли
приложение "грубо" terminate() завершается.
return - выход из функции.
Обработчик завершения. Операционная система и компилятор гарантируют, что код данного блока будет исполнен независимо от того, каким образом произойдет выход из защищенного __try блока. и неважно, разместите ли вы в защищенном блоке операторы return, goto, или longjump - все равно будет вызван обработчик завершения.
register - переменная размещается в регистре памяти процессора - современные компиляторы пошлют вас нах... const - переменную нельзя изменять. volatile - переменная не кэшируется а после каждой операции ее знначение копируется по указанному адресу.
extern - область видимости переменной. extern "C" - соглашение имен в стиле "С".
using - использование области имен. include - подключение файла.
Макросы и inline почти одно и тоже, но макросы - считается кривизной и чревато ошибками. template-функции - можно точно не знать о типах аргументов и возвращаемого значения.
справа налево.
В нестатической функции ключевое слово this является указателемна объект, для которого вызвана функция.Это необычная переменная; невозможно получить ее адрес или присвоить ей что-нибудь. В большинстве случаев использование this является неявным. Каждое обращение к нестатическому члену внутри класса неявно использует this. Если m_Var нестатический член класса, то обращение внутри класса внутри класса эквивалентно m_Var = 5; this->m_Var = 5;
lvalue - название для "чего-то" в памяти, выражение ссылающееся на объект.
(left value) - нечто, что может быть использовано в левой части оператора.
Для того чтобы выражать национальные буквы переносимым образом при помощи по-настоящему стандартного минимального символьного набора, введен набор "триграфов". Чтобы помочь программистам обходиться с неполным набором символов, С++ обеспечивает альтернативы: например:
Макросы изменяют текст программы до обработки его компилятором, макросы также создают проблемы
для многих инструментов разработки. используете макросов плохо сказывается на работу отладчиков
генераторов списков перекрестных ссылок, профайлеров(софт для анализа кода - производит. memleaks
и т.п.)
#define POW(a) a*a
void f()
{
The template class describes an object that stores a pointer to an allocated object of type Type* that ensures that the object to which it points gets destroyed automatically when control leaves a block.
Kонструкторы нелокальных объектов в единице трансляции (*.cpp шник ) выполняются в порядке их определений.
Анонимное объединение представляет собой объединение, у которого нет имени (тэга).
Следующая программа создает анонимное объединение, которое содержит элементы
miles и meters.
Обратите внимание, что программа трактует элементы как обычные переменные.
Однако различие между элементами и обычными переменными заключается в том, что, когда вы
присваиваете значение любому из этих элементов, значение другого элемента теряется:
#include<iostream.h>
void main(void)
{
функтор - класс с operator().
Нужно использовать статические данные. Именно так делает, например, известная функция strtok(),
разбивающая предложение на слова (упрощенно). К сожалению, в этом случае клиент функции должен
свое "дело" обязательно доводить до конца, поскольку другой клиент, если получит управление,
обязательно изменит статические данные. Если у Вас работает несколько потоков, можно применить
критическую секцию или семафор, но это не решает проблему. Нужно очень четко следить, чтобы
функция не была вызвана без вашего ведома, а это не всегда реально. Если Вы между двумя вызовами
strtok() вызовете функцию из библиотеки, или создадите объект (и вызовете конструктор), или
что-то еще, нет никакой гарантии, что там никто strtok() не вызывал.
Решение в функторах. Используется возможность перегрузки оператора operator().
Определите класс CFunctor, частные данные которого будут использоваться как статические данные, и оператор operator(),
который будет делать что-то нужное. Тогда для разных экземпляров класса CFunctor, например cf1 и cf2 эти данные будут,
как обычно частными, но для каждого из вызовов cf1() и cf2() (а это одна и та же функция, только с разными именами) эти
данные будут статическими - то есть сохраняться от момента создания экземпляра до уничтожения.
// сам функтор
class CFunctor
{
public:
Вопросы по STL
Итератор - объект, который абстрагирует понятие указателя на элемент последовательности.
Для записи -
Однонаправленный - Двунаправленный - С произвольным доступом
Для чтения -
Константные(const_iterator), обычные(iterator) и обратные(reverse_iterator).
Kонтейнеры - vector, list(список),
map(ассоциативный массив), queue(очередь),
stack(cтэк), dequeue(очередь с двумя концами),
priority_queue(оч. отсортированная по значению),
set(множество), multiset(множ. в котором одно значение может встречаться несколько раз),
multimap(ассоц. массив в котором ключ может встречаться несколько раз).
Стек (Stack)
Любая последовательность, поддерживающая операции back, push_back и pop_back, может использоваться для модификации stack.
В частности, могут использоваться vector, list и deque.
Очередь (Queue)
Любая последовательность, поддерживающая операции front, push_back и pop_front, может использоваться для модификации queue.
В частности, могут использоваться list и deque.
Одна из общих проблем в мобильности - это способность инкапсулировать информацию относительно модели памяти. Эта информация включает типы указателей, тип их разности, тип размера объектов в этой модели памяти, также как её примитивы выделения и освобождения памяти. STL принимается за эту проблему, обеспечивая стандартный набор требований для распределителей (allocators), являющихся объектами, которые инкапсулируют эту информацию. Все контейнеры в STL параметризованы в терминах распределителей. Это значительно упрощает задачу взаимодействия с многочисленными моделями памяти.
Вопросы по Win32 API
2Gb в пользовательском режиме. Остальное резервируется под ядро и совместимость с 16-бит. приложениями. Это так распределяется доступная процессу память ? Ничего не скажешь, крутая 9x-овая трава!!! Для NT- даже если ничего не сказать про то что в первой "половине" (2GB или 3GB, если boot.ini поправить) как устроено, в "той" части все четко и прозрачно описано где что находится, и "та" память также доступна процессу (контекст "вызвавшего потока" - один из полноправных контекстов при выполнении кода в режиме ядра). Прямо недоступно только то, что вызывается из System Working Threads (это другой поток, в контексте System), но все равно можно прочитать и оттуда при желании. Вот уж что совсем не доступно - так это специально созданные условные "сторожевые" области типа 0x00000000-0x0000FFFF (причем, она не одна, а их несколько, например, те же 0xFFFF вниз от верхней границы UserMode).
Критический раздел - объект , к которому должен обратиться поток перед получением эксклюзивного доступа к
каким - либо общим данным. Применимы для синхронизации потоков в пределах одного процесса.
CRITICAL_SECTION crSec;
InitializeCriticalSection(&crSec);
EnterCriticalSection(&crSec);
............
LeavecriticalSection(&crSec);
DeleteCriticalSection(&crSec);
Mьютекс - очень похож на критическую секцию, за исключением того, что с их помощью
можно синхронизировать доступ к данным со стороны нескольких процессов.
hMutex = ::CreateMutex(...);
::WaitForSingleObject(hMutex, INFINIT);
.........
::ReleaseMutex(hMutex);
::CloseHandle(hMutex);
Семафоры используют для учета ресурсов. Когда вы запрашиваете у семафора ресурс, операциое\нная система
проверяет свободен он или нет. Если свободен - то уменьшает счетчик доступных ресурсов, не давая вмешиваться другому потоку.
При каждом вызове из потока WaitForSingleObject() с передачей ей описателя семафора система проверяет больше ли нуля
счетчик ресурсов у данного семафора. Если да, уменьшает счетчик на единицу и будит поток. Если при вызове
WaitForSingleObject() счетчик семафора оказался обнулен, система оставляет поток не активным до того, как другой поток освободит семафор.
(т.е. увеличивает его счетчик) Поскольку на счетчик семафора могут влиять несколько потоков, семафоры - в отличие от критических разделов
и мутексов - не передаются во владение какому - либо потоку. Т.е. один поток может ждать семафор а другой освободить семафор, а мутексы
и крит.разделы, сами захватывают и сами отпускают.
hSemaphore = ::CreateSemaphore(...);
::WaitForSingleObject(hSemaphore, INFINIT);
..........
::ReleaseSemaphore(hSemaphore, nCount, NULL);
::CloseHandle(hSemaphore);
События - в отличии от семафоров и мутексов, которые применяются для контроля за доступом к данным,
просто оповещают об окончании какой - либо операции. сущ. два типа - "со сбросом вручную(manual-reset events)"
- не переустанавливаются функциями WaitForSingle(Multiple)Object в занятое состояние.
и автоматическим сбросом(auto - reser events). Первые прим-ся для оповещения об окончании операции
сразу несколько потоков , вторые для оповещения единственного потока.
Инициализирующий поток переводит событие в занятое(non-signaled) состояние и приступает к своей работе.
По окончании возвращает событие в свободное (signaled) состояние.
hEvent = ::CreateEvent(bManualReset, bInitState, strName);
::WaitForSingleObject(hEvent, INFINIT);
...........
::ResetEvent(hEvent); // переводим в занятое состояние;
::PulseEvent(hEvent); // SetEvent + ResetEvent;
::SetEvent(hEvent); // переводим в свободноет состояние;
::CloseHandle(hEvent);
ReadProcessMemory(); WriteProcessMemory(); Использование объектов ядра - pipe(канал), MailSlot(почтовые ячейки), event(события), mutex(мутекс), files(файл), semaphore(семафор),file-mapping(файл проеципуемый в память).
Речь о том чтобы только можно было запустить только одну копию программы. Один из вариантов - использовать именованный объект ядра (например мьютекс).
1. Создать или открыть объект ядра "файл", идентифицирующий дисковый файл, который будет использован
как проецируемый в память.
::CreateFile(...);
2. Создать объект ядра "проецируемый файл", чтобысообщить системе размер файла и способ доступа к нему.
::CreateFileMapping(...);
3. Указать системе: спроецировать ли в адресное пространство вашего процесса последний объект целиком илитолько его часть.
LPVOID p = ::MapViewOfFile(...);
Закончив работу.
1. Сообщить системе об отмене проецирования на адресное постранство поцесса объекта ядра "проецируемый файл".
::UnmapViewOfFile(...);
2. Закрыть этот объект.
::CloseHandle(...);
3. Закрыть объект ядра "файл".
::CloseHandle(...);
Сигнализировать - использовать объекты ядра Event. т.к. поток является объектом ядра, то дождаться завершения - ::WaitForSingleObject(hThread, ....);
Вопросы по COM(ATL)
Апартмент это не процесс и не поток, это концепция, определяющая контекст выполнения
действий в процессе.При задании модели А. для СОМ объекта мы даем инструкцию системе СОМ
, каким образом оперировать с нашими объектами в различных потоковых окружениях.
Однопоточный А.(STA) - содержит один поток. Если кокласс поддерживает эту модель то говорит этим СОМ
системе , что в данный времени доступ к нему разрешается только одному потоку. В результате
разработчику не требуется синхронизировать доступ к данным объекта, поскольку сама СОМ гарантирует,
что доступ потоков происходит в порядке очереди сообщений. Это выполняется путем создания
невидимого окна, используемого для сериализации клиентского доступа к серверу.(с пом. генератора
сообщений). Но объекты STA должны обеспечивать защиту любых глобальных и статических данных
от конкурентного доступа при помощи CRITICAL_SECTION.
Многопоточный А. (MTA) - может содержать произвольное кол-во потоков В отличие от последовательного
доступа посредствомневидимого окна, MTA позволяет выполнять запросы клиентов в отдельных потоках.
Необходимо использовать объекты синхронизации.
Реализация COM-сервера в виде in-process (DLL) сервера дает значительные преимущества, например, лучшую структуризацию проекта, раздельную компиляцию модулей, возможность повторного использования модулей и т.п. Но в использовании внутрипроцессного (in-process) сервера есть одно небольшое, но порой критичное неудобство. Их невозможно создавать и вызывать по сети. Решить эту проблему помогают суррогатные процессы. Формально, суррогатный процесс - это процесс, который порождается специальным EXE-модулем. Отличительной особенностью суррогатный процесса является то, что в него можно загрузить внутрипроцессный COM-сервер. Что же дает использование суррогатных процессов? Суррогатный процесс позволяет превратить внутрипроцессный сервер в полноценное DCOM-приложение, давая возможность вызывать компоненты этого сервера удаленно. Суррогатные процессы позволяют изолировать компонент от клиента, тем самым исключая влияние на него таких критических ошибок, как проходы по памяти или утечки памяти во внутрипроцессном сервере. Таким образом, запуская компоненты в суррогатном процессе, клиент может защитить себя от возможно сбойного серверного кода при доступе к сервисам, предоставляемым сервером.Один суррогатный процесс может обслуживать нескольких клиентов одновременно. DLL внутрипроцессного сервера, запущенная в суррогатном процессе, получает все настройки контекста безопасности, установленные для суррогатного процесса. Возможность использования так называемых системных суррогатных процессов, а также создания пользовательских суррогатных процессов появилась в Windows NT 4.0 SP2 и Windows 95 (с установленным DCOM).
Тут надо рассказать про функции CoInterThreadMarshalInterfaceInStream и CoGetInterfaceAndReleaseStream, не запутавшись в названиях.
See RSDNs Article
Marshaling - это процесс запаковки и посылки вызовов (методов COM-интерфейса) через границу процесса
(или потоков одного и того же приложения). COM поддерживает два вида маршалинга: <стандартный маршалинг> и <ручной маршалинг>.
Первый подразумевает практически автоматическую поддержку со стороны средств разработки. Второй же дает максимальную гибкость и
позволяет заменить реализацию процесса маршалинга на собственную. В процессе маршалинга в другой контекст (возможно, удаленный)
передаются данные, которые там распаковываются и преобразуются в вид, пригодный для использования. Указатели на эти данные помещаются
в стек, и производится передача управления вызываемому/вызывающему коду. При этом в качестве данных могут быть переданы и указатели
на другие интерфейсы (возможно, других объектов).
#define _WIN32_DCOM // утверждается, что обязательно.
#include "windows.h"
::CoInitializeEx(NULL, COINIT_MULTITHREADED); // MTA
или
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); // STA
IUnknown
HRESULT QueryInterface(REFFID, void**) = 0;
ULONG AddRef() = 0;
ULONG Release() = 0;
Задачки на сообразительность
Даны N натуральных чисел и натуральное число M. Нужно за время log(N*M) и используя не более log(M) памяти сказать можно ли выбрать несколько чисел из наших N штук так, чтобы их сумма была равна M.Ссылки
Задания.