На главную. К полному списку вопросов.

1. Что такое виртуальный метод. Механизм работы.

2. Бывает ли виртуальный конструктор, деструктор.

3. Можно ли в конструкторе вызвать виртуальную функцию.

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

5. Написать функцию которая переворачивает строку(1-ый эелемент становится последним, 2-предпоследним...).

6. На некий вход из потока поступает бесконечная последовательность символов. Все символы встречаются чётное колличесво раз, кроме одного. Как найти этот символ?

7. Найти все простые числа от 0 до 100000.

8. Написать класс двумерный массив. Реализовать в нём обращение к елементу массива. Запись нового значения и получение значения.

9. Зачем нужен спецификатор volatile, static_cast, reinterpret_cast, __single_inheritance, mutable, selectany, deprecated, explicit, naked.

1. Что такое виртуальный метод. Механизм работы.

Полазив по инету я встретил такое определение виртуального метода:

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

Стоит к этому добавить что виртуальные методы должны иметь аргументы одного типа и объявляется он с помощью спецификатора virtual. Виртуальный метод(функция) даёт объекту производного класса способность модифицировать поведение, определенное на уровне базового класса.
Пример:
	class A
	{
		 ...
	  public:
		 ...
	  virtual void Metod_1(int val);
	  virtual void Metod_2();
	};

	...
	class C: public A
	{
		 ...
	  public:
	  virtual void Metod_1(int val);
	  virtual void Metod_2();
	};
Механизм виртуальных функций реализован посредством таблицы указателей на функции. Она создаётся во время компиляции. Кроме таблицы компилятор добавляет в класс указатель на неё. Вызов виртуальных функций происходит через эту таблицу, во время выполнения программы.
Например:
	class A
	{
	   public:
	   virtual int Fun1(int);
	};
....
        void main()
     	{
	   A elem;
	   A* pObj = &elem; 
	   pObj->Fun1(1); // Это выражение можно представить как (*(pObj->vptr[0])) (pObj,1);
	   ... 	
	}
(*(pObj->vptr[0])) (pObj,1) - Здесь буквально сказано следующее: ВЫЗВАТЬ ФУНКЦИЮ, РАСПОЛОЖЕННУЮ ПО НУЛЕВОМУ ИНДЕКСУ ТАБЛИЦЫ ВИРТУАЛЬНЫХ ФУНКЦИЙ, АДРЕС НАЧАЛА КОТОРОЙ МОЖНО НАЙТИ ПО УКАЗАТЕЛЮ vptr. В СВОЮ ОЧЕРЕДЬ, ЭТОТ УКАЗАТЕЛЬ ДОСТУПЕН ПО УКАЗАТЕЛЮ pObj, НАСТРОЕННОМУ НА ОБЪЕКТ elem. ФУНКЦИИ ПЕРЕДАЁТСЯ ДВА ПАРАМЕТРА, ПЕРВЫЙ ИЗ КОТОРЫХ ЯВЛЯЕТСЯ АДРЕСОМ ОБЪЕКТА elem (значение для this указателя), ВТОРОЙ - ЦЕЛОЧИСЛЕННЫМ ЗНАЧЕНИЕМ, РАВНЫМ 1.

Есть также чисто виртуальные функции - это функции, обьявленные в классе как виртуальные, но не имеющие описания. Класс, в котором есть хоть одна чисто виртуальная функция называется абстрактным классом. Нельзя создать объект абстрактного класса. Он может быть базовым классом, через указатель которого обращаются к потомкам. Для объявления чисто виртуальной функции используется следующая форма:
  virtual тип имя-функции(список параметров) = 0; 

наверх.

2. Бывает ли виртуальный конструктор, деструктор.

Виртуальных конструкторов не бывает, деструкторы бывают. Более того, если используется наследование, то деструктор обязан быть виртуальным, иначе произойдёт утечка памяти. Это происходит из-за того, что компилятор не знает что указатель базового класса указывает на производный и удаляет базовый класс. Если же деструтор базового класса объявлен как виртуальный, то в таблице виртуальных функций базового класса будет содержаться указатель на деструктор производного класса.
Пример:
	base *p = new derived;
	....
	delete p; // (*( p->_vtable[DESTRUCTOR_SLOT] ))(p); 

наверх.

3. Можно ли в конструкторе вызвать виртуальную функцию.

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

наверх.

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


Эти классы нельзя друг от друга наследовать при такой постановке задачи. Нарушается принцип подстановки Лисков(Подклассы должны быть взаимозаменяемы для своих родительских классов). Можно сделать интерфейс с методом вычисления площади. И реализовать его в каждом из классов.

наверх.

5. Написать функцию которая переворачивает строку(1-ый эелемент становится последним, 2-предпоследним...).

В библиотеке С уже есть такая функция - char *_strrev( char *string ). Но если нужно написать, то ниже код:
// при условии что нельзя пользоваться функциями рабты со строками и функциями работы с памятью (memcmp, memcpu и т.д.)
  void reverse_string(char *string){
	int size = 0;
	while(string[size]!= 0)
	{
		size++;
	}
	if(size != 0)
	{
		char* result = &string[size-1];
		for(int i = 0;i<(size/2);i++)
		{
        		char vs = *result;
			*result = string[i];
			string[i] = vs;
			result--;
		}
	}
 }

наверх.

6. На некий вход из потока поступает бесконечная последовательность символов. Все символы встречаются чётное колличесво раз, кроме одного. Как найти этот символ?

Для упрощения рассмотрим функцию которой на вход подают произвольный набор символов, среди которых 1 встречается нечётное колличество раз, а остальные чётное.
 Искомая функция:
	char In_Out(char* in)
	{
		char res = 0;
		for(int i = 0;;i++)
		{
			if(in[i] == 0) break;
			res ^= in[i];
		}
  		return res;
	}

наверх.

7. Найти все простые числа от 0 до 100000.

Сначало надо написать функцию определиющую является ли число n простым. Надо оценить верхнюю и нижнюю границы для перебора чисел на которые оно делится. Не стоит проверять делимость числа n на делители, большие, чем n / 2. Если бы там были интересующие нас делители, то дополнительными делителями были бы числа (n / i) < 2, а они не целые! Будем проверять делимость на числа i в порядке возрастания значений i. Пусть i >= sqrt(n) и является делителем числа n, тогда целым делителем должно быть и число n/i. Таким образом, множитель n/i будет обнаружен никак не позже, чем i, и поэтому множитель i проверять уже не надо. Итак, надо проверить, делится ли n на числа i, принимающие все значения от 2 до sqrt(n).
  bool Simple(int number)
    {
	bool ret_val = true;
	int n = (int)sqrt(number);
	for(int i = 2;i<=n;i++){
		if((number%i)==0)
		{
			ret_val = false;			
			break;
		}
	}
	return ret_val;
    }

Решето Эратосфена.

Этот древний грек (3 век до н.э.) придумал способ получения всех простых чисел, не превосходящих данного числа. Когда надо найти все такие числа, его алгоритм оказывается намного экономнее, чем если просто проверять каждое число на простоту функцией IsSimple. Вот, например, как получить все простые числа до 30:
1) Выписываются подряд все натуральные числа от 1 до данного.
2) 1 - не простое число. Вычеркиваем его.
3) Из следующих за ним чисел берем первое невычеркнутое. Оно простое. Оставляем его.
4) вычеркиваем все кратные ему.
5) Было что-то вычеркнуто? Если да, то возвращаемся к последнему подчеркнутому числу и повторяем 3).
Если нет, то... оставшиеся невычеркнутыми числа, подчеркнутые или нет, все суть простые.
    bool Simple(int number)
    {
	bool ret_val = true;
	int n = (int)sqrt(number);
	for(int i = 2;i<=n;i++){
		if((number%i)==0)
		{
			ret_val = false;			
			break;
		}
	}
	return ret_val;
    }

    void Eratosfen(int begin,int end,int* &retval)
    {
	int* simplnum = new int[end-begin+1];
	int size = 0;
	for(int i = begin;i<=end;i++)
	{
		if(Simple(i))
		{
			simplnum[size] = i;
			size++;
		}
	}
	retval = new int[size];
	size--;
	while(size>=0){
		retval[size] = simplnum[size];
		size--;
	}
	delete simplnum;
    }

    int main()
    {
    	int* retval = 0;
	Eratosfen(0,100000,retval);
	delete retval;
	retval = 0;
    }

наверх.

8. Написать класс двумерный массив. Реализовать в нём обращение к елементу массива. Запись нового значения и получение значения.

Двумерный массив - это массив где каждый элемент которого есть одномерный массив. Реализация двумерного массива делается с помощью 2-х классов, представляющих одномерный массив каждый.
	template 
	class Matrix  // Класс который является одномерным массивом-элементом
		      // двумерного массива 
	{
		   int mY; // колличество элементов в одномерном массиве
		   T *point_to_Mass; // непосредственно одномерный массив
		public:
		   Matrix(int maxY);
		   Matrix(const Matrix& ctVal);
 		   virtual ~Matrix();

		   T& operator[](int x);
		   const T& operator[](int x) const; // нужен для const доступа 
						     //	к элементам 
					             // Например:
					//void myproc(const MatrixOut& m) {
					//	  int a = m[5][5];
					//}
 	
	};

	template 
	class MatrixOut // Класс который является хранилищем одномерных 
			// массивов-элементов двумерного массива 
	{
		int number; //колличество одномерных массивов
		int size;   //количество элементов в одномерном массиве
		void Retrive(int x,int y); //отслеживает неверные входные значения  
		Matrix**myMatrix;  // непосредственно хранилище
	
	     public:
		MatrixOut(int x,int y);
		MatrixOut(const MatrixOut& ctVal);
		Matrix& operator[](int x);
		const Matrix& operator[](int x) const; // нужен для const доступа 
						          // к элементам 
		~MatrixOut();
	
	};
	

	........
        //Реализация

	template 
	Matrix::Matrix(int maxY)
	{
		mY = maxY;
		point_to_Mass = new T[maxY];
		for(int i = 0;i
	Matrix::Matrix(const Matrix& ctVal)
	{
		point_to_Mass = new T[ctVal.mY];
		for(int i = 0;i
	Matrix::~Matrix()
	{
		delete[] point_to_Mass; 	
	};

	
	template 
	T& Matrix::operator[](int x)
	{
		if(x >= mY) throw "Index y more than size of array";
		T* nn = point_to_Mass + x-1;
		return *nn;
	}

	
	template 
	const T& Matrix::operator[](int x) const
	{
		if(x >= mY) throw "Index y more than size of array";
		T* nn = point_to_Mass + x-1;
		return *nn;
	}


	//-------------------------------- MatrixOut -------
	template 
	MatrixOut::MatrixOut(int x,int y)
	{
		Retrive(x,y);
		number = x;
		size = y;
		MatrixOut::myMatrix = new Matrix*[x];
		for(int i = 0;i< x;i++){	
			myMatrix[i] = new Matrix(y);
		}
	}

	
	template 
	MatrixOut::MatrixOut(const MatrixOut& ctVal)
	{
		MatrixOut::myMatrix = new Matrix*[ctVal.number];
		for(int i = 0;i< ctVal.number;i++)
		{	
			myMatrix[i] = new Matrix(ctVal.size);
			*myMatrix[i] = *(ctVal.myMatrix[i]);
		}
	}

	
	template  
	void MatrixOut::Retrive(int x,int y)
	{
		if(x<0 || y<0) throw "One or all parameters are illegal.";
	}
	

	template 
	Matrix& MatrixOut::operator[](int x)
	{
		if(x >= number) throw "Index x more than size of array"; 
		return *(myMatrix[x]);
	}
	

	template 
	const Matrix& MatrixOut::operator[](int x) const
	{
		if(x >= number) throw "Index x more than size of array"; 
		return *(myMatrix[x]);
	}


	template 
	MatrixOut::~MatrixOut()
	{
		for(int i = 0; i < number; i++){
			delete myMatrix[i]; 
		}
		delete myMatrix;
	}
	
	
	.......
	//Пример вызова

	MatrixOut elem(10,6);
	elem[5][5] = 7;
	int a = elem[5][5]; 
	.......	

наверх.

9. Зачем нужен спецификатор volatile, static_cast, reinterpret_cast, __single_inheritance, mutable, selectany, deprecated, explicit, naked.

VOLATILE - показывает что переменная может меняться фоновым процессом. Можно использовать для обменна данными между потоками. Определение - Атрибут volatile обозначает, что переменная может модифицироваться способом, неизвестным компилятору.
STATIC_CAST - позволяет преобразовывать типы, основываясь лишь на сведениях о типах выражений, известных во время компиляции. Иными словами, static_cast не проверяет типы выражений во время выполнения.
REINTERPRET_CAST - позволяет преобразовать совершенно не связанные между собой типы.
__SINGLE_INHERITANCE - элемент может иметь только одного прямого предка.
MUTABLE - Член класса ни при каких условиях не является const.
SELECTANY - позволяет упростить процесс инициализации глобальной переменой, объединив объявление и инициализацию прямо в заголовочном файле. __declspec(selectany) int x1=1;
DEPRECATED - объявление __declspec(deprecated) говорит о том что функция, переменная, класс и т.п. устарели и при попытки вызова выдают предупреждающее сообщение C4996.
EXPLICIT - запрещает неявное преобразование типа при присваивании классов.
	class C
	{
		public:
		explicit C(int i);
		
	};
	....
	C g = 6; // компилятор выдаст ошибку, можно только так C g(6)

NAKED - квалификатор __declspec (naked), помещение которого в заголовке определяемой функции запрещает генерацию пролога/эпилога функции - начальной (сохранение регистров, установка указателя кадра) и завершающей (восстановление регистров, команда возврата) последовательностей команд. Результат компиляции будет содержать только код, присутствующий в теле функции в явном виде. Это позволяет программисту выполнить нужные действия в необходимой последовательности, однако налагает требования по соблюдению внутренних правил языка. В частности, должны быть сохранены регистры EBX, ESI, EDI, EBP; при использовании параметров в такой функции должен быть явно установлен указатель кадра в регистре EBP, а при использовании локальных переменных - зарезервировано достаточное количество байтов в стеке. Для возврата из naked - функции в ее тело должна быть явно помещена команда _asm ret, если только функция не использует средств вроде VxDJmp, которые сами выполняют возврат в функцию, из которой был сделан вызов.

наверх.
Hosted by uCoz