Тут размещают свои топики новички в С/С++.

Тема в разделе "LANGS.C", создана пользователем nitrotoluol, 4 мар 2007.

  1. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    есть виртуальный class baseClass и есть class deriverClass : public baseClass;
    есть std::vector< baseClass * > _vector;
    он заполнился след. образом:
    Код (Text):
    1.     derivedClass * test1 = new derivedClass();
    2.     _vector.push_back( test1 );
    3.     baseClass *test2 = new derivedClass();
    4.         _vector.push_back( test2 );
    теперь нужно как-то при получении любого элемента из контейнера определить, является ли этот элемент derivedClass*, или baseClass*. не приводимым, _а именно является ли он _изначально_ derivedClass* или же baseClass*.
    понятно, что любой baseClass* можно привести к derivedClass*, и любой derivedClass* можно привести к baseClass*, если он действительно являлся baseClass*, пускай даже и был создан как new derivedClass(); тем самым, получается, что способ с dynamic_cast отпадает. или я ошибаюсь?

    подскажите правильное и простое решение, пожалуйста.

    понятно, что если бы тот же
    Код (Text):
    1. baseClass *test2 = new derivedClass();
    2. _vector.push_back( test2 );
    был заменен на
    Код (Text):
    1. baseClass *test2 = new baseClass();
    2. _vector.push_back( test2 );
    , то проблема бы решалась очень просто, через тот же typeinfo. но мне ужасно интересно знать, есть ли решение у оригинального куска кода выше.

    ps:
    для оригинального куска кода применяя typeinfo
    Код (Text):
    1. printf("%s\n", typeid(*_vector[ index ]).name() );
    получаем что все объекты у нас типа derivedClass* ... :-/
     
  2. Ustus

    Ustus New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2005
    Сообщения:
    834
    Адрес:
    Харьков
    varnie
    Виртуальных классов в цепепе нет. Есть только классы-виртуальные наследники базового.
    (Может имелся ввиду абстрактный? Вряд ли, объекты таких классов не создаются, тогда главный вопрос поста не имеет смысла)

    Элетенты контейнера всегда имеют один тип. Иначе получается кортеж, который в С++ можно сделать только через задницу. В данном случае - это тип baseClass*. А вот указывать такой элемент может на экземпляр baseClass или любого его наследника, в частности derivedClass.

    ...
    Тогда получился бы совсем другой вектор.
    Кстати, не принципиально:
    Код (Text):
    1. baseClass* z = new derivedClass();
    2. _vector.push_back(z);
    или
    Код (Text):
    1. derivedClass* z = new derivedClass();
    2. _vector.push_back(z);
    - все равно объект z будет преобразован к типу baseClass*

    только не derivedClass*, а derivedClass
    Все правильно. Все элементы вектора - типа baseClass*. Но в конкретном примере все они указывают на объекты класса derivedClass.

    Постановка вопроса некорректна в принципе. Динамическое определение типа возможно только для объектов классов. Да и честно говоря и там не очень-то и нужно... Недаром мистер Страуструп столько упирался, прежде, чем ввел его поддержку в язык.
     
  3. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    Ustus
    угу, имелся ввиду абстрактный класс (т.е. класс, который содержит один или более virtual методов). терминологию я подзабыл, каюсь.

    да, элементы вектора имеют тип baseClass*;
    но ведь в изначальном коде в моем посте выше test1 является указателем на derivedClass; а test2 -- на baseClass;
    по логике, напротив, все эти 2 объекта должны быть типа baseClass*, но никак не derivedClass*.
    т.к. имеет место случай:
    разве тут нету противоречия?

    или же в случае с:
    Код (Text):
    1. baseClass *test2 = new derivedClass();
    не важно, был ли test2 объявлен как baseClass*, или же как derivedClass*, и главное то, как он был инициализирован ( new derivedClass(), или new baseClass() )?
    если эта мысль верна, то все проясняется.

    так у меня эта картина и выходит: я хотел получить тип данных, на который указывают указатели, хранимые в _vector.

    так именно эту информацию я и хочу получить в run-time! это возможно или нет? или в любом случае будет выдаваться информация о типе T, указанном в _vector<T> как тип, который этот контейнер хранит?
     
  4. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Это полиморфный класс. Абстрактный - это класс, имеющий один или более "чисто виртуальных методов".

    Как вариант:
    Код (Text):
    1. struct A
    2. {
    3.     virtual void doit(){}
    4. };
    5.  
    6. struct B: A
    7. {
    8.     virtual void doit(){}
    9. };
    10.  
    11. int main(int argc,char* argv[])
    12. {
    13.     A* a1 = new A();
    14.     A* a2 = new B();
    15.    
    16.     printf("a1 is '%s', a2 is '%s'\n", typeid(a1).name(), typeid(a2).name());
    17.     if(dynamic_cast<B*>(a1))
    18.         puts("  but a1 is B really");
    19.     if(dynamic_cast<B*>(a2))
    20.         puts("  but a2 is B really");
    21.  
    22.     return 0;
    23. }
     
  5. Ustus

    Ustus New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2005
    Сообщения:
    834
    Адрес:
    Харьков
    varnie
    нет, абстрактный класс - это класс, объекты которого не создаются, в С++ это класс, содержащий хотя бы одну pure virual (чисто виртуальную) функцию. А класс с виртуальными функциями - это обычный класс. Но это я так, придираюсь к словам. :)

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

    Тип этих переменных - соответственно derivedClass* и baseClass*. Но после:
    Код (Text):
    1.     derivedClass * test1 = new derivedClass();
    2.     _vector.push_back( test1 );
    3.     baseClass *test2 = new derivedClass();
    4.         _vector.push_back( test2 );
    обе указывают на объекты типа derivedClass.

    Нет. Вектор содержит объекты типа baseClass* (указатели, а не объекты!), которые могут указывать на объекты как baseClass, так и derivedClass. При вызове _vector.pushback ему должен передаваться параметр типа baseClass*. Поэтому test1 (типа derivedClass) неявно преобразуется к baseClass*.

    в принципе, в данном контексте - да.

    проще всего - на примере:
    Код (Text):
    1. #include <typeinfo>
    2. #include <iostream>
    3. #include <vector>
    4.  
    5. class Base
    6. {
    7. public:
    8.     virtual ~Base() {}
    9. };
    10.  
    11. class Derived : public Base { };
    12.  
    13. int main()
    14. {
    15.     std::vector<Base*> v;
    16.     v.push_back(new Base);
    17.     v.push_back(new Derived);
    18.     std::cout
    19.         << typeid(v[0]).name() << '\n'
    20.         << typeid(v[1]).name() << '\n'
    21.         << typeid(*v[0]).name() << '\n'
    22.         << typeid(*v[1]).name() << '\n';
    23.     delete v[1];
    24.     delete v[0];
    25.     return 0;
    26. }
    после выполнения на выходе:
    Код (Text):
    1. Base *
    2. Base *
    3. Base
    4. Derived
    то есть тип элементов вектора - таки всегда Base*, а вот тип объектов, на которые они указывают - разный.

    +++
    Точно! Есть такое слово! Блин, забыл... :dntknw:
    :):):)
     
  6. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    IceStudent,
    Ustus,
    спасибо за развернутые объяснения. теперь я уяснил. у меня раньше были проблемы в понимании:
    теперь прояснилось все.
     
  7. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Ustus
    О, у тебя корректнее. Я тестил такой вариант, но без виртуальных функций - и не получилось. Потом, когда возился с dynamic_cast, компилятор напомнил о необходимости наличия виртуальной функции.
     
  8. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    в продолжение темы..

    сейчас у Брюса Эккеля в первом томе его "Thinking in C++" нашел интересную заметку:
    т.е. при down-casting можно заменить использование dynamic_cast на typeinfo + static_cast, и тем самым снизить "extra overhead".

    хорошо, но почему тогда нижсеследующий пример даёт разные результаты:
    Код (Text):
    1. #include <iostream>
    2. #include <typeinfo>
    3. using namespace std;
    4.  
    5. class Shape { public: virtual ~Shape() {}; };
    6. class Circle : public Shape {};
    7. class Square : public Shape {};
    8.  
    9. void testFirst()
    10. {
    11.     Circle c;
    12.     Shape* s = &c; // Upcast: normal and OK
    13.  
    14.     Circle* cp = 0;
    15.     Square* sp = 0;
    16.  
    17.     // Static Navigation of class hierarchies
    18.     // requires extra type information:
    19.     if(typeid(s) == typeid(cp)) // C++ RTTI
    20.             cp = static_cast<Circle*>(s);
    21.     if(typeid(s) == typeid(sp))
    22.             sp = static_cast<Square*>(s);
    23.  
    24.     cout << "testFirst:" << endl;
    25.     if(cp != 0)
    26.             cout << "It's a circle !" << endl;
    27.      if(sp != 0)
    28.             cout << "It's a square !" << endl;
    29.     cout << endl;
    30. }
    31.  
    32. void testSecond()
    33. {
    34.     Circle c;
    35.     Shape* s = &c; // Upcast: normal and OK
    36.  
    37.     Circle* cp = 0;
    38.     Square* sp = 0;
    39.  
    40.     cout << "testSecond:" << endl;
    41.     cp = dynamic_cast<Circle*>(s);
    42.     sp = dynamic_cast<Square*>(s);
    43.  
    44.     if(cp != 0)
    45.             cout << "It's a circle !" << endl;
    46.      if(sp != 0)
    47.             cout << "It's a square !" << endl;
    48.     cout << endl;
    49. }
    50.  
    51. int main()
    52. {
    53.     testFirst();
    54.     testSecond();
    55.        
    56.         return 0;
    57. } ///:~
    вывод:
    тестил на компилере gcc.
     
  9. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    код ф-ции void testFirst() я взял целиком и полностью с вышеупомянутой книги.
    код ф-ции void testSecond() -- уже мой.

    сейчас поразмыслил, и пришел к выводу, что код из книги неверный ( код ф-ции void testFirst() ), и правильный вариант должен быть следующим:
    Код (Text):
    1. void testFirst()
    2. {
    3.     Circle c;
    4.     Shape* s = &c; // Upcast: normal and OK
    5.  
    6.     Circle* cp = 0;
    7.     Square* sp = 0;
    8.  
    9.     // Static Navigation of class hierarchies
    10.     // requires extra type information:
    11.     if(typeid(*s) == typeid(Circle))                // C++ RTTI
    12.             cp = static_cast<Circle*>(s);
    13.     if(typeid(*s) == typeid(Square))
    14.             sp = static_cast<Square*>(s);
    15.  
    16.     cout << "testFirst:" << endl;
    17.     if(cp != 0)
    18.             cout << "It's a circle !" << endl;
    19.      if(sp != 0)
    20.             cout << "It's a square !" << endl;
    21.     cout << endl;
    22. }
    в этом случае обе ф-ции выдают:
    что будет верно.
     
  10. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    на рсдн.ру подтвердили мои предположения. в книге ("Thinking in CPP 2nd Edition, 2nd Ed Volume 1", Chapter 15) дан неверный код. опечатка это или ошибка - неизвестно.

    так что вопрос закрыт:)
     
  11. varnie

    varnie New Member

    Публикаций:
    0
    Регистрация:
    2 янв 2005
    Сообщения:
    1.785
    upd: вопрос снят
     
  12. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    что означает действие
    a << 8 ?
    и вообще
    что в сях означает "<<" ?

    в гугле хз - не нашел... :dntknw:
     
  13. Xerx

    Xerx Алексей

    Публикаций:
    0
    Регистрация:
    17 фев 2005
    Сообщения:
    528
    Адрес:
    Russia
    Magnum
    сдвиг влево на 8 разрядов, т.е.
    shl a, 8

    ну и соответственно a >> 8 - сдвиг вправо
     
  14. Magnum

    Magnum New Member

    Публикаций:
    0
    Регистрация:
    29 дек 2007
    Сообщения:
    925
    Xerx
    ясно
    сенкс
    что-то типа ускоренного умножения/деления на (2^8)
     
  15. Xerx

    Xerx Алексей

    Публикаций:
    0
    Регистрация:
    17 фев 2005
    Сообщения:
    528
    Адрес:
    Russia
    Magnum
    риторический вопрос )
     
  16. Benzin

    Benzin Сергей

    Публикаций:
    0
    Регистрация:
    26 авг 2007
    Сообщения:
    41
    Адрес:
    St.Petersburg
    Народ, объясните пож-та, как распологаются объекты в ячейках памяти, к примеру
    вот программа:

    Код (Text):
    1. #include <iostream.h>
    2.  
    3. int main()
    4. {
    5.     double ac=3.1, bc=5.3;
    6.  
    7.     double *dac=&ac, *dbc=&bc;
    8.     int *iac=(int *)&ac, *ibc=(int *)&bc;
    9.     float *fac=(float *)&ac, *fbc=(float *)&bc;
    10.     char *cac=(char *)&ac, *cbc=(char *)&bc;
    11.    
    12.     cout <<"Adres ac = " << &ac <<"\n";
    13.     cout <<"Adres bc = " << &bc <<"\n\n";
    14.  
    15.     cout <<"(&ac - &bc) = " << (&ac - &bc) <<"\n";
    16.     cout <<"(int)&ac - (int)&bc = " << (int)&ac - (int)&bc  <<"\n\n";
    17.    
    18.     cout <<"dac - dbc = " << dac - dbc <<"\n";
    19.     cout <<"iac - ibc = " << iac - ibc <<"\n";
    20.     cout <<"fac - fbc = " << fac - fbc <<"\n"; 
    21.     cout <<"cac - cbc = " << cac - cbc <<"\n";
    22.    
    23.     return 0;
    24. }
    Народ, объясните пож-та, как распологаются объекты в ячейках памяти, к примеру
    вот программа:

    2 переменные по 8 байт каждая, разность значений адресов это тоже
    подтверждает, разность адресов 1, тоже понятно, а вот разность самих указателей не
    пойму как получилась, для этого надо понять как расположены эи указатели, там вроде как
    то с младших разрядов..., желательно на рисунке такого типа:

    Схема распооложения переменной ab в памяти(у bc аналогичная):
    ___________________________________________________
    1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
    ___________________________________________________
     
  17. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine
    Порядок расположения переменных в стеке не определён, имхо.
     
  18. simnet_

    simnet_ New Member

    Публикаций:
    0
    Регистрация:
    18 дек 2007
    Сообщения:
    109
    Здравствуйте
    Такой вопрос: для чего во многих компиляторах "заставляют" использовать функции/идентификаторы/ключевые слова, начинающиеся с символов "_" и "__" (например, __asm, _asm, asm)? Это какое-то указание линкеру, или просто для того, чтоб показать "особенность" этих команд?
     
  19. censored

    censored New Member

    Публикаций:
    0
    Регистрация:
    5 июл 2005
    Сообщения:
    1.615
    Адрес:
    деревня "Анонимные Прокси"
    simnet_
    compiler specific feautures
     
  20. Ustus

    Ustus New Member

    Публикаций:
    0
    Регистрация:
    8 авг 2005
    Сообщения:
    834
    Адрес:
    Харьков
    simnet_
    Традиционно - идентификаторы начинающиеся с "_" - это чего-то системное, чаще всего - непереносимое и/или низкоуровневое, некоторые зависят от компилятора. Не рекомендуется использовать в коде, особенно в глобальном пространстве имен.
    Идентификаторы, начинающиеся с "__" - это вапще ужоснах из бездны, как правило - детали реализации компилятора и/или стандартных библиотек. Практически всегда зависят от компилятора. Создавать такие идентификаторы - искать приключений на заднюю часть тела. О-о-очень не рекомендуется, если нет необходимости.
    Ключевые слова с подчеркиванием - скорее дань традициям. На старых компиляторах, те же ассемблерные вставки у одних были - asm, у других - _asm, у третьих - __asm. В каком-то компилере (точно не помню уже) были равнозначные asm и __asm, а вот _asm не было. Еще в одном (не помню :dntknw: Watcom, что ли из ранних?) asm - однострочная вставка, __asm {} - блок.
    Современные компилеры, как правило поддерживают их все (из соображений совместимости?), причем, как правило - одинаково. Но это не гарантируется, как правильно заметил censored - зависит от компиилятора.