Модераторы: Daevaorn

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Наследование: создается один большой класс? методы и this 
:(
    Опции темы
Gunslinger
Дата 14.1.2010, 16:38 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 842
Регистрация: 30.12.2006
Где: Астрахань

Репутация: 1
Всего: 3



Цитата(mes @  14.1.2010,  15:57 Найти цитируемый пост)

struct A {  void f (){ std::cout << 1; } };
struct B {  void f (){ std::cout << 2; } };
struct AB : A, B {};
int main() {
AB * ab = NULL;
//   ab->f(); // request for member `f' is ambiguous
            // так как обе  f одинаково подходят.   
   
 ((A*) ab) ->f();
 ((B*) ab) ->f(); 
}

Да, именно об этом я и спрашивал: что через один указатель мы можем получать доступ как к методам наследника, так и к методам родителя(лей). Отчасти поэтому и выбрал пример с множественным наследованием -- яснее видно. Теперь, когда мы говорим об одном и том же -- повторю. Физически A::f(), B::f() и C::f()(если есть) компилятор располагает в памяти друг за другом? Т.к. благодаря этому приведение указателя на наследник к указателю на родителя имеет смысл.

Цитата(mes @  14.1.2010,  15:57 Найти цитируемый пост)
Цитата(Gunslinger @  14.1.2010,  12:52 Найти цитируемый пост)
Приведение указателя -- адрес остается тем же, только меняется тип. Т.е. указывал на участок памяти одного размера (начиная с какого-то адреса), теперь указывает на участок памяти другого размера (при этом, начиная с того же самого адреса). 

При множественном наследовании это не так. Адрес меняется.

Т.е. при одиночном наследовании -- все так, как я написал? А при множественном?

PM MAIL   Вверх
mes
Дата 14.1.2010, 16:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Gunslinger @  14.1.2010,  15:38 Найти цитируемый пост)
Физически A::f(), B::f() и C::f()(если есть) компилятор располагает в памяти друг за другом? Т.к. благодаря этому приведение указателя на наследник к указателю на родителя имеет смысл.

нет.. 
1. не обязательно друг за другом.. 
2. функции вообще может не существовать в машинном коде 
3. НЕ благодаря этому приведение имеет смысл

Добавлено через 13 минут и 37 секунд
Цитата(Gunslinger @  14.1.2010,  15:38 Найти цитируемый пост)
Т.е. при одиночном наследовании -- все так, как я написал?

нет. Сути Вы не написали.. то что адрес при преобразовании не меняется, нам роли не играет..

Вот еще один пример :

Код

struct A
{  
   void f () { std::cout <<"A::f() "; }
   int i;
};
struct B : A {
   void f() { std::cout <<"B::f() "; }
};


int main (int argc, char* argv[])
{
    B b;
    A a;
    
    a.f (); // тут по типу подбирается какaя функция
    b.f();  // больше подходит.
    
    b.A::f(); // тут явно укаызваем какую функцию 
    b.B::f(); // хотим вызвать
}    

т.е  когда мы явно не указываем каую функцию мы хотим вызвать, еще на стадии компиляции заменяется 
  a.f (); на a.A::f(); 
  b.f();  на b.B::f(); 
потому как компилятор обладает всей необходимой инфой для этого  на тот момент времени.



--------------------
PM MAIL WWW   Вверх
Gunslinger
Дата 14.1.2010, 17:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 842
Регистрация: 30.12.2006
Где: Астрахань

Репутация: 1
Всего: 3



Цитата(mes @  14.1.2010,  16:47 Найти цитируемый пост)
1. не обязательно друг за другом.. 

Значит в памяти могут располагаться так (х - какая-либо другая инфа; С -наследник А и В):
xx[A::f()]xxxxxxxx[B::f()]xxxxx[C::f()]xxx
а не так:
ххххххxx[A::f()][B::f()][C::f()]xxxххххххх

Цитата(mes @  14.1.2010,  16:47 Найти цитируемый пост)
3. НЕ благодаря этому приведение имеет смысл

Тогда при приведении:
1.  компилятор должен определить, где начинается, например, А и положить в указатель адрес на область А.
2. можно привести указатель с С на указатель не только к ео родителям А, В, а к любому другому. Например, к какому-нибудь Е, который от А и В вообще  не наследует.


Это сообщение отредактировал(а) Gunslinger - 14.1.2010, 17:16
PM MAIL   Вверх
baldina
Дата 14.1.2010, 17:17 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



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

вот пример:

Код

// C++
class A {
 int x;
public:
 void f1 ();
 void f2 ();
 void f3 ();
};

void foo ()
{
  A a;
  a.f1(); // фактически вызов A::f1(&a);
  a.f2();
  a.f3();
}

void A::f2 () { x = 2; }
void A::f3 () { x = 3; }
void A::f1 () { x = 1; }

// то же самое на C

typedef struct {
  int x;
} A;

void f2 (A*a) { a->x = 2; }
void f3 (A*a) { a->x = 3; }
void f1 (A*a) { a->x = 1; }

void foo ()
{
  A a;
  f1(&a);
  f2(&a);
  f3(&a);
}


Добавлено через 7 минут и 17 секунд
Цитата

2. можно привести указатель с С на указатель не только к ео родителям А, В, а к любому другому. Например, к какому-нибудь Е, который от А и В вообще  не наследует.

Технически можно. Практически не нужно. ЯВУ существует как раз для того, что бы не думать об адресах и т.п., а думать в терминах задачи
PM MAIL   Вверх
mes
Дата 14.1.2010, 17:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Gunslinger @  14.1.2010,  16:15 Найти цитируемый пост)
Тогда при приведении:
1.  компилятор должен определить, где начинается, например, А и положить в указатель адрес на область А.


зачем компилятору адрес, если есть имя.. Пусть линкер думает что и куда соединять и по какому адресу располагать smile


Цитата(Gunslinger @  14.1.2010,  16:15 Найти цитируемый пост)
можно привести указатель с С на указатель не только к ео родителям А, В, а к любому другому. Например, к какому-нибудь Е, который от А и В вообще  не наследует

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

Добавлено через 3 минуты и 14 секунд
Кстати Ваш вопрос (о выборе метода) к наследованию прямого отношения не имеет. Это определяется правилами видимости.




--------------------
PM MAIL WWW   Вверх
mes
Дата 14.1.2010, 17:49 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



а понял с какой стороны не хватало обзора.. 

вот два примерчика, в обоих один и тот же принцип :
Код

struct A {
 static void f();
};
struct B : A {
};
struct C : B {
 static void f();
};

((A*)NULL)->f();
//  A::f();
((B*)NULL)->f();
//  B::f(); == A::f();
((C*)NULL)->f();
//  C::f();


Код

namespace A
{
   void f( );
}
namespace B {
  using A::f()
}
namespace C {
  using A::f()
}
A::f():
B::f(); // == A::f();
C::f();


и главное нужно понимать, что в примерах НЕТ ни одной функции  с именем f(); 
Мы используем лишь часть имени, потому как о другой части компилятор может сам догадаться..
реально функция называется N::f()  (где N специализация имени).. Т.е область определения входит в состав полного имени.
Так что компилятор  выбирает не какой адрес подставить, а какую специализацию несет имя. (т.е фактически опр. какое полное имя функции)




Это сообщение отредактировал(а) mes - 14.1.2010, 17:51


--------------------
PM MAIL WWW   Вверх
Gunslinger
Дата 15.1.2010, 11:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 842
Регистрация: 30.12.2006
Где: Астрахань

Репутация: 1
Всего: 3



Как всегда все оказалось сложнее, чем я думалsmile
Вывод по теме:
если у класса 1 или несколько родителей, то компилятор:
1. не создает копии методов родителей;
2. не располагает методы родителей и наследника в памяти друг за другом -> возможность преобразования указателя с наследника на его родителя обеспечивается... линковщиком?
PM MAIL   Вверх
baldina
Дата 15.1.2010, 11:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



Цитата

1. не создает копии методов родителей;

Более того, любая функция существует лишь в одном экземпляре. Помимо соображений достаточности есть еще одно простое: если используется указатель на функцию, то он должен быть одинаковым во всех частях программы. Значит и функция одна.
При открытом наследовании родительские открытые функции становятся частью интерфейса потомка. Но не частью реализации. И вызов derived::f(), если f() унаследована из base, фактически означает base::f(), т.е. функция одна. mes все это подробно объяснил.

Цитата

2. не располагает методы родителей и наследника в памяти друг за другом -> возможность преобразования указателя с наследника на его родителя обеспечивается... линковщиком? 

располагает как сочтет нужным, как именно - совершенно непринципиально. вообще-то компилятор этим занимается (в пределах единицы компиляции), но современные продвинутые линковщики тоже могут двигать.
а преобразование типа линковщиком не осуществляется, это бред. в общем случае линковщик понятия не имеет о классах. адрес вызова определяет компилятор; в коде уже адрес конкретной функции.
PM MAIL   Вверх
mes
Дата 15.1.2010, 12:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Gunslinger @  15.1.2010,  10:16 Найти цитируемый пост)
если у класса 1 или несколько родителей, то компилятор:
1. не создает копии методов родителей;

родители тут вобще не причем. Да и сами классы тоже.
Важны понятия видимости/перекрытия имен ну и соответсвенно scope в котором располагается функция.


Цитата(Gunslinger @  15.1.2010,  10:16 Найти цитируемый пост)
2. не располагает методы родителей и наследника в памяти друг за другом.. 

к памяти это отношения АБСЛЮТНО НЕ имеет, см пример с namespace
важен не адрес функции, а ее Полное Специализированное Имя!

Цитата(Gunslinger @  15.1.2010,  10:16 Найти цитируемый пост)
возможность преобразования указателя с наследника на его родителя обеспечивается... 

указатель тут НЕ причем  !..  преобразование тоже НЕ причем !.. оно относится (в том смысле в каком Вы в этой  теме подразумевали) только для доступа к данным. 
Для методов это "побочный эффект" - просто от типа указателя, зависит scope в котором компилятор начнет искать подходящее имя.
т.е. если указатель  типа B* то p->f() заменится на p->B::f().
когда делаете преобразование меняется и тип объекта к которому хотите применить операцию, ну и соответсвенно  компилятор подставляет нужную специализацию.




Это сообщение отредактировал(а) mes - 15.1.2010, 12:06


--------------------
PM MAIL WWW   Вверх
Gunslinger
Дата 15.1.2010, 12:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 842
Регистрация: 30.12.2006
Где: Астрахань

Репутация: 1
Всего: 3



Цитата(mes @  15.1.2010,  12:04 Найти цитируемый пост)
Важны понятия видимости/перекрытия имен ну и соответсвенно scope в котором располагается функция.

 Важно компилятору? Чтобы сделать преобразование:
Цитата(mes @  15.1.2010,  12:04 Найти цитируемый пост)
т.е. если указатель  типа B* то p->f() заменится на p->B::f().
?




PM MAIL   Вверх
mes
Дата 15.1.2010, 12:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Gunslinger @  15.1.2010,  11:22 Найти цитируемый пост)
Важно компилятору? Чтобы сделать преобразование:

нет.. Важно для выбора  метода.. 
Вы рассматриваете преобразование и выбор подходящего метода как одну операцию , а их то две !

Добавлено @ 12:54
Чтоб не путаться дальше  сформируйте вопросы заново, чтоб видно было, с чем Вы уже разобрались, а где ушли в сторону.

Добавлено @ 13:05
Вот я вам составил пример, убрав  все лишнее : методы, классы, наследование и т.д.

Код

namespace A {
    void f(int)  { std::cout << " int " << std::endl; }
}
namespace B {
    void f(char) { std::cout << " char "<< std::endl; }     
}

void test1 ()
{
    using namespace A;
    f(1);     
    f(char());
    f(short());    
    f(int());   
}
void test2 ()
{
    using namespace B;
    f(1);     
    f(char());
    f(short());    
    f(int());
}
void test3 ()
{
    using namespace A;
    using namespace B;
    f(1);     
    f(char());
    f(short());    
    f(int());
}

int main (int argc, char* argv[])
{
     std::cout << std::endl << "test1" << std::endl;
     test1 (); 

     std::cout << std::endl << "test2" << std::endl;
     test2();

     std::cout << std::endl << "test3" << std::endl;
     test3();

  return 0;

}


для начала разберитесь что происходит здесь, а потом уже легко расширите и на классы.
smile

Это сообщение отредактировал(а) mes - 15.1.2010, 13:05


--------------------
PM MAIL WWW   Вверх
mes
Дата 15.1.2010, 13:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



а также почему не работает, если изменить пример :
Код

...

void test ()
{
    f(1);     
    f(char());
    f(short());    
    f(int());   
}

int main (int argc, char* argv[])
{
     std::cout << std::endl << "test" << std::endl;
     using namespace A;
     test (); 
}    


Это сообщение отредактировал(а) mes - 15.1.2010, 13:10


--------------------
PM MAIL WWW   Вверх
Gunslinger
Дата 15.1.2010, 14:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 842
Регистрация: 30.12.2006
Где: Астрахань

Репутация: 1
Всего: 3



Цитата(mes @  15.1.2010,  13:09 Найти цитируемый пост)
void test ()
{
    f(1);     
    f(char());
    f(short());    
    f(int());   
}

Не знаю, что это -- char(). Вроде функция, но char -- ключевое слово, так использовать нельзя. Если это параметр char, то компилятор не поймет, т.к. в пространстве А функции с таким параметром нет.

Цитата(mes @  15.1.2010,  12:50 Найти цитируемый пост)
Чтоб не путаться дальше  сформируйте вопросы заново, чтоб видно было, с чем Вы уже разобрались, а где ушли в сторону.

Я уже запутался... Придется начать сначала.
Что уже известно: 
1. Данные класса группируют в памяти друг за другом ("блоковое" расположение) и у каждого объекта класса -- их своя копия. 
2. Такие блоки связаны со своими методами скрытым параметром this (адресом на блок).
3. Методы класса (и простые функции) не группируются друг за другом и копий не имеют. 
Как?

Добавлено через 3 минуты и 45 секунд
Цитата(mes @  15.1.2010,  13:09 Найти цитируемый пост)
а также почему не работает, если изменить пример :
...
void test ()
{
    f(1);     
    f(char());
    f(short());    
    f(int());   
}

Не известно пространство имен.


Это сообщение отредактировал(а) Gunslinger - 15.1.2010, 15:02
PM MAIL   Вверх
mes
Дата 15.1.2010, 15:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


любитель
****


Профиль
Группа: Участник Клуба
Сообщений: 7954
Регистрация: 14.1.2006

Репутация: 144
Всего: 250



Цитата(Gunslinger @  15.1.2010,  13:59 Найти цитируемый пост)
Не знаю, что это -- char(). Вроде функция, но char -- ключевое слово, так использовать нельзя. Если это параметр char, то компилятор не поймет, т.к. в пространстве А функции с таким параметром нет.

ну так запустите и проверьте..

Цитата(Gunslinger @  15.1.2010,  13:59 Найти цитируемый пост)
1. Данные класса группируют в памяти друг за другом ("блоковое" расположение) и у каждого объекта класса -- их своя копия. 

данные-члены также бывают статические и нестатические. 

1. статические объявляются в классе, но  должны быть определены вне класса .. к объекту класса отношения не имеют.
(для констант есть допуски которые не касаются этой темы )

2. нестатические определяются внутри класса, и все вместе (вместе с нестатическими данными всех подобъектов) образуют общую (не смотря на то что  описаны в разных классах) структуру объекта. Именно на эту структуру и указывает this в нестатических функциях-членах. Она же и определяет размер класса.
("структура" подразумевается не как ключевое слово stuct, a как понятие)

Цитата(Gunslinger @  15.1.2010,  13:59 Найти цитируемый пост)
2. Такие блоки связаны со своими методами скрытым параметром this (адресом на блок).

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

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

Цитата(Gunslinger @  15.1.2010,  13:59 Найти цитируемый пост)
3. Методы класса (и простые функции) не группируются друг за другом и копий не имеют. 

Что будет представлять в машинном коде функция(-член) мы сказать не можем. Она может быть заинлайнена, да так что от нее не останется и следа, а может состоять из целой череды асм-функций. Гарантируется одно, что адрес функции, где бы его не взяли, будет одним и тем же.

Добавлено через 5 минут и 35 секунд
Цитата(Gunslinger @  15.1.2010,  13:59 Найти цитируемый пост)
Не известно пространство имен.

угу.. точнее потому что подходящей функции с таким именем нет в области видимости.



Это сообщение отредактировал(а) mes - 15.1.2010, 15:30


--------------------
PM MAIL WWW   Вверх
baldina
Дата 15.1.2010, 16:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 32
Всего: 101



mes, мне кажется, Вы даете топикстартеру ответы на вопросы, которые ему даже не приходят в голову. Представляю, какая уже каша из понятий видимости, наследования и проч...  smile 

Вообще мне кажется, стоит начать с того, что данные и код в памяти компьютера могут быть (и как правило находятся) в различных, удаленных друг от друга блоках памяти. Программист имеет дело с семантикой, описанной в стандарте, т.е. с некоей виртуальной машиной С++, а машинный код может получиться разным в зависимости от реализации компилятора. Есть наиболее популярные реализации, есть требования стандарта (типа последовательного расположения в памяти элементов vector), накладывающие ограничения на реализацию, но не более того.
PM MAIL   Вверх
Страницы: (4) Все 1 [2] 3 4 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
Earnest Daevaorn

Добро пожаловать!

  • Черновик стандарта C++ (за октябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика(4.4мб).
  • Черновик стандарта C (за сентябрь 2005) можно скачать с этого сайта. Прямая ссылка на файл черновика (3.4мб).
  • Прежде чем задать вопрос, прочтите это и/или это!
  • Здесь хранится весь мировой запас ссылок на документы, связанные с C++ :)
  • Не брезгуйте пользоваться тегами [code=cpp][/code].
  • Пожалуйста, не просите написать за вас программы в этом разделе - для этого существует "Центр Помощи".
  • C++ FAQ

Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Earnest Daevaorn

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Общие вопросы | Следующая тема »


 




[ Время генерации скрипта: 0.0646 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.