Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Вопрос на собеседовании


Автор: Paspartu 21.9.2008, 13:54
Доброго времени суток.

Вот такой вопрос:

23.  Что будет напечатано в результате?

Код

class A
{
    public: virtual void msg () {cout << "classA ";}
};

class B : public A
{
    public: virtual void msg () {cout << "classB ";}
};

void f(A class) 
{
    class.msg();
}

int main() {

    A * pointer = new B;
   pointer ->msg();
    f(*pointer );
}



Ответ:

 c) classB classA

Как правильно обосновать ответ?

пробую так:

т.к. функция msg () является виртуальной в базовом классе A и она переопределена в производном классе B,
 унаследованном от класса A, то решение о том, какая ее версия будет вызвана определяется по типу объекта…
т.к. указатель на базовый класс можно использовать для ссылки на объекты любых других классов, выведенных 
из базового то A * pointer = new B, var – указывает на объект класса B, соответственно pointer->msg() приведет к 
вызову версии msg () для класса B. При вызове f(*pointer) в функции void f(A cl) будет времмено создан объект 
класса A и соответственно class.msg() приведет к вызову msg () для класса A. 

Что Вы думаете по этому поводу?


Автор: vinter 21.9.2008, 14:36
Цитата(Paspartu @  21.9.2008,  14:54 Найти цитируемый пост)
Что Вы думаете по этому поводу?

многабукафф.

Цитата(Paspartu @  21.9.2008,  14:54 Найти цитируемый пост)
Как правильно обосновать ответ?

в первом случае будет выведен classB, по принципу полиморфности. Во втором classA т.к обьект передаеться по значению, значит он будет обрезан до родителя smile

Автор: Mayk 21.9.2008, 15:43
Цитата(Paspartu @  21.9.2008,  17:54 Найти цитируемый пост)

Что Вы думаете по этому поводу?

compile time error в строчке 
Цитата(Paspartu @  21.9.2008,  17:54 Найти цитируемый пост)
void f(A class) 


Добавлено через 26 секунд
даже подсветка хворума показывает что этот код крив.

Автор: mes 21.9.2008, 17:49
Цитата(Mayk @  21.9.2008,  15:43 Найти цитируемый пост)
void f(A class) 

по смыслу название переменной "class" тоже неправильно: как параметр передается объект, а не класс... ;)

Автор: Paspartu 21.9.2008, 20:46
с class я конечно загнул, аш самому смешно  smile 
в попыхах писал, код кривой, но важна суть...

с принципом полиморфности согласен, а чем обосновать ответ
< Во втором classA т.к обьект передаеться по значению, значит он будет обрезан до родителя >?
Стандарт?

Автор: vinter 21.9.2008, 21:15
Цитата(Paspartu @  21.9.2008,  21:46 Найти цитируемый пост)
Стандарт?

стандарт я не читал ниразу smile А зачем обосновывать? обосновывают решения, а не прописные истины.

Автор: mes 21.9.2008, 21:21
Цитата(Paspartu @  21.9.2008,  20:46 Найти цитируемый пост)
а чем обосновать ответ< Во втором classA т.к обьект передаеться по значению, значит он будет обрезан до родителя >?

Очень просто, при полиморфном вызове используется указатель на VMT, который устанавливается конструктором.
Т.е надо "посмотреть" чьим конструктором создан объект и станет ясно , какой метод вызовется.
Код

A * pointer = new B; // oбъект создан конструктором класса B

void f(A a)  // передача по значению говорит о том что будет создана копия, используя конструктор класса A
{
 a.msg;   
}

void f1(A* a)  // передача по указателю говорит о том что будет будет использован "оригинал" и зависит от того каким конструктором был создан
{
 a->msg;   
}


Автор: Paspartu 21.9.2008, 21:28
Так, понятно, огромное спасибо!

Вот еще вопрос

дано:

char* str = "12345";

и усе!

перевернуть все символы не используя буфер т.е. получить я так понял указатель на "54321", по google - л, но хочется понять принцип, и как зная только лишь указатель это можно сделать?

Автор: MAKCim 21.9.2008, 21:42
Paspartu, 
никак в такой формулировке

Автор: Paspartu 21.9.2008, 21:47
Вот и я голову ломаю, с массивом все понятно, а так через указатель?
В любом случае нужен буфер, хотя в задании четко прописано - дополнительный буфер не использовать!?
Во засада! 

Автор: vinter 21.9.2008, 21:48
Код

char str[] = "12345";
for(size_t i = 0; i < strlen(str)/2; ++i )
    std::swap( str[i], str[ strlen(str) - 1 - i] );


Paspartu, 
Цитата(Paspartu @  21.9.2008,  22:28 Найти цитируемый пост)
char* str = "12345";

при такой записи вероятно, что нельзя будет сделать, т.к очень часто это воспринимается компилятором как const char*

Автор: Rpahut 21.9.2008, 23:52
Цитата

В любом случае нужен буфер

Код

// swap values
// [0]<->[4]
str[0] ^= str[4];
str[4] ^= str[0];
str[0] ^= str[4];
// [1]<->[3]
str[1] ^= str[3];
str[3] ^= str[1];
str[1] ^= str[3];


Автор: J0ker 22.9.2008, 03:29
Цитата(Paspartu @  21.9.2008,  20:46 Найти цитируемый пост)
а чем обосновать ответ
< Во втором classA т.к обьект передаеться по значению, значит он будет обрезан до родителя >?
Стандарт? 

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

Автор: J0ker 22.9.2008, 03:51
Цитата(Rpahut @ 21.9.2008,  23:52)
Цитата

В любом случае нужен буфер

Код

// swap values
// [0]<->[4]
str[0] ^= str[4];
str[4] ^= str[0];
str[0] ^= str[4];
// [1]<->[3]
str[1] ^= str[3];
str[3] ^= str[1];
str[1] ^= str[3];

как написал vinter это либо не будет компилиться, либо приведет к undefined behavior т.к. по стандарту строковые литералы - константы (2.3.14)

Автор: Rickert 22.9.2008, 04:08
С указателем никак, а если через буффер, то ещё можно так:
Код
//=============================================================================

#include <stdio.h>
#include <string.h>

//=============================================================================

int main(int argc, char** argv)
{
  char str[] = "12345";
  size_t max = strlen(str);

  for (size_t i = 0; i < max/2; ++i)
  {
    str[i] = str[i] + str[max - (i + 1)];
    str[max - (i + 1)] = str[i] - str[max - (i + 1)];
    str[i] = str[i] - str[max - (i + 1)];
  }

  return 0;
}

//=============================================================================

Автор: J0ker 22.9.2008, 04:29
Цитата(Rickert @  22.9.2008,  04:08 Найти цитируемый пост)

Код

    str[i] = str[i] + str[max - (i + 1)];
    str[max - (i + 1)] = str[i] - str[max - (i + 1)];
    str[i] = str[i] - str[max - (i + 1)];

Код

str[i] ^= str[max - (i + 1)] ^= str[i] ^= str[max - (i + 1)];

Автор: Rickert 22.9.2008, 05:01
Кто больше? smile 

Автор: J0ker 22.9.2008, 05:07
Цитата(Rickert @ 22.9.2008,  05:01)
Кто больше? smile

нет
у кого длиннее

Автор: Torsten 22.9.2008, 12:24
Цитата(Paspartu @  21.9.2008,  21:28 Найти цитируемый пост)
дано:char* str = "12345";и усе!перевернуть все символы не используя буфер т.е. получить я так понял указатель на "54321", по google - л, но хочется понять принцип, и как зная только лишь указатель это можно сделать?

хм ... в каком то из недавних номеров журнала хакер (летнем)  КК показыавл как это сделать.

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

Автор: UnrealMan 22.9.2008, 13:04
Цитата(J0ker @ 22.9.2008,  05:29)
Код

str[i] ^= str[max - (i + 1)] ^= str[i] ^= str[max - (i + 1)];

Тут undefined behavior.

Автор: J0ker 22.9.2008, 17:58
Цитата(UnrealMan @ 22.9.2008,  13:04)
Цитата(J0ker @ 22.9.2008,  05:29)
Код

str[i] ^= str[max - (i + 1)] ^= str[i] ^= str[max - (i + 1)];

Тут undefined behavior.

в каком месте?

Автор: UnrealMan 23.9.2008, 14:08
Между двумя соседними точками следования нельзя дважды модифицировать один и тот же скалярный объект.

Автор: J0ker 23.9.2008, 18:13
Цитата(UnrealMan @ 23.9.2008,  14:08)
Между двумя соседними точками следования нельзя дважды модифицировать один и тот же скалярный объект.

здесь нет соседних точек:
Код

str[i].operator^=( str[max - (i + 1)].operator^=( str[i].operator^=( str[max - (i + 1)] ) ) );

так что всё тип-топ

Автор: UnrealMan 24.9.2008, 01:31
Цитата(J0ker @ 23.9.2008,  19:13)
Цитата(UnrealMan @ 23.9.2008,  14:08)
Между двумя соседними точками следования нельзя дважды модифицировать один и тот же скалярный объект.

здесь нет соседних точек:
Код

str[i].operator^=( str[max - (i + 1)].operator^=( str[i].operator^=( str[max - (i + 1)] ) ) );

Ну, тут-то их конечно нет, т.к. такая программа некорректна smile

Что касается предыдущего твоего примера, то там две соседние точки следования обязательно будут. Первая точка следования будет либо такой:

Цитата(1.9/17)
When calling a function (whether or not the function is inline), there is a sequence point after the evaluation
of all function arguments (if any) which takes place before execution of any expressions or statements in
the function body.

либо такой:

Цитата(1.9/16)
There is a sequence point at the completion of evaluation of each full-expression

Следующая точка следования будет согласно абзацу 1.9/16.

Функция, о которой идёт речь при упоминании случая с 1.9/17, - это та, что содержит expression-statement, коим является твой кусок кода.

Цитата(J0ker @ 23.9.2008,  19:13)
так что всё тип-топ

То есть хреново smile

Автор: J0ker 24.9.2008, 05:08
изучаю вопрос

Автор: J0ker 24.9.2008, 07:09
да, согласен, был неправ (5/4)
плюсик вам
Цитата(UnrealMan @  24.9.2008,  01:31 Найти цитируемый пост)
Ну, тут-то их конечно нет, т.к. такая программа некорректна smile

программа корректна - эквивалентность некорректна - прояснилось из 5/2

Однако хочу уточнить - согласно тому-же 5/2 - при перегруженном операторе ^= undefined behavior исчезает. Я правильно понимаю?

UPD упс, не могу поставить плюсик. Обязательно поставлю когда наберу 100 постов   smile 

Автор: vinter 24.9.2008, 08:50
Цитата(J0ker @  24.9.2008,  08:09 Найти цитируемый пост)
UPD упс, не могу поставить плюсик. Обязательно поставлю когда наберу 100 постов 

поставил

Автор: UnrealMan 24.9.2008, 12:46
Цитата(J0ker @  24.9.2008,  08:09 Найти цитируемый пост)
программа корректна 

Напомню, что в теме речь шла про массивы char-ов, а с char-ами такая бодяга не пройдёт.

Цитата(J0ker @  24.9.2008,  08:09 Найти цитируемый пост)
Однако хочу уточнить - согласно тому-же 5/2 - при перегруженном операторе ^= undefined behavior исчезает. Я правильно понимаю?

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

Цитата(1.9/17)
When calling a function (whether or not the function is inline), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body. There is also a sequence point after the copying of a returned value and before the execution of any expressions outside the function

Автор: J0ker 24.9.2008, 16:24
Цитата(UnrealMan @  24.9.2008,  12:46 Найти цитируемый пост)
Напомню, что в теме речь шла про массивы char-ов, а с char-ами такая бодяга не пройдёт.

именно это и ввело меня в заблуждение - считал операторы простых типов полностью эквивалентными по реализации операторам классов (за исключением операторов || и &&). Естественно я понимаю что .operator op к простым типам не применим синтаксически.


Цитата(vinter @  24.9.2008,  08:50 Найти цитируемый пост)
поставил 

спасибо  smile 

Автор: Maka6er 24.9.2008, 18:57
И я нас... сделаю свой вклад smile С указателем, без буфера:
Код

void rev(char *str)
{
    char *start = str;
    for(;*str;str++)
        ;
    for(--str;start < str; --str, start++) {
        *str = *str^*start;
        *start = *str^*start;
        *str = *str^*start;
    }
}

Автор: Annihilator 24.9.2008, 19:54
Цитата(Maka6er @  24.9.2008,  22:57 Найти цитируемый пост)
char *start = str;

а где память под start выделяешь??? Без буфера - никак
Цитата(Maka6er @  24.9.2008,  22:57 Найти цитируемый пост)

Код

for(--str;start < str; --str, start++)
 {
        *str = *str^*start;
        *start = *str^*start;
        *str = *str^*start;    
}


а про strrev(char* s) все дружно забыли. Но опять же без буфера никуда

Автор: mes 24.9.2008, 21:03

Цитата(Annihilator @  24.9.2008,  19:54 Найти цитируемый пост)
а где память под start выделяешь??? Без буфера - никак

посмотрите внимательнее код - память для start не нужна - он используется как указатель на основную строку,
равноправный указателю str, под который тоже память нигде не выделялась )

а технология используется следующая :
Код


int x=5;
int y=7;

x ^=y;
y ^=x;
x ^=y;

cout <<"x="<<x<<endl; // 7
cout <<"y="<<y<<endl; // 5


как видете ни одного стороннего байта не используются. Такое возможно благодаря некоторому свойству оператора XOR.

Автор: vinter 24.9.2008, 21:15
ну это все равно не рабочий вариант, считай у нас дано const char *

Автор: mes 24.9.2008, 21:37
Цитата(vinter @  24.9.2008,  21:15 Найти цитируемый пост)
ну это все равно не рабочий вариант, считай у нас дано const char * 

ну  const char* вобще повернуть нельзя , хоть с буфером хоть без. Можно только получить "повернутую" копию. 
Так что, ИMXO, в задании просто опечатка.


Автор: Maka6er 25.9.2008, 12:23
Цитата(Annihilator @  24.9.2008,  19:54 Найти цитируемый пост)
а где память под start выделяешь???

А разве обязательно под указатель выделять память? Указатель должен указывать на область в памяти. Я устанавливаю один указатель на начало строки, один - на конец.
Цитата(vinter @  24.9.2008,  21:15 Найти цитируемый пост)
ну это все равно не рабочий вариант, считай у нас дано const char * 

const char * - это указатель на строковую константу (строковый литерал), а константу изменить нельзя.

Автор: W4FhLF 25.9.2008, 13:02
Цитата(Maka6er @  25.9.2008,  12:23 Найти цитируемый пост)
const char * - это указатель на строковую константу (строковый литерал), а константу изменить нельзя.


А vinter что имел ввиду? 

Автор: Maka6er 25.9.2008, 13:38
Цитата(W4FhLF @  25.9.2008,  13:02 Найти цитируемый пост)
А vinter что имел ввиду?  

Сори, туплю. Я имел ввиду из задания: char* str = "12345";

Автор: serg76 26.9.2008, 13:24
Код

  char *str = "12345";
  *(str)^=*(str+4);
  *(str+4)^=*(str);
  *(str)^=*(str+4);
  *(str+1)^=*(str+3);
  *(str+3)^=*(str+1);
  *(str+1)^=*(str+3);

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

Автор: J0ker 26.9.2008, 17:48
Цитата(serg76 @ 26.9.2008,  13:24)
Код

  char *str = "12345";
  *(str)^=*(str+4);
  *(str+4)^=*(str);
  *(str)^=*(str+4);
  *(str+1)^=*(str+3);
  *(str+3)^=*(str+1);
  *(str+1)^=*(str+3);

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

ты бы почитал сначала обсуждение
этот код в большинстве случаев приведет к Segmentation fault и Access violation

Автор: serg76 27.9.2008, 10:49
Цитата

ты бы почитал сначала обсуждение
этот код в большинстве случаев приведет к Segmentation fault и Access violation


а где приведет то, конкретнее?

зы насчет const наскока помню в С через указатели можно все менять
для примера к коду выше
заменить
Код
char *str= "12345";

на
Код

  const char str1[] = "12345";
  char *str=(char *)str1;

Автор: mes 27.9.2008, 11:54
Цитата(serg76 @  27.9.2008,  10:49 Найти цитируемый пост)
зы насчет const наскока помню в С через указатели можно все менять

Цитата(serg76 @  27.9.2008,  10:49 Найти цитируемый пост)
  char *str=(char *)str1;

При таком подходе Вы надеетесь, что даные размещенные по указателю str1, размещаются в пользовательской области с правом записи.
Однако строчка :
Цитата(serg76 @  27.9.2008,  10:49 Найти цитируемый пост)
 const char str1[] = "12345";

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

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

Автор: J0ker 27.9.2008, 19:04
Цитата(serg76 @  27.9.2008,  10:49 Найти цитируемый пост)
а где приведет то, конкретнее?

здесь:
Код

*(str)^=*(str+4);

строковые литералы явяются константами и могут размещаться в защищенном от записи сегменте
возможность присвоения их не константам - тяжелое наследие прошлого

Добавлено @ 19:07
объявление типа
Код

char str[] = "123456";

размещает массив на стеке - что позволяет менять его содержимое

Добавлено @ 19:17
Цитата(mes @  27.9.2008,  11:54 Найти цитируемый пост)
Однако строчка :
Код

 const char str1[] = "12345";

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

это говорит компилятору, что str константный массив. Но размещение его будет на стеке (потому, что это массив - он просто с инициализатором)
выражение:
Код

const char *str = "12345";

говорит о том, что str указатель на константную строку, инициализируемый АДРЕСОМ строкового литерала, который возможно находится в защищенном от записи сегменте
как грится "почувствуйте разницу"

Автор: W4FhLF 28.9.2008, 06:25
Цитата(J0ker @  27.9.2008,  19:04 Найти цитируемый пост)
это говорит компилятору, что str константный массив. Но размещение его будет на стеке


Ну это смотря где его объявить. 

Автор: J0ker 28.9.2008, 06:55
Цитата(W4FhLF @ 28.9.2008,  06:25)
Цитата(J0ker @  27.9.2008,  19:04 Найти цитируемый пост)
это говорит компилятору, что str константный массив. Но размещение его будет на стеке


Ну это смотря где его объявить.

да
в функции
иначе в секции .rdata (защищенные от записи и исполнения инициализированные данные) и .data (защищенные от исполнения инициализированные данные) соответственно
хотя рояля это и не играет  smile 

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)