Interesting C/C++ interview questions

tchicago
Уже с Приветом
Posts: 1009
Joined: 16 Sep 2001 09:01
Location: USA

Post by tchicago »

шпиён wrote:С первого взгляда кажется просто.
1) Лишняя аллокация/деаллокация памяти. Да еще и лик может быть, если из catch своё исключение бросится до delete.
2) Лишнее копи-конструирование, что для приведенного в примере exep не хуже копирования указателя в случае 1, но для больших/сложных классов может быть дорого.
На мой взгляд, метод 2 однозначно лучше, т.к. за расчет на высокую скорость обработки исключений надо anyway бить лопатой по голове.


похоже на правду. Вот только в случае использования варианта c указателем видится мне в приведенном коде ошибка одна... Выплывет она не обязательно, но в общем, при использовании SEH есть вероятность что выплывет. Какая?
User avatar
roadman
Уже с Приветом
Posts: 707
Joined: 12 Mar 2003 22:29
Location: Moscow->Bay Area, CA

Post by roadman »

tchicago wrote:Вот только в случае использования варианта c указателем видится мне в приведенном коде ошибка одна... Выплывет она не обязательно, но в общем, при использовании SEH есть вероятность что выплывет. Какая?

Поскольку пользователь может создать класс нового exception exep_new на основе базового класса exep и при этом catch operator сработает также, однако delete операция вызовет деструктор базового класса. Ещё один пример, когда лучше объявить деструктор виртуальным в базовом классе
class exep
{
public:
exep(int err) : _error(err) {}
virtual ~exep() {}
int get_code() const { return _error; }
private:
int _error;
};
The philosophy of one century is the common sense of the next. --Henry Ward Beecher
User avatar
Boriskin
Уже с Приветом
Posts: 18906
Joined: 30 Aug 2001 09:01
Location: 3rd planet

Post by Boriskin »

tchicago wrote: Вот только в случае использования варианта c указателем видится мне в приведенном коде ошибка одна...


ИМХО, если при создании объекта через new чтото произойдет, то я не уверен, что catch поймает исключение.
Тупизна как Энтропия. Неумолимо растет.
User avatar
roadman
Уже с Приветом
Posts: 707
Joined: 12 Mar 2003 22:29
Location: Moscow->Bay Area, CA

Post by roadman »

Boriskin wrote:
tchicago wrote: Вот только в случае использования варианта c указателем видится мне в приведенном коде ошибка одна...


ИМХО, если при создании объекта через new чтото произойдет, то я не уверен, что catch поймает исключение.

Вы имеете в виду создание через new самого объекта exception?
То есть программа кинет exception в момент создания объекта exception.
В таком случае я не вижу разницы и в том и в другом случае catch не сработает, поскольку это будет другой тип exception.
The philosophy of one century is the common sense of the next. --Henry Ward Beecher
User avatar
Boriskin
Уже с Приветом
Posts: 18906
Joined: 30 Aug 2001 09:01
Location: 3rd planet

Post by Boriskin »

То есть программа кинет exception в момент создания объекта exception.


Или new кинет недостаток памяти, что, как мне кажется, и есть самая интересная ситуация, а искать - лениво. :mrgreen:
Тупизна как Энтропия. Неумолимо растет.
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Post by AndreyT »

roadman wrote:Поскольку пользователь может создать класс нового exception exep_new на основе базового класса exep и при этом catch operator сработает также, однако delete операция вызовет деструктор базового класса. Ещё один пример, когда лучше объявить деструктор виртуальным в базовом классе


Не совсем понимаю, почему это "еще один" пример. Виртуальный деструктор, с формальной точки зрения, нужен тогда и только тогда, когда объект класса удаляется вот таким вот полиморфным образом. Так что это не "еще один" пример. Это просто очередная вариация одного и того же примера.
Best regards,
Андрей
tchicago
Уже с Приветом
Posts: 1009
Joined: 16 Sep 2001 09:01
Location: USA

Post by tchicago »

AndreyT wrote:Не совсем понимаю, почему это "еще один" пример. Виртуальный деструктор, с формальной точки зрения, нужен тогда и только тогда, когда объект класса удаляется вот таким вот полиморфным образом. Так что это не "еще один" пример. Это просто очередная вариация одного и того же примера.


вот типичная ситуация:

Code: Select all

class exep 
{
public:
   exep(int err) : _error(err) {}
   int get_code() const { return _error; }
private:
   int _error;
};

class SomeLibraryClass
{
public:
   ...
   virtual void do_something()
   {
      // Does something
      ...
      throw new exep( 1 );
      ...
   }
   ...
private:
   void do_something_internally()
   {
      ...
      try
      {
         ...
         do_something();
         ..
      }
      catch( exep* ex )
      {
         ...
         delete ex;
      }
   }
};



Поскольку SomeLibraryClass подразумевает дальнейшее наследование, то вполне возможно, что автор унаследованного класса захочет определить и свои исключения. Они будут унаследованы от исключений, используемых данным классом, чтобы базовый класс мог их корректно обрабатывать. Не определив виртуальный деструктор для базового исключения мы таким образом запрещаем потомку exep иметь свой деструктор. А если потомок его определит, и будет надеяться, что он будет вызван - looming disaster.
User avatar
шпиён
Уже с Приветом
Posts: 3459
Joined: 29 Oct 2002 20:08
Location: US

Post by шпиён »

tchicago wrote:
Поскольку SomeLibraryClass подразумевает дальнейшее наследование, то вполне возможно, что автор унаследованного класса захочет определить и свои исключения. Они будут унаследованы от исключений, используемых данным классом, чтобы базовый класс мог их корректно обрабатывать. Не определив виртуальный деструктор для базового исключения мы таким образом запрещаем потомку exep иметь свой деструктор. А если потомок его определит, и будет надеяться, что он будет вызван - looming disaster.


Поэтому бросать и ловить исключение по указателю - изврат.
tchicago
Уже с Приветом
Posts: 1009
Joined: 16 Sep 2001 09:01
Location: USA

Post by tchicago »

шпиён wrote:Поэтому бросать и ловить исключение по указателю - изврат.


Не изврат, а вполне разумная оптимизация.
А вот пользоваться char* строками программируя на c++ - точно изврат. :^P
User avatar
шпиён
Уже с Приветом
Posts: 3459
Joined: 29 Oct 2002 20:08
Location: US

Post by шпиён »

tchicago wrote:
шпиён wrote:Поэтому бросать и ловить исключение по указателю - изврат.


Не изврат, а вполне разумная оптимизация.
А вот пользоваться char* строками программируя на c++ - точно изврат. :^P


Оптимизация чего? 8O
tchicago
Уже с Приветом
Posts: 1009
Joined: 16 Sep 2001 09:01
Location: USA

Post by tchicago »

шпиён wrote:Оптимизация чего? 8O


Количества вызываемых конструкторов копирования.

А насчет char* - сорри, это я кажется Вас с Фигли перепутал.
User avatar
шпиён
Уже с Приветом
Posts: 3459
Joined: 29 Oct 2002 20:08
Location: US

Post by шпиён »

tchicago wrote:
шпиён wrote:Оптимизация чего? 8O


Количества вызываемых конструкторов копирования.


Инлайновых, которые копируют только указатель на VMT (и, может быть, неколько байт доп. инфо)? ;) По сравнению с отведением и освобождением памяти на куче? ;)

И самое главное - зачем? Уж не думаете ли Вы, что обработка исключения сравнима с копированием нескольких байт? И её стоит иметь в performance-critical потоке исполнения? И тем более в комплекте с аллокациями-деаллокациями? :pain1:
tchicago
Уже с Приветом
Posts: 1009
Joined: 16 Sep 2001 09:01
Location: USA

Post by tchicago »

шпиён wrote:
tchicago wrote:
шпиён wrote:Оптимизация чего? 8O


Количества вызываемых конструкторов копирования.


Инлайновых, которые копируют только указатель на VMT (и, может быть, неколько байт доп. инфо)? ;) По сравнению с отведением и освобождением памяти на куче? ;)

И самое главное - зачем? Уж не думаете ли Вы, что обработка исключения сравнима с копированием нескольких байт? И её стоит иметь в performance-critical потоке исполнения? И тем более в комплекте с аллокациями-деаллокациями? :pain1:


Зависит от:

a) Сложности объекта исключения. Во многих случаях это не просто несколько байт памяти.
б) Архитектуры приложения/библиотеки. Некоторые библитеки действительно используют механизм исключений для доставки пользователю результатов работы (с точки зрения разработчика библиотеки это исключительная ситуация, с точки зрения ее пользователя - нет)
в) Наличия собственного аллокатора памяти или свойств стандартного.
User avatar
A. Fig Lee
Уже с Приветом
Posts: 12072
Joined: 17 Nov 2002 03:41
Location: английская колония

Post by A. Fig Lee »

tchicago wrote:
шпиён wrote:Поэтому бросать и ловить исключение по указателю - изврат.


Не изврат, а вполне разумная оптимизация.
А вот пользоваться char* строками программируя на c++ - точно изврат. :^P

Ну если на одном С++ программируешь... А если прыгаешь с С на С++ и обратно...
Лучше уж одного стиля придерживатся.

Code: Select all

#ifndef COPY_STRING
#include <string.h>
#ifdef __cplusplus
#define COPY_STRING(TO, FROM)   {int this_len = TO?strlen(TO):0;\
   int that_len = FROM?strlen(FROM):0;\
   if (TO &&  (!FROM || (FROM && (that_len > this_len))))\
   {\
      delete [] TO; TO=NULL;\
   }\
   if (!FROM)\
   {\
      TO = NULL;\
   }else if (!TO)\
      TO = new char[that_len + 1];\
   if (FROM)\
      strcpy(TO, FROM);}

#define MOVE_STRING(TO, FROM) if (TO) delete [] TO; TO = FROM; FROM = NULL;
#else
#include <stdlib.h>
#define COPY_STRING(TO, FROM)   {int this_len = TO?strlen(TO):0;\
   int that_len = FROM?strlen(FROM):0;\
   if (TO &&  (!FROM || (FROM && (that_len > this_len))))\
   {\
      free((void*) TO); TO=NULL;\
   }\
   if (!FROM)\
   {\
      TO = NULL;\
   }else if (!TO)\
      TO = (char*) malloc(that_len + 1);\
   if (FROM)\
      strcpy(TO, FROM);}

#define MOVE_STRING(TO, FROM) if (TO) free((void*) TO); TO = FROM; FROM = NULL;

#endif
#endif
Верить нельзя никому - даже себе. Мне - можно!
User avatar
шпиён
Уже с Приветом
Posts: 3459
Joined: 29 Oct 2002 20:08
Location: US

Post by шпиён »

tchicago wrote:
шпиён wrote:
tchicago wrote:
шпиён wrote:Оптимизация чего? 8O


Количества вызываемых конструкторов копирования.


Инлайновых, которые копируют только указатель на VMT (и, может быть, неколько байт доп. инфо)? ;) По сравнению с отведением и освобождением памяти на куче? ;)

И самое главное - зачем? Уж не думаете ли Вы, что обработка исключения сравнима с копированием нескольких байт? И её стоит иметь в performance-critical потоке исполнения? И тем более в комплекте с аллокациями-деаллокациями? :pain1:


Зависит от:

a) Сложности объекта исключения. Во многих случаях это не просто несколько байт памяти.
б) Архитектуры приложения/библиотеки. Некоторые библитеки действительно используют механизм исключений для доставки пользователю результатов работы (с точки зрения разработчика библиотеки это исключительная ситуация, с точки зрения ее пользователя - нет)


А причем тут производительность? При таких условиях о ней УЖЕ мечтать не приходится.

tchicago wrote:в) Наличия собственного аллокатора памяти или свойств стандартного.


Вам известны аллокаторы, производительность которых (на new И на delete) сравнима с отсутсвием аллокации? ;)
tchicago
Уже с Приветом
Posts: 1009
Joined: 16 Sep 2001 09:01
Location: USA

Post by tchicago »

A. Fig Lee wrote:Ну если на одном С++ программируешь... А если прыгаешь с С на С++ и обратно...
Лучше уж одного стиля придерживатся.

Code: Select all

#ifndef COPY_STRING
...
#endif


Unrelated. Чето я смотрел-смотрел на это, но так и не понял зачем делать это различие между c и с++. У Вас new/delete переопределены?
tchicago
Уже с Приветом
Posts: 1009
Joined: 16 Sep 2001 09:01
Location: USA

Post by tchicago »

шпиён wrote:
tchicago wrote:Зависит от:

a) Сложности объекта исключения. Во многих случаях это не просто несколько байт памяти.
б) Архитектуры приложения/библиотеки. Некоторые библитеки действительно используют механизм исключений для доставки пользователю результатов работы (с точки зрения разработчика библиотеки это исключительная ситуация, с точки зрения ее пользователя - нет)


А причем тут производительность? При таких условиях о ней УЖЕ мечтать не приходится.


Привести пример приложения? Таковые есть, и производительность тоже требуется. И достигается.

tchicago wrote:в) Наличия собственного аллокатора памяти или свойств стандартного.


Вам известны аллокаторы, производительность которых (на new И на delete) сравнима с отсутсвием аллокации? ;)


неверно поставлен вопрос. Надо так:
"Вам известны аллокаторы, производительность которых (на new И на delete) сравнима с производительностью конструктора копирования (и деструктора)?"
User avatar
roadman
Уже с Приветом
Posts: 707
Joined: 12 Mar 2003 22:29
Location: Moscow->Bay Area, CA

Post by roadman »

AndreyT wrote:
roadman wrote:Поскольку пользователь может создать класс нового exception exep_new на основе базового класса exep и при этом catch operator сработает также, однако delete операция вызовет деструктор базового класса. Ещё один пример, когда лучше объявить деструктор виртуальным в базовом классе


Не совсем понимаю, почему это "еще один" пример. Виртуальный деструктор, с формальной точки зрения, нужен тогда и только тогда, когда объект класса удаляется вот таким вот полиморфным образом. Так что это не "еще один" пример. Это просто очередная вариация одного и того же примера.


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

А вот ещё вопрос по С++:

Могу я определить фукцию класса и virtual и inline одновременно, ну что-то типа:
class A
{
public:
inline virtual int get_type() { return 0; }
};

Вопрос(ы):
1. Если не могу, то почему?
2. Если могу, то будет ли inline работать или компилятор проигнорирует inline?
Функция маленькая и без virtual компилятор на 100% сделает её inline.
The philosophy of one century is the common sense of the next. --Henry Ward Beecher
User avatar
AndreyT
Уже с Приветом
Posts: 3000
Joined: 14 Apr 2004 01:11
Location: SFBA (было: Минск, Беларусь)

Post by AndreyT »

roadman wrote:А вот ещё вопрос по С++:

Могу я определить фукцию класса и virtual и inline одновременно, ну что-то типа:
class A
{
public:
inline virtual int get_type() { return 0; }
};


Да, можешь. В данном конкретном примере слово 'inline' излишне, ибо функция, определенная внутри определения класса и так является 'inline', но ошибки в этом нет.

Вопрос(ы):
1. Если не могу, то почему?


N/A, ибо можешь.

2. Если могу, то будет ли inline работать или компилятор проигнорирует inline?


Встраиваемость 'inline' функций в С++ проявляется на per-call basis. Т.е. один вызов одной и той же функции может встроиться, а другой - нет. Какой вызов встроится, а какой нет - зависит от условий, в которых происходит вызов, а также от способностей компилятора.

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

В тех же ситуациях, когда конкретноая целевая функция ясна уже в compile-time (известен динамический тип объекта или функция вызвана через квалифицированное имя) ничто не мешает компилятору выполнить встраивание. Большинство компиляторов так и поступает.
Best regards,
Андрей
User avatar
A. Fig Lee
Уже с Приветом
Posts: 12072
Joined: 17 Nov 2002 03:41
Location: английская колония

Post by A. Fig Lee »

tchicago wrote:
A. Fig Lee wrote:Ну если на одном С++ программируешь... А если прыгаешь с С на С++ и обратно...
Лучше уж одного стиля придерживатся.

Code: Select all

#ifndef COPY_STRING
...
#endif


Unrelated. Чето я смотрел-смотрел на это, но так и не понял зачем делать это различие между c и с++. У Вас new/delete переопределены?

Ну дак в одном случае бай дефоулт используем delete, а в С - используем free.
Поетому и разные. Чтоб не помнить как удалять. Чтоб не думать, когда программируешь. :wink:
Верить нельзя никому - даже себе. Мне - можно!
ig
Уже с Приветом
Posts: 491
Joined: 09 Apr 2000 09:01
Location: Tigard, OR

Post by ig »

roadman wrote:
class A
{
public:
inline virtual int get_type() { return 0; }
};

Вопрос(ы):
1. Если не могу, то почему?
2. Если могу, то будет ли inline работать или компилятор проигнорирует inline?
Функция маленькая и без virtual компилятор на 100% сделает её inline.

По идеи, если объект не вызывается через указатель или референс, то компилятор может встроить такую функцию (inline virtual).
User avatar
roadman
Уже с Приветом
Posts: 707
Joined: 12 Mar 2003 22:29
Location: Moscow->Bay Area, CA

Post by roadman »

AndreyT wrote:В тех же ситуациях, когда конкретноая целевая функция ясна уже в compile-time (известен динамический тип объекта или функция вызвана через квалифицированное имя) ничто не мешает компилятору выполнить встраивание. Большинство компиляторов так и поступает.

AndreyT, если Вы и дальше будете быстро и исчерпывающе отвечать на вопросы, то их будет неинтересно задавать :D . В футбол не играете?
The philosophy of one century is the common sense of the next. --Henry Ward Beecher
User avatar
adb
Уже с Приветом
Posts: 9275
Joined: 14 Dec 2001 10:01
Location: Российская Федерация

Post by adb »

tchicago wrote:Количества вызываемых конструкторов копирования.

А насчет char* - сорри, это я кажется Вас с Фигли перепутал.


Хмм, а разве не для этого ловят исключения по ссылки?

Code: Select all

#include <string>

class myexception
{
public:

   myexception(){
       printf("ctor\n");
   }
   ~myexception(){
       printf("dtor\n");
   }

   myexception(const myexception& s){
       printf("ctor\n");
   }

private:
   std::string str;
};

void  foo1()
{
      throw myexception();
}

void foo()
{
   try{
    foo1();
   }catch(myexception &ex)
   {
       throw;
   }

}

void main()
{
   try{
    foo();
   }catch(myexception &ex)
   {
   }
}


Вывод при использовании &:
ctor
dtor

Без &:
ctor
ctor
ctor
dtor
dtor
dtor

Компилятор MSVC 6. Наверняка и стандарте что-нибудь есть по этому поводу, только лень искать.

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

P.S. Или я неправильно понял ваше утверждение?
tchicago
Уже с Приветом
Posts: 1009
Joined: 16 Sep 2001 09:01
Location: USA

Post by tchicago »

adb wrote:P.S. Или я неправильно понял ваше утверждение?


Оч странно. У меня все компиляторы вызывают конструктор копии. Один раз.
User avatar
adb
Уже с Приветом
Posts: 9275
Joined: 14 Dec 2001 10:01
Location: Российская Федерация

Post by adb »

tchicago wrote:
adb wrote:P.S. Или я неправильно понял ваше утверждение?


Оч странно. У меня все компиляторы вызывают конструктор копии. Один раз.


MSVC 7.0/Intel 8.0 у меня отработали также. Без копирования. Либо это оптимизация, либо несоотвествие стандарту.

У Меерса нашел это.
That leaves only catch-by-reference. Catch-by-reference suffers from none of the problems we have discussed. Unlike catch-by-pointer, the question of object deletion fails to arise, and there is no difficulty in catching the standard exception types. Unlike catch-by-value, there is no slicing problem, and exception objects are copied only once.

Return to “Работа и Карьера в IT”