Вопросы, часто задаваемые на собеседовании.


Содержание
  1. Вопросы по С++.
  2. Вопросы по STL.
  3. Вопросы по Win32 API.
  4. Вопросы по COM(ATL).
  5. Задачки на сообразительность.
  6. Ссылки.



Вопросы по С++

Чем отличается ссылка от указателя ?

Ссылки следует использовать: когда точно известно что объект существует - не надо проверять аргументы функций, когда точно известно что объект на который ссылаются будет одним и тем же, в некоторых операторах (напр [])

Три столпа OOП ?

Инкапсуляция - возможность скрыть детали реализации(private, protected, public)
Наследование - возможность повторного использования кода.
Полиморфизм - одинаковое обращение с похожими объектами(виртуальные функции + имея указатель(ссылку) на объект производного класса, способность получить доступ к объектам базового класса)

Виртуальные таблицы и указатели на виртуальные таблицы.

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

class C1
{

C1(){}
virtual ~C1(){}
virtual Func1(){}
}
C1 vtbl
C1::~C1
C1::Func1

class C2
{
C2(){}
virtual ~C2(){}
virtual Func1(){}
virtual Func2(){}
}

C2 vtbl
C2::~C2
C2::Func1
C2::Func2

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

Для чего нужен виртуальный конструктор ?

1. Ничего не имеет общего с понятием виртуальных функций. Это когда ты возвращаешь указатель на базовый класс, а создаешь объект в зависимости от аргумента.
2. Для того чтобы создать класс тип которого ты точно не знаешь. В некоторых языках он есть в прямой форме, а в C++ это название одного из производящих паттернов.
3. Дело в том, что в C++ таких конструкторов не существует. "Виртуальный конструктор" - это скорее паттерн ООП, который легко реализовать на C++. Его ещё называют фабричным методом. Виртуальный конструктор - это виртуальный метод, который определяет интерфейс для создания некоторого объекта, но оставляет её реализацию классам-потомкам. Типичное применение этого паттерна - клонирование объектов, тип которых неизвестен на этапе компиляции.

class A
{
public:

virtual A *Clone() { return new A; }
};
class B : public A
{
public:
A* Clone() { return new B; }
};
extern A *ptr; // мы не знаем, на какой объект ссылаемся
...
A *copy = ptr->Clone(); // создаём копию объекта, не зная его точного типа
В этом примере Clone можно назвать виртуальным конструктором. Использовать его гораздо удобнее, чем определять тип объекта, адресуемого ptr, через RTTI, а затем создавать нужную копию в switch'е. Кроме того, такое решение оказывается более гибким и лучше соответствует идеалам ООП.
Хотелось бы уточнить, что клонирование и фабрика и всеж таки две разные вещи. Приведенный пример, это клонирование. А абстрактная фабрика, это отдельный класс, который умеет создавать экземпляры объектов, и наличие этой абстрактной фабрики позволяет инкапсулировать способ создания конкретных экземпляров. Простейший пример:
// Определение IA, IB, A и B
class IObjFactory
{
public:
virtual IA* newA() = 0;
virtual IB* newB() = 0;
};
class ObjFactory : public IObjFactory
{
public:
virtual IA* newA() { return new A(); }
virtual IB* newB() { return new B(12); }
};
Кстати,
здесь есть что-то под названием Виртуальные конструкторы, но в конечном итоге там все сводится к abstract factory паттерну.


Для чего нужен виртуальный деструктор ?

С виртуальными деструкторами проще - они нужны, чтобы объект, адресуесый указателем на базовый класс, корректно уничтожался. Например:
class A
{
public:

virtual ~A(){}
};
class B : public A
{
private:
char *buf;
public:
B() { buf = new char[255]; }
~B() { delete [] buf; }
};
int main(int argc, char* argv[])
{
A *ptr = new B;
delete ptr;
return 0;
}
Если не объявить деструктор виртуальным, то в строке delete ptr; вызовется только деструктор класса A (которому соответствует тип указателя ptr), и память, выделенная под B::buf, останется неосвобождённой. Ну а в деструкторе вот какой смысл. Если он не виртуальный, то при вызове деструктора потомка деструктор предка не будет вызван. Иначе - будет вызван после потомка.


Чем отличается вызов виртуального деструктора от обычной виртуальной функции ?

1. Т.к. каждый класс имеет свое отличное от других имя, то при вызове p->~A(); реально может вызваться ~DerivedA();
2. Вызов виртуального метода записывается точно так же, как вызовы других функций, однако механизм обращения слегка усложняется, делая его более гибким. Поскольку виртуальные методы могут быть переопределены в производных классах, их адреса невозможно определить на стадии компиляции. Фактические адреса виртуальных методов становятся известными только во время выполнения программы. Ключевое слово virtual в объявлении метода создает запись в таблице виртуальных методов объекта (VMT). При выполнении программы эти записи преобразуются в фактические адреса всех виртуальных методов данного компонентного класса. Когда вы производите новый объектный класс, он наследует VMT всех своих предшественников, добавляя к таблице дополнительные виртуальные методы, объявленные в производном классе. Кроме того, производный класс может переопределять наследованные виртуальные методы. Отметим, что явный вызов деструктора не отменяет его автоматический вызов.
3. Если и у базового и у производного классов есть конструкторы и деструкторы, то конструкторы выполняются в порядке наследования, а деструкторы - в обратном порядке. Т.е. если А базовый класс, В - производный из А, а С - производный из В (А-В-С), то при создании объекта класса С вызов конструкторов будет иметь следующий порядок: конструктор А - конструктор В - конструктор С. Вызов деструкторов при разрушении этого объекта произойдет в обратном порядке: деструктор С - деструктор В - деструктор А. Понять закономерность такого порядка не сложно, поскольку базовый класс "не знает" о существовании производного класса, любая инициализация выполняется в нем независимо от производного класса, и, возможно, становится основой для инициализации, выполняемой в производном классе. С другой стороны, поскольку базовый класс лежит в основе производного, вызов деструктора базового класса раньше деструктора производного класса привел бы к преждевременному разрушению производного класса.


Чем отличаются const char *ptr и char const *ptr ?

Ничем. 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 является константой.


Чем чреват выброс исключения из конструктора, деструктора ?

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

Использование исключений в конструкторах и деструкторах Функции создания и уничтожения объектов не могут возвращать результат. Однако, используя механизм исключений, чтобы сообщить из конструктора или деструктора о состоянии ошибки. Если создаваемый объект имеет тип auto (локальный объект), для него вызывается соответствующий конструктор. При возникновения исключения в конструкторе, деструкторы вызываются только для полностью сформированных объектов. Например, для данного объекта будут вызваны деструкторы базовых классов и полностью сконструированных объектов данных.
Если создаваемый объект имеет динамический тип памяти, то для него вызывается оператор new. При возникновении исключения в конструкторе выделенная оператором new память корректно возвращается оператором delete.
class A { ... };
class B { ... };
class C : public A
{
B b;
public:
C() { throw; "ошибка"; };
~C() {};
};
void main()
{
A *ap;
try
{
A a; // вызывается конструктор локального объекта
ap = new A; // вызывается перегруженный оператор A::new
...
delete ap; // не выполняется при возникновении исключения char*
}
catch(const char* msg)
{ // обработка msg
return -1;
}
return 0;
}
Для локального объекта будет вызвана следующая последовательность функций:
A::A(); // конструктор базового класса
B::B(); // конструктор объекта-данного
С::C(); // конструктор локального объекта
В конструкторе локального объекта выбрасывается исключение char*, что влечет за собой вызов следующих функций:
~B::B(); // деструктор объекта-данного
~A::A(); // деструктор базового класса
Деструктор класса С вызываться не будет, так как объект типа С еще не сформирован. После этого выполнится исключение char* и основная программа возвратит результат -1. Для динамически созданного объекта будет вызвана следующая последовательность функций:
С::operator new();
A::A(); // конструктор базового класса
B::B(); // конструктор объекта-данного
С::C(); // конструктор локального объекта
В конструкторе локального объекта выбрасывается исключение char*, что влечет за собой вызов следующих функций:
~B::B(); // деструктор объекта-данного
~A::A(); // деструктор базового класса
С::operator delete();
Далее будет выбран соответствующий обработчик, который выполнин необходимую обработку и возвратит результат -1.
Использовать try(catch)
Что касается деструкторов то:
Деструктор может быт вызван в двух ситуациях. Во-первых, при нормальных условиях, т.е. когда объект покидает область видимости или явно вызван оператор delete/ во-вторых - когда объект удаляется механизмом обработки исключений во время выравнивания стека. Таким образом, в момент вызова деструктора исключение уже может быть сгенерировано, а может и не быть. к сожалению при исполнении кода деструктора определить это нельзя. поэтому приходиться писать деструкторы, исходя из пессимистического предроложения, что исключение уже возникло. Ведь если конструктор при уже имеющемся исключении также генерирует иключение и управление передается в вызывающий модуль, то С++ запускает функцию terminate Действие этой функции - немедленно завершает выполнение программмы, не удаляя даже локальные переменные.
Не допускать распространение исключений за пределы деструкторов
1. это предотвращает вызов terminate()
2. гарантирует что деструкторы полностью выполнили свои функции.


В чем потенциальная опасность конструктора копирования по умолчанию ?

class A
{

char* str;
A() { str = new char[100]; }
~A() { delete[] str; }
};
void F()
{
A a1;
A a2 = a1; // копир конструктор - проблема
A a3;
a3 = a1; // присваивание - проблема.
}
при этом конструкиор A по умолчанию будет вызван дважды - для а1 и а3. Он невызовется для a2, т.к. эта переменная проинициализирована при помощи копирования. Однако деструктор будет вызываться 3 раза для каждого по одному разу. Указатель str в а3 пропадет т.к. будет перезаписан присваиванием а3 = а1; С другой стороны массив str созданный для а1 будет удаляться три раза в а1, а2, а3 что приведет к крашу. Этого можно избежать если явно задать копирующий конструктор и оператор присваивания. А(const A& a) { str = new char[100]; }
operator= (const A& a) { if( this != &a) { delete str; str = new char[100]; } }


Чем отличаются операторы приведения типа static_cast, reinterpret_cast, dynamic_cast, const_cast ?

dynamic_cast - динамическое приведение. Может преобразовать базовый класс в производный или "братский" класс, при условии наличия виртуальных функций. Используется когда правильность преобразования не может быть определена компилятором(на этапе выполнения).В случае неудачи возвращает 0, иначе приведенный указатель на обьект. Некоторые преобразования являются переносимыми.
static_cast осуществляет преобразование родственных типов на этапе компиляции, например указателя на один тип к указателю на другой из той же иерархии классов, перечислений в целые типы или типа с плавающей точкой в интегральный. Некоторые преобразования являются переносимыми.
reinterpret_cast может преобразовать указатель в другой любой, в интегральный тип или наоборот. Короче говоря, он преобразует как бы никак не связанные типы. Практически никогда не переносимо.
const_cast реализован "для полноты картины". Этот оператор снимает только константность и volatile'ность объекта, так как другие новые операторы преобразования этого сделать не могут (static_cast, dynamic_cast, reinterpret_cast). В то же время, старые преобразования типа T(xxx) константность могут снимать. Используя оператор const_cast, вы подчеркиваете, что собираетесь только изменить атрибут const или volatile какого - либо объекта.


Метод const. класс const. ключевое слово mutable, volatile.

Метод const
int func() const; - этот метод не изменяет состояние класса членом которого он является. ( т.е. внутри него мы не можем изменить значение какой либо мембер переменной или вызвть не const метод).
Класс const.
const A; - говорит о том что объект не будет меняться после инициализации. Нельзя вызывать не const методы.
mutable - позволяет модифицировать член класса в const-методе. (7.1.1, 7.1.5.1)
class X
{
public:

int x;
mutable int y;
};
int main()
{
const X x;
x.y = 100; // Это пройдёт
x.x = 200; // А вот это нет.
return 0;
}
volatile - ключевое слово существует ещё с языка С. Если упрощённо, то информирует компилятор о том, что переменная может быть модифицирована извне. (3.9, 5.2.5, 7.1.1) Если упрощённо, то volatile используется, когда нужно запретить компилятору делать предположения о стабильности значения переменной. Ну, например, если это общая переменная для нескольких процессов, размещённая по фиксированному адресу. Особенно актуально, если нужно запретить запись (т.е., объявить const), но разрешить чтение, притом чтение должно всегда производиться по фиксированному адресу. Если не добавить volatile, компилятор растащит копии значения где надо и не надо. Если же volatile есть, то чтение всегда будет производиться из ячейки памяти с одним и тем же адресом. 


Как определить чисто виртуальную функцию, абстрактный базовый класс ?

Виртуальная функция делается "чистой" при помощи инициализатора = 0. Класс с одной или несколькимичисто виртуальными функциями называется "абстрактным" классом. Невозможно создать объект абстрактного класса.
virtual Func() = 0;
virtual Func() PURE;
class A
{

virtual Func() = 0;
virtual Func1() PURE;
}


1. Как организовать освобождение ресурсов при выходе из функции/метода ?
2. Как предотвратить утечки памяти связанные с с забывчивостью вызывать Delete ?

Использовать автоматические переменные и smart указатели.


Как запретить создание объектов в куче ?

// Читаем Скотта Мейерса .....
Иногда нужно быть уверенным ,что для определенного класса не возникнут утечки памяти, т.к. все его объекты расположены не в куче. Создать код гарантирующий или запрещающий создание объектов в куче во многих случаях можно но следует иметь ввиду , что понятие "объект находится в куче" часто трактуется по разному.
Гарантированное размещение объектов в куче - объекты не размещаемые в куче, обычно создаются в момент их определения и автоматически уничтожаются в конце их существования, поэтому достаточно запретить эти явные создания и уничтжения. Простейший способ - объявить конструкторы и деструкторы как "закрытые". Недостаток такого подхода в том. что надо переопределять все конструкторы по умолчанию, поэтому проще обьявить закрытым только деструктор т.к. он всего один.
class A
{

~A();
public:
A();
A(int);
void Destroy() { delete this; }
};
A a; // err. нельзя вызвать деструктор.
A* pA = new A; // ok
delete pA; // err. нельзя вызвать деструктор.
pA->Destroy(); // ok
Запрет на размещение объектов в куче - нужно сделать так, чтобы клиенты не могли вызывать new() и delete(); new() - встроенная функция, которая вызывает operator new() который можно переопределить и определить как "закрытую".
class A
{
private: // для точности сказанного, хотя и без этого private :-)
static void* operator new(size_t size);
static void operator delete(void* ptr);
// для массивов
static void* operator new[](size_t size);
static void operator delete[](void* ptr);
};


Как запретить удаление объекта класса ?

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; // почленное копирование. Обычный конструктор не вызывается.
копирующий конструктор инициализирует "чистую"(неинициализированную) память, в то время как копирующий оператор присваивания должен правильно работать с уже созданным объектом.


Что такое конструктор конверсии ? Kлючевое слово explicit ?

По умолчанию конструктор с одним аргументом определяет неявное преобразование.
class ClassA
{

ClassA(int){}
}
ClassA objA = 1; // проинициализировать objA значением ClassA(1) В других случаях неявное преобразование нежелательно и может привести к ошибке ClassA objA = 'a'; // проинициализировать objA значением ClassA(int('a')); неявное приведение можно подавить, объявив конструктор с модификатором explicit
explicit - запрещает использовать конструктор для неявного преобразования типов, например, при передаче параметров.
class X
{
public:
explicit X(int) {...}
};
X x(1); // Ok
X x = 1; // Err, поскольку конструктор X(int) вызывается неявно.


В каких случаях переменная класса должна быть проинициализирована в списке инициализации конструктора ?

Eсли она имеет конструктор не по умолчанию.


Kогда класс должен иметь конструктор по умолчанию ?

1. T.к. константы и ссылки должны быть проинициализированны, то класс содержащий члены, являющиеся константами или ссылками, не может быть сконструирован по умолчанию, если только программист не предоставил явно конструктор.
class A
{

const int a;
const int& b;
};
A a; // Err, нет конструктора по умолчанию для А. 2. Если у вашего класса нет конструктора по умолчанию ,то при попытке создания массива объектов типа T вы получите ошибку при компиляции.


Kогда можно вернуть из функции ссылку на локальный объект ?

1. Если локальный объект статический.
2. Если возвращаемое значение используется как аргумент функции.


Почему в C++ нельзя реализовать автоматическую сборку мусора ?

Надо отметить, что автоматическая сборка мусора не включена в стандарт C++ по той простой причине, что программа, её использующая, будет всегда работать медленнее, чем если бы сборка мусора не использовалась вообще. Поэтому Бьёрном Страуструпом было предложено перепоручить обязанности сборки мусора внешним библиотекам, не затрагивая самого C++, что может позитивно сказаться на производительности приложений, поскольку программист сам может решить, где и когда ему стоит использовать автоматическое управление памятью. Это и является серьёзным отличием С++ от Java - при использовании Java у программистов просто нет выбора.


Что такое полиморфизм ?

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


Что такое is-a и has-a отношения ?

class A {/*...*/};
class B : public A {/*...*/}; // is-a (т.е. наследование это is-a отношение)
class A
{

A a; // has-a (т.е. включение это has-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).


Как реализован виртуальный деструктор в C++ ?

Именно поэтому я и упомянул о том, как компиляторы решают эту проблему. Например, 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:

static void operator delete(void* p);
virtual ~A();
};
class B : public A
{
public:
static void operator delete(void* p);
};
...
A* p = new B;
delete p;
После деструкции объекта должен быть вызван 'operator delete'. Какая по-твоему версия 'operator delete' должна быть вызвана: 'A::operator delete' или 'B::operator delete'? Ответ: должен быть вызван 'B::operator delete', как этого требует стандарт языка. Замечаешь? Функция 'operator delete' объявлена как статическая, но вести она себя должна как виртуальная. Как этого достичь? Одним из наиболее простых вариантов является описанный мною выше - вызывать правильный 'operator delete' из деструктора объекта. Т.е. механизм виртуализации выбирает правильный деструктор, а деструктор уже знает, какой 'operator delete' ему надо вызвать. Каким образом достгается то, что 'operator delete' не вызывается, когда его не просят, я уже описал. АТ>> Чтобы деструкор знал, надо ли ему вызывать функцию освобождения памяти, компилятор обычно передает ему специальный параметр. Другой вариант: компилятор генерирует две версии деструктора - с вызовом функции освобождения памяти и без. VD>VC и большинство реальных компиляторов передают в деструктор только указатель на удаляемый блок. Причем деструктор в них является обычным методом (с необычным именем). То что ты называешь "деструктором" в них называется оператором delete вернее кодом который вызывает деструкторы и этот оператор. Именно этот код подставляет компилятор при вызове delete p;. Когда объект размещается в стеке или глобальной переменно оператор delete вообще не вызывается и соответственно не вызывается процедура освобождения памяти. Причем для глобальных объектов VC (по крайней мере вызывает деструкторы вручную). Ну здрасьте! Теперь ты говоришь, что деструктор вызывается из 'operator delete'. А что, это логичнее, чем вызов 'operator delete' из деструкора? Чем, интересно? Anyway, я вижу, что спор идет о названиях. Ты называешь функцию 'A::'scalar deleting destructor' оператором 'delete'. Я называю эту функцию другой версией деструктора. Мое мнение правильное, а твое - нет. Во-первых, само внутренне названее метода содержит слово 'destructor' :) Во-вторых, как я говорил выше, при явном вызове виртуального деструктора 'p->~A()' управление сразу попадает в эту самую функцию, которую ты назвал 'operator delete'. С чего бы это вдруг в ответ на такой вызов комплятор стал вызывать 'operator delete'? АТ>>Именно так работают MSVC++ 6 и GCC. Я не удивлюсь, если окажется, что так поступает большинство компиляторов. Вот только практика показывает, что верны именно мои слова. Практика показывает, что ты просто неправильно проинтерпретировал происходящее. Что по твоему будет ели вызвать такой код: pSomeClass->>~SomeClass(); Я уже описал это выше. VD>И зачем нужен оператор delete, да и new? Ты, кажется, попадаешь в классическую русскую терминологическую ловушку. Что ты имеешь в виду под "оператор delete, да и new"? Выражения new/delete? Или функции 'operator new'/'operator delete'? Последние занимаются только выделением и освобождением сырой памяти. Никаких конструкторов или деструкторов они не вызывают. АТ>>Попробуй посмотреть на ассемблерный код уничтожения динамического объекта в MSVC++ 6. Ты увидишь, что никакого вызова функции освобождения памяти там нет. Есть только вызов деструктора. Освобождение памяти вызывается уже из него. VD>Ты просто очень странно понимаешь слово деструктор. Код я сто раз смотрел. Освобождение есть, но если деструктор не inline-ится, то этот код компилятор выносит в отдельную область где вызываются последовательно все деструкторы (если деструктор не виртуальный), а потом вызывается оператор delete. Нет, в данном случае ответ вопрос о том, как назвать эту функцию, нетривиален. Ты не принял во внимание некоторые важные детали и назавал эту функцию "оператор delete". Это, как я уже продемонстрировал, неверное название. То, что вызывает MSVC++ - это отдельная версия деструктора. VD>Все же деструктор это отдельный метод (по-моему). Если использовать другую терминологию (или называть деструктором и ~Xxx и процедуру (а то и inline-код) вызываемую при уничтожении объекта память для которого была размещена через оператор new), то все рассуждения на эту тему станут запутанными. Соврешенно верно. 'operator delete' - тоже отдельный метод. Но требования стандарта ("виртуальность" статического метода 'operator delete') приводят к тому, что компиляторы вызывают 'operator delete' из деструкторов.


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

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


Объясните, чем отличаются глобальные статические функции от статических функций, членов классов ?

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


Что общего, и чем обычно отличаются реализации конструкторов копирования, инициализации и перегруженной операции присваивания ?

Oни все копируют члены класса.
Oтличаются синтаксисом :-)


Сравните 2 способа перегрузки операций: как метод класса и как внешнюю функцию-друг.


Может ли шаблонный (темплэйтный) класс быть абстрактным ? Почему ?


В чем специфика перегрузки операций "sizeof", "typeid", "new" и "delete" ? Чем она вызвана ?


Сравните особенности перегрузки операций "()" и "[]".


Дайте развернутое сравнение работы со строками в стиле С (char*) и С++ (string).


Дайте развернутое сравнение работы с динамической памятью в С и С++.


Дайте развернутое сравнение стандартных средств ввода-вывода в С и С++.


Расскажите, что и как нужно сделать, чтобы объекты написанного Вами класса можно было помещать в cout и читать из cin ?


Объясните разницу между понятиями "анонимное пространство имен" и "анонимное перечисление".


Что означает оператор throw без параметра ?

class CMyErr {...};
void F()
{

throw CMyErr(...);
}
throw - генерация исключения. Без параметров - повторная генерация того же самого исключения. Перехватив исключение обработчик иногда не может полностью обработать его, в этом случае он делает то что может и и вновь генерит исключение (throw; без параметров) чтобы сработал другой - следующий обработчик.
try
{
}
catch(CMyErr Er)// обработчик исключения в скобках - тип объектов которые могут быть перехвачены.
{
cout << Err.msgStr;
}


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

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


Сравните операторы throw и return.

Результатом throw является раскручивание стека до тех пор, пока не будет обнаружен подходящий tch. После обработки исключения продолжаем выполнение, если обработчика не нашли приложение "грубо" terminate() завершается.
return - выход из функции.


Для чего в C++ используется ключевое слово __finally ?

Обработчик завершения. Операционная система и компилятор гарантируют, что код данного блока будет исполнен независимо от того, каким образом произойдет выход из защищенного __try блока. и неважно, разместите ли вы в защищенном блоке операторы return, goto, или longjump - все равно будет вызван обработчик завершения.


В чем различие между модификаторами register, const и volatile ?

register - переменная размещается в регистре памяти процессора - современные компиляторы пошлют вас нах... const - переменную нельзя изменять. volatile - переменная не кэшируется а после каждой операции ее знначение копируется по указанному адресу.


В чем разница между конструкциями "extern" и "extern "C"" ?

extern - область видимости переменной. extern "C" - соглашение имен в стиле "С".


В чем, в рамках традиционного С++, различие между using и include ?

using - использование области имен. include - подключение файла.


Сравните использование макросов, inline-функций и template-функций.

Макросы и inline почти одно и тоже, но макросы - считается кривизной и чревато ошибками. template-функции - можно точно не знать о типах аргументов и возвращаемого значения.


В каком порядке вычисляются аргументы функции при ее вызове ?

справа налево.


Что такое и как используется this ?

В нестатической функции ключевое слово this является указателемна объект, для которого вызвана функция.Это необычная переменная; невозможно получить ее адрес или присвоить ей что-нибудь. В большинстве случаев использование this является неявным. Каждое обращение к нестатическому члену внутри класса неявно использует this. Если m_Var нестатический член класса, то обращение внутри класса внутри класса эквивалентно m_Var = 5; this->m_Var = 5;


Что такое lvalue ?

lvalue - название для "чего-то" в памяти, выражение ссылающееся на объект.
(left value) - нечто, что может быть использовано в левой части оператора.


Что такое и для чего нужны триграфы ?

Для того чтобы выражать национальные буквы переносимым образом при помощи по-настоящему стандартного минимального символьного набора, введен набор "триграфов". Чтобы помочь программистам обходиться с неполным набором символов, С++ обеспечивает альтернативы: например:

 ??<  тоже самое что { , ??- тоже самое что  ~ ,   ??! тоже самое что   | .


В чем опасность использования макросов? Приведите примеры.

Макросы изменяют текст программы до обработки его компилятором, макросы также создают проблемы для многих инструментов разработки. используете макросов плохо сказывается на работу отладчиков генераторов списков перекрестных ссылок, профайлеров(софт для анализа кода - производит. memleaks и т.п.)
#define POW(a) a*a
void f()
{

int a = 0;
int iVar = POW(a+2); // iVar = a+2*a+2; - без скобок т.о. получим не 4 а 2.
}


Для чего нужен и как используется класс auto_ptr ?

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)
{

union
{
int miles;
long meters;
};
miles = 10000; cout << "Значение в милях " << miles << endl;
meters = 150000; cout << "Значение в метрах " << meters << endl;
}
Как видите, с помощью анонимного объединения, программа может сэкономить память, не используя имя объединения и точку для обращения к значениям элементов.


Что такое функтор. Что такое алгоритм ?

функтор - класс с operator().


Как сохранить промежуточные данные между вызовами функции ?

Нужно использовать статические данные. Именно так делает, например, известная функция strtok(), разбивающая предложение на слова (упрощенно). К сожалению, в этом случае клиент функции должен свое "дело" обязательно доводить до конца, поскольку другой клиент, если получит управление, обязательно изменит статические данные. Если у Вас работает несколько потоков, можно применить критическую секцию или семафор, но это не решает проблему. Нужно очень четко следить, чтобы функция не была вызвана без вашего ведома, а это не всегда реально. Если Вы между двумя вызовами strtok() вызовете функцию из библиотеки, или создадите объект (и вызовете конструктор), или что-то еще, нет никакой гарантии, что там никто strtok() не вызывал. Решение в функторах. Используется возможность перегрузки оператора operator(). Определите класс CFunctor, частные данные которого будут использоваться как статические данные, и оператор operator(), который будет делать что-то нужное. Тогда для разных экземпляров класса CFunctor, например cf1 и cf2 эти данные будут, как обычно частными, но для каждого из вызовов cf1() и cf2() (а это одна и та же функция, только с разными именами) эти данные будут статическими - то есть сохраняться от момента создания экземпляра до уничтожения.
// сам функтор 
class CFunctor
{
public:

CFunctor(int _pStat = 0) : m_pStat (_pStat){};
const int& operator()(int _i=0)
{
return m_pStat+= _i;
}
const int& readval ()
{
return m_pStat;
}
private:
int m_pStat;
};
// таймер
class CTimer
{
public: // установка функции обратного вызова - функтора
void setcallback(CFunctor _cf) { m_cf = _cf; }
private:
CFunctor m_cf;
void onTick(void){ m_cf(); }
};
// здесь попробуем как действует 
int main(int argc, char* argv[])
{
CFunctor f1(6);
CFunctor f2(100);
CTimer ct1, ct2;
// Это - анонимные экземпляры функторов. Они живут одно мгновение, и сейчас исчезнут.
//Но их копии останутся внутри таймеров до поры до времени.
ct1.setcallback (CFunctor(5));
ct2.setcallback (CFunctor(66));
cout << f1(2)<< endl; cout << f1(3)<< endl;
cout << f2(200)<< endl; cout << f2(300)<< endl;
// Здесь якобы ваш цикл обработки сообщений
// for (;;) {}
return 0;
}
Что здесь происходит: f1, f2 - два функтора одного типа, создаются и ждут своего часа. ct1, ct2 - два таймера, создаются, и оба конфигурируются экземплярами класса CFunctor, только со своими параметрами - и эти параметры сохраняются для каждого таймера по отдельности! Учтите, кстати, что функторы передаются по значению, ибо неименованные экземпляры уничтожатся немедленно!




Вопросы по STL


Расскажите, какие бывают итераторы и чем они отличаются ?

Итератор - объект, который абстрагирует понятие указателя на элемент последовательности.
Для записи -
Однонаправленный - Двунаправленный - С произвольным доступом
Для чтения -
Константные(const_iterator), обычные(iterator) и обратные(reverse_iterator).


Какие есть способы поиска в контейнерах ?

1. использование итераторов.
2. использование функций членов ( напр find )


В чем разница между stack, queue и другими контейнерами ?

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.


Что такое распределители (allocators) ?

Одна из общих проблем в мобильности - это способность инкапсулировать информацию относительно модели памяти. Эта информация включает типы указателей, тип их разности, тип размера объектов в этой модели памяти, также как её примитивы выделения и освобождения памяти. 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(файл проеципуемый в память).


Как создать единственный экземпляр приложения в памяти (singleton) ?

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


Как происходит создание отображаемого в память файла ?

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).


Как передать указатель на интерфейс из одного потока в другой в STA ?

Тут надо рассказать про функции CoInterThreadMarshalInterfaceInStream и CoGetInterfaceAndReleaseStream, не запутавшись в названиях.


Виды маршалинга, их достоинства и недостатки.

See RSDNs Article
Marshaling - это процесс запаковки и посылки вызовов (методов COM-интерфейса) через границу процесса (или потоков одного и того же приложения). COM поддерживает два вида маршалинга: <стандартный маршалинг> и <ручной маршалинг>. Первый подразумевает практически автоматическую поддержку со стороны средств разработки. Второй же дает максимальную гибкость и позволяет заменить реализацию процесса маршалинга на собственную. В процессе маршалинга в другой контекст (возможно, удаленный) передаются данные, которые там распаковываются и преобразуются в вид, пригодный для использования. Указатели на эти данные помещаются в стек, и производится передача управления вызываемому/вызывающему коду. При этом в качестве данных могут быть переданы и указатели на другие интерфейсы (возможно, других объектов).


Какие есть способы передачи массива структур в COM-сервер ?


Передача блоков данных переменной длины.


Кто должен распределять и освобождать память при передаче строк BSTR ?


Как получить на клиенте данные из буфера, распределенного в COM-сервере ?


Какой интерфейс в ATL используется для передачи клиенту информации об ошибках ?


Как организовать передачу callback-вызовов от COM-сервера к клиенту ?


Как в ATL создать многопоточный объект (MTA) ?

#define _WIN32_DCOM // утверждается, что обязательно. 
#include "windows.h"
::CoInitializeEx(NULL, COINIT_MULTITHREADED); // MTA
или
::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); // STA


Какой интерфейс является базовым для всех com-интерфейсов ?

IUnknown


Какие 3 метода содержит IUnknown ?

HRESULT QueryInterface(REFFID, void**) = 0;
ULONG AddRef() = 0;
ULONG Release() = 0;




Задачки на сообразительность

Даны N натуральных чисел и натуральное число M. Нужно за время log(N*M) и используя не более log(M) памяти сказать можно ли выбрать несколько чисел из наших N штук так, чтобы их сумма была равна M.
Ну и всякие задачки типа реализуй стандартную библиотечную функцию с помощью базовых средств C++ и того же типа только с более сложными структурами данных.
1. Есть замкнутый полигон с самопересечениями. Разбить на полигоны без самопересечений. Пересечения происходят в точках излома (для простоты). Полигоны задаются рядом точек.
2. Дано N монет одинакового диаметра. Разместить монеты максимально плотно друг к другу без наложений так, чтобы занимаемая площадь была минимальной. Под занимаемой площадью понимается выпуклая оболочка, натянутая вокруг монет.
3. Аппроксимировать дугу отрезками.
4. Построить выпуклую оболочку вокруг множества точек.
5. Построить касательную к двум полигонам.


Англичанин живет в красном доме. Швед держит собаку. Датчанин пьет чай. Зеленый дом стоит слева от белого. Жилец зеленого дома пьет кофе. Человек, курящий "Pall Mall", содержит птицу. Жилец из среднего дома пьет молоко. Жилец желтого дома курит "Dunhill". Норвежец живет в первом доме. Курильщик "Marlboro" живет около того, кто держит кошку. Человек, который содержит лошадь, живет около того, кто курит "Dunhill". Курильщик сигарет "Winfield" пьет пиво. Норвежец живет около голубого дома. Немец курит "Rothmans". Курильщик "Marlboro" живет по соседству с человеком, который пьет воду. ВОПРОС: У кого живет рыба?
P.S. Предполагалось что первый дом - это тот который слева, и все держат по одной зверюшке, курят по одному сорту сигарет, и т.д. Вроде решил вашу задачку, получилось что рыба живет у немца. Немец живет в зеленом доме и пьет кофе.

У шахматной доски вырезали два противоположных угла (по одной ячейке). Можно ли её закрыть доминошками по две ячейки. Нельзя,доминошки всегда ложатся на ячейки разного цвета А вырезали ячейки одного цвета. "Гарднер"



Ссылки

Задания.
CPP Lite.
CPP questions.
CPP questions from RSDN.