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

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Передача в метод не наследника, а родителя. Как, получить доступ к свойствам наследника? 
:(
    Опции темы
Gunslinger
Дата 18.11.2009, 09:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Это вроде композицией называется. На примере сериализации. От этого класса есть какое-то количество наследников, которые все нужно сериализовать. Ошибочно реализовывать метод сериализации для каждого наследника, вместо этого создается один метод сериализации, но с аргументом не какого-либо наследника, а их родителя. Тогда в такой метод можно передавать любого наследника. Вопрос: в таком случае доступ к свойствам и методам наследника будет? Ведь наследник - это свойства\методы родителя + свои, наследник передается через аргумент с типом его родителя, то есть, свойства\методы наследника не доступны.. Или я ошибаюсь?

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


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


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

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



Цитата(Gunslinger @  18.11.2009,  08:56 Найти цитируемый пост)
Это вроде композицией называется.

нет, не это.

Цитата(Gunslinger @  18.11.2009,  08:56 Найти цитируемый пост)
Ошибочно реализовывать метод сериализации для каждого наследника, вместо этого создается один метод сериализации, но с аргументом не какого-либо наследника, а их родителя.

имхо, или не так описали, или не так поняли.
 smile 

Цитата(Gunslinger @  18.11.2009,  08:56 Найти цитируемый пост)
Вопрос: в таком случае доступ к свойствам и методам наследника будет?

либо базовый класс должен обладать необходимым интерфейсом, 
либо нужно привести базу к типу наследника.

 smile 


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


Опытный
**


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

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



Цитата(mes @  18.11.2009,  10:16 Найти цитируемый пост)
имхо, или не так описали, или не так поняли.

ага, писал по памяти. Сейчас найти ту информацию не могу. Нечто вроде:
Код

class Pet;
class Dog: Pet;
class Cat: Pet;
......
class N: Pet;

class PetHouse;
PetHouse house1; // есть метод isHomeCorrect(Pet& pet), смысл не корректен (придумывал на ходу), главное - должен вызываться для каждого наследника 
house1.isHomeCorrect(Dog); 
house1.isHomeCorrect(Cat);
......
house1.isHomeCorrect(N);

Вопрос: в таком варианте добавочные свойства\методы наследников Dog, Cat,.., N не доступны, раз они передаются по типу Pet? И если не доступны, как сделать так, чтобы были доступны и при этом сохранить такой тип аргумента, чтобы не реализовывать метод для каждого наследника?

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


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


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

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



Цитата(Gunslinger @  18.11.2009,  09:40 Найти цитируемый пост)
И если не доступны, как сделать так, чтобы были доступны 



"обычный" полиморфизм
двойная диспатчеризация (она же паттерн визитер)
dynamic_cast



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


Опытный
**


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

Репутация: нет
Всего: 38



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


--------------------
Тысячами незримых нитей обвивает тебя Закон. Разрубишь одну - преступник. Десять - смертник. Все - Бог.
Эвенгар Салладорский, основатель Школы Тьмы.
PM MAIL   Вверх
Gunslinger
Дата 18.11.2009, 13:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(mes @  18.11.2009,  11:43 Найти цитируемый пост)
"обычный" полиморфизм
двойная диспатчеризация (она же паттерн визитер)
dynamic_cast

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

Цитата(KelTron @  18.11.2009,  11:52 Найти цитируемый пост)
Так если у тебя логика функции отличается для разных типов объектов,

Само собой логика одна и та же, поэтому плодить метод бессмысленно, а как правильно - не знаю.



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


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


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

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



Цитата(Gunslinger @  18.11.2009,  12:53 Найти цитируемый пост)
про обычный полиморфизм не понял, как это возможно, может быть вы имеете ввиду виртуализацию? по остальному - гуглю.

тогда тоже в гугль (или сразу в книжку) - динамический полиморфизм



Цитата(Gunslinger @  18.11.2009,  12:53 Найти цитируемый пост)
Само собой логика одна и та же, поэтому плодить метод бессмысленно, а как правильно - не знаю.

ну для начала приведите пример более точно отражающий вашу задачу, чем пример выше.
smile


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


Опытный
**


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

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



Цитата(mes @  18.11.2009,  14:38 Найти цитируемый пост)
тогда тоже в гугль (или сразу в книжку) - динамический полиморфизм

Я про виртуальные функции и говорил.

Цитата(mes @  18.11.2009,  14:38 Найти цитируемый пост)
ну для начала приведите пример более точно отражающий вашу задачу, чем пример выше.

Пример привести не могу, т.к. конкретной задачи нет. Интересовало вот что: если мы передаем в функцию с параметром типа Родитель аргумент с типом ПотомокЭтогоРодителя, то в функции у нас будет доступ к свойствам и методам  потомка или только к унаследованным от Родителя?
Код

класс Родитель;
класс Потомок: Родитель;
указатель объект=new Потомок;

прототип:
функция\метод(Родитель объект);

в коде:
функция\метод(Потомок);



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


Опытный
**


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

Репутация: нет
Всего: 38



Цитата(Gunslinger @  19.11.2009,  09:35 Найти цитируемый пост)
то в функции у нас будет доступ к свойствам и методам  потомка или только к унаследованным от Родителя?

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




--------------------
Тысячами незримых нитей обвивает тебя Закон. Разрубишь одну - преступник. Десять - смертник. Все - Бог.
Эвенгар Салладорский, основатель Школы Тьмы.
PM MAIL   Вверх
mes
Дата 19.11.2009, 14:32 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(Gunslinger @  19.11.2009,  08:35 Найти цитируемый пост)
Пример привести не могу, т.к. конкретной задачи нет. Интересовало вот что: если мы передаем в функцию с параметром типа Родитель аргумент с типом ПотомокЭтогоРодителя, то в функции у нас будет доступ к свойствам и методам  потомка или только к унаследованным от Родителя?

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

http://forum.vingrad.ru/index.php?showtopi...t&p=1715082
http://forum.vingrad.ru/index.php?showtopi...t&p=1692823

(ну вот пришлось вместо Вас поиск пользовать... smile )

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


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


Опытный
**


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

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



mes, извините, что отнял столько времени с поиском -  я в гугле искал, а по форуму поискать не додумался.
Читаю ссылки, что вы дали. Понял, что в понимании низкого уровня пробел. Пока другой вопрос: про указатели и адреса памяти. Какой смысл писать тип указателя, тем более структуры или класса? Указатель же хранит адрес ячейки памяти, а структура\класс со своими полями\методами занимает больше, чем одна ячейка... Или эта инфа для компилятора?

з.ы. под ячейкой я понимаю 1 байт.

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


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


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

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



Цитата(Gunslinger @  19.11.2009,  15:34 Найти цитируемый пост)
mes, извините, что отнял столько времени с поиском -  я в гугле искал, а по форуму поискать не додумался.

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

Добавлено через 5 минут и 29 секунд
Цитата(Gunslinger @  19.11.2009,  15:34 Найти цитируемый пост)
Или эта инфа для компилятора?

Да.. но не только для компилятора, но и для удобства программиста.


Это сообщение отредактировал(а) mes - 19.11.2009, 16:56


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


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


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

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



Цитата(Gunslinger @  19.11.2009,  15:34 Найти цитируемый пост)
Указатель же хранит адрес ячейки памяти 

нет указатель нечто больше.. адрес то можно хранить и в void *, но тогда статическая информация  о типе (в частности об интерфейсе  доступа к объекту) будет утеряна.

Цитата(Gunslinger @  19.11.2009,  15:34 Найти цитируемый пост)
под ячейкой я понимаю 1 байт. 

Ну взять хотя бы  инкрементацию указателя.. Она изменяет его значение не на один байт, а на размер объекта типа.
 smile 

P.S. не забывайте про правило форума :один вопрос - одна тема.


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


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


Опытный
**


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

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



Цитата(mes @  19.11.2009,  17:12 Найти цитируемый пост)
P.S. не забывайте про правило форума :один вопрос - одна тема.

Понял, создал отдельную: "Механика работы указателей" -- 
http://forum.vingrad.ru/forum/act-ST/f-92/...0/unread-1.html



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


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


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

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



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

Типы бывают встроенными (int, double...) и пользовательскими (struct, class, union). 

Тип переменной проассоциирован с ней на протяжении всей ее видимости. т.е. программист пишет int i; и в дальнейшем везде где используется эта i  компилятор знает, что она int.

Благодаря типизации возможна перегрузка функций. Т.е. вызывая какую нибудь функцию с аргументами компилятор ищет из всех функций с одинаковым именем, наиболее подходящую.
Польза от перегрузки очевидная: 
Код

вместо того чтоб писать :

void f_int_double (a,b) {...}
void f_double_ int (a,b) {...}

var<4> a; //4 байта , условное int
var<8> b; //8 байт, условное  double
f_int, double(a,b); 
f_double_int(b,a);
//f_double_int(a,b); // ошибка которая не будет обнаружена компилятором


и самому помнить что хранит переменная и как с этим работать,  
мы перегружаем эту работу на компилятор и пишем:

void f(int a, double b) {..}
void f(double b, int a) {..}

int a;
int b;
f(a,b); // 
f(b,a); // компилятор сам выберет нужную функцию на основе типов.

В C++ перегрузка существует не  только для функций, но и для операторов. т.е. в выражении a+b; выберется к примеру  op+<int, int>() или op+<double,double>()

Однако одной перегрузки нeдостаточно для удобной работы, так как нам пришлось бы определять кучу одинаковых функций для всех нужных типов. Для решения этой проблемы в c++ введено приведение типов. (Оно бывает явное и неявное, а также делится по уровням продвижение, стандарное и пользовательское преобразования).
теперь для выражения char_+int_ не  нужно реализовывать отдельный плюс, а можно привести к виду int_ + int_. То же касается и пользовательских типов, для которых программист может определить нужные преобразования.

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

По типам вроде бы все, пойдем дальше.. Чтоб работать с объектом в С++ существуют временный объект, переменная, ссылка, и указатель.
Каждая это представление (еще одно помимо типа)  некой абстракции для пользования объектом. 

Указатель это такой объект, который хранит адрес другого объекта или 0, позволяет итерацию (размер шага которой определяется типом указателя), а также предоставляет доступ путем разъименования или через operator->()

Ссылка это сущность (не объект) которая проассоциирована с адресом объекта и предоставляет к нему доступ посредством operator.()


Ну кажется все.. Надеюсь объяснение не сильно отличается от истины smile



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


Опытный
**


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

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



mes, smile спасибо за такой развернутый ответ. разницу между ссылкой и указателем на "механическом" уровне я, вроде бы, понимаю - тоже нечто абстракции. Между программистом и аппаратурой - компилятор: программист не может создавать переменные\объекты где захочет. Компилятор выделяет каждой переменной\объекту определенный адрес, жестко привязывая ее (мнемокод) к этому адресу. А вот чтобы можно было "скакать" по памяти - ввели указатель. Временный объект, для уточнения, это переменная\объект в блоке, не глобальный\не поле объекта?

Цитата(mes @  20.11.2009,  12:20 Найти цитируемый пост)
позволяет итерацию (размер шага которой определяется типом указателя),

здесь не понял, моли бы вы пояснить?
PM MAIL   Вверх
mes
Дата 20.11.2009, 13:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(Gunslinger @  20.11.2009,  12:02 Найти цитируемый пост)
здесь не понял, моли бы вы пояснить? 

ну Вы же уже сами сказали :
Цитата(Gunslinger @  20.11.2009,  12:02 Найти цитируемый пост)
А вот чтобы можно было "скакать" по памяти - ввели указатель


или Вы про шаг ?
объясните себе почему в этом примере вычисления дают разный результат :
Код

    int i,  *p  = &i+1;
    std::cout << "ptr`s arifmethic: " << p - &i << "; int`s : " << (int)p - (int)&i <<";" << std::endl;







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


Эксперт
****


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

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



Gunslinger, 
Цитата

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


Динамический полиморфизм. Мне больше нравится пример иерархии графических объектов с функцией рисования объекта. В С++ реализуется посредством виртуальных функций.

Код

#include <iostream>
#include <vector>

// абстрактный базовый класс.
// мы не можем создавать объектов типа Shape, но можем - указатели и ссылки
class Shape {
 public:
  virtual void draw() const = 0; // чистая виртуальная функция. класс является абстрактным, если содержит хотя бы одну чистую (=0) виртуальную ф-ию
};

class Circle : public Shape {
    int radius;
  public:
    Circle () : radius(0) {}
    virtual void draw () const { std::cout << "I'm circle with radius=" << radius << std::endl; }
};

class Rect : public Shape {
    int width, height;
  public:
    Rect () : width(0), height(0) {}
    virtual void draw () const { std::cout << "I'm rect with width=" << width << " and height=" << height << std::endl; }
};

class Triangle : public Shape {
    int a, b, c;
  public:
    Triangle () : a(0), b(0), c(0) {}
    virtual void draw () const { std::cout << "I'm triangle with a=" << a << ", b=" << b << " c=" << c << std::endl; }
};

int main ()
{
   Shape *p = new Circle;
   p->draw (); // вызывается Circle::draw()
   p = new Rect;
   p->draw (); // вызывается Rect::draw()

   // создадим массив указателей на объекты типа Shape
   const int n = 10;
   std::vector <Shape*> v;
   for (int i=0; i < n; ++i)
   {
     Shape *sp;
     switch (rand()%3)
     {
      case 0: sp = new Circle; break;
      case 1: sp = new Rect; break;
      case 2: sp = new Triangle; break;
     }
     v.push_back (sp);
   }

   std::cout << "\nDraw shapes in array\n";
   for (int i=0; i < n; ++i)
     v[i]->draw ();
}


поскольку вызывается функция, реализуемая в конкретном классе-наследнике, ей известно и какого типа объект, и есть доступ к переменным класса.
PM MAIL   Вверх
Gunslinger
Дата 23.11.2009, 09:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(mes @  20.11.2009,  13:19 Найти цитируемый пост)
объясните себе почему в этом примере вычисления дают разный результат :
Код

    int i,  *p  = &i+1;
    std::cout << "ptr`s arifmethic: " << p - &i << "; int`s : " << (int)p - (int)&i <<";" << std::endl;


В переменной, на которую указывает р кладется сумма адреса i+1, затем выводится их разница. А вот потом не понял. Зачем приводить адреса к int? Они и так int.

Цитата(baldina @  20.11.2009,  14:44 Найти цитируемый пост)
Код

   Shape *p = new Circle;

Что значит эта запись с технической стороны? Создается переменная с размером Shape, она инициализируется размером Circle. Куда компилятор денет свойства\методы, которые не поместятся в размер Shape?

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


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


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

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



Цитата(Gunslinger @  23.11.2009,  08:33 Найти цитируемый пост)
А вот потом не понял. Зачем приводить адреса к int? Они и так int.

если это так, то почему разный результат ? Вы пробовали запускать ?


Цитата(Gunslinger @  23.11.2009,  08:33 Найти цитируемый пост)

Что значит эта запись с технической стороны? Создается переменная с размером Shape, она инициализируется размером Circle. 

Эта запись состоит из двух частей : 
правая - это создание нового объекта (т.е.  выделение памяти и инициализация ее в связи с требованием к объекту).
левая - это определение указателя инициализированного в данном случае адресом новосозданного объекта.

Цитата(Gunslinger @  23.11.2009,  08:33 Найти цитируемый пост)
Куда компилятор денет свойства\методы, которые не поместятся в размер Shape?

Методы описаны в классе, но не существуют в объекте. Память при создании объекта выделяется только под его дата-члены (в том числе и под служебные данные в некоторых случаях)
То что методы описываются внутри класса это всего лишь особенность С++, фактически нужная для того, чтоб был определен class scope функции, а также права доступа.



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


Эксперт
****


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

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



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

Цитата

Создается переменная с размером Shape, она инициализируется размером Circle. Куда компилятор денет свойства\методы, которые не поместятся в размер Shape?


Код

Shape *p = new Circle;


в динамической памяти (при помощи оператора new) создается объект типа Circle. Естественно, размером sizeof (Circle).
указатель на этот объект помещается в переменную p типа указатель на Shape. Это вполне законно, т.к. Circle наследует Shape.
При обращении к виртуальным методам класса Shape типа p->draw() будет вызываться функция не из класса Shape, а из Circle (технически это реализуется компилятором через таблицу виртуальных функций vtable, но для программиста этот факт совершенно не важен). Таким образом язык реализует динамический полиморфизм - одну из своих ключевых концепций.

Иными словами программист обращается с указателем p как с указателем на Shape, вызывая разные методы. Объект при этом ведет себя в зависимости от реального типа объекта, на который указывает p.
В некоторых (редких) случаях, когда требуется привести указатель на объект базового класса к производному, используется dynamic_cast<>, но в подавляющем большинстве ситуаций полиморфизма достаточно.

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

ЗЫ: Мы не можем корректно сделать копию объекта, имея только указатель базового класса, не прибегая к разным хитростям именно потому , что размер реального объекта неизвестен. Если подобное требуется, обычно реализуют в классе виртуальный метод clone().

Добавлено через 9 минут и 50 секунд
Gunslinger, почитайте таки про арифметику указателей. тут например.
PM MAIL   Вверх
Gunslinger
Дата 2.12.2009, 09:14 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(mes @  20.11.2009,  13:19 Найти цитируемый пост)
объясните себе почему в этом примере вычисления дают разный результат :
Код

    int i,  *p  = &i+1;
    std::cout << "ptr`s arifmethic: " << p - &i << "; int`s : " << (int)p - (int)&i <<";" << std::endl;

Наконец установил компилятор и проверил. Первый вычет действительно возвращает разницу в 1, а второй - 4. Получается, первый вычет это как бы высокоуровневое значение, а второй - типа sizeof, количество байт. А было похоже на приведение типа.

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


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


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

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



Цитата(Gunslinger @  2.12.2009,  08:14 Найти цитируемый пост)
Получается, первый вычет это как бы высокоуровневое значение, а второй - типа sizeof, количество байт. А было похоже на приведение типа.

нет .. в первом случае арифметика указателей, и считается сколько штук объектов (int) может поместиться,
а во втором обычная числовая математика, и показывает на сколько единиц отличаются значения,
также 3й вариант (char*)p - (char*)&i - тоже арифметика указателей, но так как объект в данном случае char (1 байтовый), можно принять, что считает разницу в  байтах.

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



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


Опытный
**


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

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



mes, не могу понять это: (int)&i и (char*)&i. Само приведение понятно: удаляются\добавляются недостающие\избыточные байты до нужного типа, адрес остается тот же. Т.е. работа с кусками памяти (ячейками). А тут операция приведения применяется к адресам ячеек, а не к самим ячейкам. А адрес - это номер всего лишь первого байта...
Могли бы вы разъяснить?
PM MAIL   Вверх
baldina
Дата 3.12.2009, 15:56 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Gunslinger, никакие байты (в данном случае) не удаляются. есть числовое значение (адрес), изменяется лишь его семантика (смысл) и, соответственно, способ его использования.
Код

int i1=8, int i2=4;
i1-i2; // === 8-4=4

int *p1=8, int *p2=4;
p1-p2; // === (8-4)/sizeof(int) = 1

int i3=10;
++i3; // === 10+1=11

int *p3=10;
++p3; // ===  p3 = p3+1 = p3+sizeof(int)*1 = 10+4=14

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


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


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

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



Цитата(Gunslinger @  3.12.2009,  12:54 Найти цитируемый пост)
не могу понять это: (int)&i и (char*)&i. Само приведение понятно: удаляются\добавляются недостающие\избыточные байты до нужного типа, адрес остается тот же. Т.е. работа с кусками памяти (ячейками). А тут операция приведения применяется к адресам ячеек, а не к самим ячейкам. А адрес - это номер всего лишь первого байта...
Могли бы вы разъяснить? 


Какие байты удаляются ?!   smile    Как это операция к адресу применяется ?!  smile 
Приведение к типу это не удаление, а просто смена взгляда.

давайте на абстрактных примерах:
у Вас есть ящик, в нем 12 коробок в каждом из которых 10 яблок.
представим ящик как коробки, сколько их ?    // 12
представим коробку как апельсины, сколько их ? // 10
представим ящик как апельсины, сколько их ? // 120

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

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

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



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


Опытный
**


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

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



baldina, 
mes,  да, сказал не корректно. Так, может, лучше: при приведении меняется тип и, соответственно, размер ячейки; все, что не попало в новый тип(размер) - остается "за бортом".  
mes, вроде аналог коробок..

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


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


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

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



Цитата(Gunslinger @  3.12.2009,  16:34 Найти цитируемый пост)
: при приведении меняется тип и, соответственно, размер ячейки; 

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

для начала вот Вам пример, объясните результат :
Код


    union
    {
        float f;
        int i;

    } u = { 1150. };

// полагаю на вашей платформе тоже sizeof (float) == sizeof (int) .. 
    std::cout <<" sizeof (float) is " << sizeof (float) << std::endl;
    std::cout <<" sizeof (unsigned) is " << sizeof (int) << std::endl << std::endl;

// union как Вы знаете располагает все свои элементы по одному адресу.
    std::cout << " &u.f == " << &u.f << std::endl;
    std::cout << " &u.i == " << &u.i << std::endl << std::endl;

// раз начальный адрес одинаков,  и занимаемый размер тоже,
// то как там могут хранится разные значения ?
    std::cout << " u.f == " << u.f << std::endl;
    std::cout << " u.i == " << u.i << std::endl << std::endl;






Это сообщение отредактировал(а) mes - 3.12.2009, 18:36


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


Опытный
**


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

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



mes, может быть потому, что типы разные? Хранящийся float компилятор выводит как int. Больше вариантов нет.
PM MAIL   Вверх
mes
Дата 4.12.2009, 14:09 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(Gunslinger @  4.12.2009,  12:22 Найти цитируемый пост)
, может быть потому, что типы разные? Хранящийся float компилятор выводит как int. Больше вариантов нет. 

угу, не смотря на то что в битовом представлении обе сущности идентичны, взаимодействие с ними происходит по разному. Это определяет тип.
Т.е. грубо говоря тип показывает не только (и не столько) сколько памяти требует объект, а как эту память трактовать, то есть какие действия и как с ней производить.
На самом деле память вообще не причем, это всего лишь частный случай. 


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


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


Опытный
**


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

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



Цитата(mes @  4.12.2009,  14:09 Найти цитируемый пост)
На самом деле память вообще не причем, это всего лишь частный случай. 

Спасибо за пример, доходчиво. Теперь осознал, что речь не только о количестве байт. Но, если это как-то связано с вопросами выше: (int)&i и (char*)&i, - то я все равно не догнал. Облазил гугл в поисках подобных примеров, но ничего не нашел - только приведение обычных переменных.
PM MAIL   Вверх
baldina
Дата 4.12.2009, 14:28 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


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

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



Gunslinger, см. выше, я же специально привел пример вычисдений с int и int*
PM MAIL   Вверх
mes
Дата 4.12.2009, 15:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


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


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

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



Цитата(Gunslinger @  4.12.2009,  13:22 Найти цитируемый пост)
Но, если это как-то связано с вопросами выше: (int)&i и (char*)&i, - то я все равно не догнал.


Gunslinger, в каждом ящике 10 яблок, ответьте  сколько яблок будет если увеличить на единицу
1. кол-во яблок
2. кол-во ящиков
3. вес яблока в граммах

Теперь давайте посчитаем в десятках, 10,20,30,40 следующее 50
в сотнях 100, 200, 300, 400, следующее 500.
Не смущает что слово "следующий " аналог ++ (+1) ?
T.e. реальное действие  зависит от контекста. Также и в арифметике указателей, при инкрементации адрес изменяется не на единицу, а на размер объекта.

т.е. ++pint  имеет смысл pint = (int*)( (char*) pint + sizeof(int) );
smile

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


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


Опытный
**


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

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



baldina, читал. Пример понял, но вот что он мне должен был разъяснить касательно приведения- нет. 

Цитата(mes @  4.12.2009,  15:16 Найти цитируемый пост)
T.e. реальное действие  зависит от контекста. Также и в арифметике указателей, при инкрементации адрес изменяется не на единицу, а на размер объекта.
т.е. ++pint  имеет смысл pint = (int*)( (char*) pint + sizeof(int) );

Сначала по ссылке baldina, а потом поисковика с прошлой недели изучаю все, что нашел про арифметику указателей. Так что этот момент я уже знаю. Вгоняют в ступор сами приведения со звездочками (int*, char* и т.п.). Без звездочек понятно... Или это указание компилятору, что приводятся не простые переменные, а указатели?

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


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


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

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



Цитата(Gunslinger @  4.12.2009,  16:16 Найти цитируемый пост)
Вгоняют в ступор сами приведения со звездочками (int*, char* и т.п.)

звездочка означает указатель ..  раньше был указатель на коробки (int*) , a привели к указателю на апельсины (char*)
ведь точка отсчета у них одна и та же. 
При вычитании коробок (int*) считали сколько коробок (int) поместится.
А при вычитании апельсинов (char*), считаем сколько поместится апельсинов (char).
По этому в одном случае получили 1 , то есть одну коробку, а в другом 4, то есть 4 апельсина.

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




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


Опытный
**


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

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



Цитата(Gunslinger @  4.12.2009,  17:16 Найти цитируемый пост)
++pint  имеет смысл pint = (int*)( (char*) pint + sizeof(int) );

Приводим указатель к char, затем смещаем на 4 байта и приводим обратно к int? 
PM MAIL   Вверх
Страницы: (3) [Все] 1 2 3 
Ответ в темуСоздание новой темы Создание опроса
Правила форума "С++:Общие вопросы"
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.4184 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


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

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