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

Поиск:

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


Опытный
**


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

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



Цитата(mes @  20.2.2010,  15:32 Найти цитируемый пост)
ага, в принципе так, только в более удобном для него виде. И с функциями тоже самое.

Таким образом компилятор реализует и различные ограничители, вроде private, protected, namespace, const --  всего лишь добавляет к имени переменной\метода соответствующие  "пометки"? 

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


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


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

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



Цитата(Gunslinger @  20.2.2010,  16:55 Найти цитируемый пост)
Таким образом компилятор реализует и различные ограничители, вроде private, protected, namespace, const --  всего лишь добавляет к имени переменной\метода соответствующие  "пометки"? 


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

Единственное, что не все пометки отражаются на имени. Полное имя нужно линкеру для связки - и на тот момент уже нет ни типов, ни скоупов, есть только имя.
Компилятору необходимо гораздо больше информации (например тип, размер, право доступа), поэтому все "пометки" он хранит в специальных внутренних структурах. Как именно он хранит, каждый компилятор решает сам.
smile




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


Опытный
**


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

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



Цитата(mes @  20.2.2010,  20:42 Найти цитируемый пост)

Единственное, что не все пометки отражаются на имени. 

static переменные\методы, например? Существуют ведь в одной копии -- линкер вроде запутаться не должен.
По поводу Вашего предыдущего примера кода.
Код

struct A [::this_t] {
  char [A::] c;   
};
struct B [::this_t] {
  [U]char [A::] c;[/U]
  char [B::] c;
};

void [A::] f ( [A::this_t* this] ) { std::cout << this->[U][A::]c[/U]; }
void [B::] f ( [B::this_t* this] ) { std::cout << this->[B::]c; }

int main () {
A  a;
B  b;
a. f();
b.f();
b.A::f[U]()[/U];
}

b.A::f() -- вроде вызывается для объекта b, но у метода адрес на данные объекта а. Получается, f() возьмет не переменную "c" наследника, родителя: а.с?   
PM MAIL   Вверх
mes
Дата 20.2.2010, 22:55 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(Gunslinger @  20.2.2010,  21:08 Найти цитируемый пост)
static переменные\методы, например? Существуют ведь в одной копии -- линкер вроде запутаться не должен.

про запутаться :
Код

void f(char c);
void f(int c);

какую f вызвать из этих линкеру было бы не понятно, поэтому для него обоим функциям 
задается вид типа например такого :
Код

fn1f4char;
fn1f3int;

 где в нашем случае для разделителя выбрано число показывающее кол-во символов до следующего разделителя.

второй вариант, экспортировать имя функции в стиле, тогда у функции будет "нормальное" имя, но такая функция лишиться возможности перегрузки и расположения не в глобальном пространстве имен.

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

Цитата(Gunslinger @  20.2.2010,  21:08 Найти цитируемый пост)
 Получается, f() возьмет не переменную "c" наследника, родителя: а.с?

по адресу b есть данные как наследника , так и родителя,
Цитата(Gunslinger @  20.2.2010,  21:08 Найти цитируемый пост)
 Получается, f() возьмет не переменную "c" наследника, родителя: а.с?

да, так как мы указали явно что надо использовать функцию A::f будет вызвана именно она, а соответсвенно использоваться будет дата_член A::c.
smile


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


Опытный
**


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

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



Цитата(mes @  20.2.2010,  22:55 Найти цитируемый пост)

да, так как мы указали явно что надо использовать функцию A::f будет вызвана именно она, а соответсвенно использоваться будет дата_член A::c.

В b есть копия переменной-родителя, однако ни метод b.f(), ни b.a::f() не могут к ней обратиться: либо к b.c, либо к a.c. Как же тогда обратиться к [A::]с в наследнике? Знаю только, как в ActionScript3 такое сделано, но там все просто: super.наследованная_переменная.

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


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


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

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



Цитата(Gunslinger @  21.2.2010,  09:37 Найти цитируемый пост)
но там все просто: super.наследованная_переменная.

а в паскaле есть служебное слово inherited, ну а в С++ никаких служебных слов для этого не надо, достаточно указать специализированное имя переменной.
smile
Код

struct A
{
  int i;
  int j;
};
struct B : A
{
   int i;
   void f ()
   {
       i++;  // то же самое что и  B::i++;

       A::i++; // а вот обращение к переменной предка

       j++;  // ну а тут сразу обращаемся к переменной предка, так как в наследнике это имя не перекрыто
               // т.е аналогично A::j++;
   } 
};

smile

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


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


Опытный
**


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

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



Цитата(mes @  21.2.2010,  11:03 Найти цитируемый пост)
       A::i++; // а вот обращение к переменной предка

Это обращение к переменной struct A или унаследованной, которая в struct B? Они ведь принадлежат к разным классам -- в наследнике B содержится копия A.i.
 
PM MAIL   Вверх
mes
Дата 21.2.2010, 13:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(Gunslinger @  21.2.2010,  11:30 Найти цитируемый пост)
Это обращение к переменной struct A или унаследованной

ну так в самом начале же разбирали, что объект включает в себя все дата-члены как своего класса, так и всех предков.
то есть объект класса B имеет следующие дата-члены :
Код

A::i
B::i
B::j


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

Пример из жизни. Допустим Вы студент, и в Вашем классе есть Коля. Когда Вы говорите о нем со своими однокурсниками, то всем понятно о ком речь.
Но на параллельном курсе тоже есть Коля, и когда Вы хотите упомянуть его, то должны уточнить о ком именно идет речь. А вот например парень с именем Аквакате только один на весь институт, и ни у кого сомнения не возникнет о ком идет речь, даже не уточняя. 
smile

В общем язык программирования это тоже язык, и если в ЯНУ все плоско и каждая конструкция  имеет только одно соответствие, то ЯВУ позволяет себе "такую же" игру слов как и разговорные язык и смысл конструкции не очевиден без знания контекста. 
для Си обычно приводят пример : что есть i * j ?;




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


Опытный
**


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

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



Цитата(mes @  21.2.2010,  13:22 Найти цитируемый пост)
ну так в самом начале же разбирали, что объект включает в себя все дата-члены как своего класса, так и всех предков.

Так ведь включает "копию", а не оригинал. Оригинал как принадлежал родителю, так и принадлежит... Вначале темы думал, что при наследовании создаются и копии методов, которые "перенаправляются" на унаследованные дата-члены. Оказалось ошибался -- копии методов не создаются и не "перенаправляются", а продолжают указывать на дата-члены родителя. Но теперь куча других вопросов:
1. Как обратиться к унаследованным переменным?
2. Если в родителе реализовывался какой-нибудь механизм (методы выполняли и писали состояние в дата-члены), то, если в наследнике предварительно не выполнить эти механизмы, то можно поломать логику работы наследника.. Как тогда быть, методы родителя же "направлены" на дата-члены родителя?
PM MAIL   Вверх
mes
Дата 23.2.2010, 11:42 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(Gunslinger @  23.2.2010,  09:45 Найти цитируемый пост)
Так ведь включает "копию", а не оригинал. 

Ну вот приехали.. smile  smile 
разберем на примере из жизни :  Млекопитающего, как базового класса и волка как наследника.
Цитата

Млекопитающие — класс позвоночных животных, основными отличительными особенностями которых являются живорождение ... и вскармливание детёнышей молоком. ... ... ...

Цитата

Волк, серый волк, обыкновенный волк (лат. Canis lupus) — хищное млекопитающее семейства псовых. ... ... ...

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

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

А вот и сам обьект, изображен на картинке :
user posted image
Как видно этому объекту соответствуют оба описания. В программировании говорится, что поддерживает оба интерфейса,
что другими словами означает, что  поведение объекта справедливо для обоих описаний. 

Также очевидно, что описаний может быть намного больше.

Теперь хотелось бы уточнить сказанное в Вашей цитате, копии чего из класса содержит объект  ?!  smile 







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


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


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


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

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



Цитата(Gunslinger @  23.2.2010,  09:45 Найти цитируемый пост)
Вначале темы думал, что при наследовании создаются и копии методов, которые "перенаправляются" на унаследованные дата-члены. Оказалось ошибался -- копии методов не создаются и не "перенаправляются", а продолжают указывать на дата-члены родителя. Но теперь куча других вопросов:

вот Вам еще один пример
Код

struct A 
{
    int i;    
    void show_i () { std::cout << "A::i == " << i << std::endl; }
} a;

struct B : A
{
    int i;
    void show_i () { std::cout << "B::i == " << i << std::endl; }
} b;

int main()
{
  std::cout << std::endl << "rtest with a :" << std::endl;
  a.i =5; 
  a.show_i();
  a.A::show_i();
  
  std::cout  << std::endl << "test with b :" << std::endl;
  b.i=7;
  b.show_i();

  b.A::i = 12;
  b.B::i = 17;
  b.A::show_i();  
  b.B::show_i();

  b.show_i();
}


Добавлено через 4 минуты и 30 секунд
теперь построчный перевод вышеприведенного примера, но все методы заменены на обычные функции.
Код

struct A 
{
    int i;    
} a;

struct B : A
{
    int i;

} b;

void A_show_i (A& _this) { std::cout << "A::i == " << _this.i << std::endl; }
void B_show_i (B& _this) { std::cout << "B::i == " << _this.i << std::endl; }    
    
int main()
{
  std::cout << std::endl << "rtest with a :" << std::endl;
  a.i =5; 
  A_show_i(a);
  A_show_i(a);
  
  std::cout  << std::endl << "test with b :" << std::endl;
  b.i=7;
  B_show_i(b);

  b.A::i = 12;
  b.B::i = 17;
  A_show_i(b);  
  B_show_i(b);

  B_show_i(b);

}


Добавлено через 10 минут и 1 секунду
теперь тот же пример при отсутствии в языке механизма наследования
Код


struct A 
{
    int i;    
} a;

struct B 
{
    int A_i;
    int i;

} b;

void A_show_i (void * _this) { std::cout << "A::i == " << (*(A*)_this).i << std::endl; }

void B_show_i (void * _this) { std::cout << "B::i == " << (*(B*)_this).i << std::endl; }    

    
int main()
{
  std::cout << std::endl << "rtest with a :" << std::endl;
  a.i =5; 
  A_show_i(&a);
  A_show_i(&a);
  
  std::cout  << std::endl << "test with b :" << std::endl;
  b.i=7;
  B_show_i(&b);

  b.A_i = 12;
  b.i = 17;
  A_show_i(&b);  
  B_show_i(&b);

  B_show_i(&b);
}

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

Это сообщение отредактировал(а) mes - 23.2.2010, 11:58


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


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


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

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



опять тот же пример, но с отсутствием в языке механизма стуктур
Код

// struct A
typedef char A [ sizeof(int) // i (A_i)
               ];              
// struct B            
typedef char B [ sizeof(A)   // A
               + sizeof(int) // i (B_i)
               ];                        

int& A_i(char * _this) { return *(int*)(_this + 0); }
int& B_i(char * _this) { return *(int*)(_this + sizeof (A) + 0); }

void A_show_i (char * _this) { std::cout << "A::i == " << A_i(_this) << std::endl; }
void B_show_i (char * _this) { std::cout << "B::i == " << B_i(_this) << std::endl; }    

A a; B b;
   
int main()
{
  std::cout << std::endl << "test with a :" << std::endl;
  A_i (a) =5; 
  A_show_i(a);
  A_show_i(a);
  
  std::cout  << std::endl << "test with b :" << std::endl;
  B_i (b) =7;
  B_show_i(b);

  A_i(b) = 12;
  B_i(b) = 17;
  
  A_show_i(b);  
  B_show_i(b);

  B_show_i(b);

}


Добавлено @ 12:17
думаю примеров Вам хватит, и в них вроде бы отражены вплоть до низшего уровня все вопросы этой темы. smile

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


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


Опытный
**


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

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



Цитата(mes @  23.2.2010,  12:15 Найти цитируемый пост)
Код

// struct A
typedef char A [ sizeof(int) // i (A_i)
               ];              
// struct B            
typedef char B [ [U]sizeof(A)[/U]   // A
               + sizeof(int) // i (B_i)
               ];                        
int& A_i(char * _this) { return *(int*)([U]_this + 0[/U]); }
int& B_i(char * _this) { return *(int*)([U]_this + sizeof (A) + 0[/U]); }


Большое спасибо за терпеливое разъяснение и за пример с отсутствием структур -- очень доходчивый. Теперь понятно. Метод родителя можно "перенаправить" на унаследованные дата-члены -- это возможно за счет того, что в наследник добавляются сначала дата-члены родителя, а потом уже наследника. При такой последовательности методы родителя "знают" смещение "своих" унаследованных дата-членов.
А как быть с множественным наследованием?
Если class C: A, B, дата-члены В будут располагаться после дата-членов А, и тогда методы В будут "смотреть" не туда -- смещение будет неверным. Компилятор создает какой-нибудь дополнительный механизм?



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


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


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

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



Цитата(Gunslinger @  28.2.2010,  08:45 Найти цитируемый пост)
Метод родителя можно "перенаправить" на унаследованные дата-члены -- это возможно за счет того, что в наследник добавляются сначала дата-члены родителя, а потом уже наследника. При такой последовательности методы родителя "знают" смещение "своих" унаследованных дата-членов

ура ! smile  

Цитата(Gunslinger @  28.2.2010,  08:45 Найти цитируемый пост)
А как быть с множественным наследованием?
Если class C: A, B, дата-члены В будут располагаться после дата-членов А, и тогда методы В будут "смотреть" не туда -- смещение будет неверным. Компилятор создает какой-нибудь дополнительный механизм?

 smile , он добавляет еще дополнительное смещение вторично-унаследованных классов.

Это сообщение отредактировал(а) mes - 28.2.2010, 14:22


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


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


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

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



Цитата(Gunslinger @  28.2.2010,  08:45 Найти цитируемый пост)
и за пример с отсутствием структур -- очень доходчивый.

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

// struct A { int i };
typedef char A [ sizeof(int) // i (A_i)
               ];              
int& A_i(char * _this) { return *(int*)(_this + 0); }

// struct B : A { i };
typedef char B [ sizeof(A)   // A
               + sizeof(int) // i (B_i)
               ];                        
int&  B_i(char * _this) { return *(int*)(_this + sizeof (A) + 0); }
char* B_A(char * _this) { return _this + 0; }

// struct AB : A, B {};
typedef char  AB [ sizeof(A)   // A
                 + sizeof(B)   // B
                 ];
char* AB_A(char * _this) { return _this + 0; }
char* AB_B(char * _this) { return _this + sizeof(A); }

int main ()
{
    A a; B b; AB ab;

    A_i(a) = 1;                // a.i;

    A_i(B_A(b)) = 2;           // b.A::i =2;
    B_i(b) = 22;               // b.i=22;

    A_i(AB_A(ab)) = 3;         // ab.A::i = 3;
    B_i(AB_B(ab)) = 33;        // ab.B::i = 33; 
    A_i(B_A(AB_B(ab))) = 333;  // ab.B::A::i = 333; 
 
    std::cout << "a.i == "        << A_i(a)             << std::endl
              << "b.i == "        << B_i(b)             << std::endl              
              << "b.A::i == "     << A_i(B_A(b))        << std::endl
              << "b.B::i == "     << B_i(b)             << std::endl
              << "ab.A::i == "    << A_i(AB_A(ab))      << std::endl
              << "ab.B::i == "    << B_i(AB_B(ab))      << std::endl
              << "ab.B::A::i == " << A_i(B_A(AB_B(ab))) << std::endl;
}

P.S. в примере специально использовано двойное унаследование класса A классом AB () (прямое и косвенное посредством класса B), 
что делает пример не соответствующим правилам С++ из за появления неопределенностей преобразования (в нашем случае работает из за явного указания нужного преобразования), но имхо так лучше для понимания происходящего.


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


--------------------
PM MAIL WWW   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
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.0630 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


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

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