Форум программистов, компьютерный форум CyberForum.ru

Почему не стоит создавать массив объектов? - C++

Восстановить пароль Регистрация
 
dovskr
0 / 0 / 0
Регистрация: 13.03.2013
Сообщений: 18
18.03.2013, 00:00     Почему не стоит создавать массив объектов? #1
Нам преподаватель сказал, что создавать массив объектов лучше не стоит(надо создавать массив указателей на объекты) так как при наследовании что-то там происходит с преобразованиями типов и привёл пример того, как не стоит писать. Вот пример:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class A
{
    public
        virtual int f()=0;
}
 
class B: public A
{
    public
        int f();
}
 
A *index(A* p,int i)
{
    return &p[i];
}
 
....
....
B peet[Count]
A* p=index(peet,2)
....
....

Можете пояснить подробно что тут такого происходит когда в функцию передаётся указатель на массив объектов и почему так нельзя писать(как тут преобразование типов влияет)?
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
18.03.2013, 00:00     Почему не стоит создавать массив объектов?
Посмотрите здесь:

Как создавать массивы объектов, и как к ним обращаться? C++
C++ как создавать динамическое число объектов
C++ Почему не стоит использовать ^(XOR) в swap
можно ли в с++ создавать массив строк? C++
Почему допустимо создавать объект по другому объекту, но нельзя присваивать уже созданный объект, другому созданному объекту? C++
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
DU
1477 / 1053 / 45
Регистрация: 05.12.2011
Сообщений: 2,279
18.03.2013, 01:51     Почему не стоит создавать массив объектов? #2
подробности есть в книге
Наиболее эффективное использование C++. 35 новых рекомендаций по улучшению ваших программ и проектов
Автор: Скотт Мейерс
Правило 3. Никогда не используйте полиморфизм в массивах
http://rsdn.ru/res/book/cpp/most_effective_cpp.xml

Связано с глюками, которые возникнуть при арифметики с указателями.
Доступ по индексу - это один из таких случаев.
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
struct Base
{
   char ch;
};
 
struct Der : public Base
{
    char ch2;
};
 
//В плюсах указатель на Der преобразуется в указатель на Base;
//В нашем случае размер объектов типа Base меньше объектов типа Der.
//Создаем массив Der;
Der derArr[10];
 
// Получаем указатель на первый элемент, преобразованный к указателю на Base
Base* baseArrPtr = derArr;
 
// Теперь пытаемся изменить значение символа второго элемента массива:
baseArrPtr[1].ch = 'c';
 
//baseArrPtr[1].ch = 'c'; эквивалентно следующему:
Base* ptr = (baseArrPtr + 1);
ptr->ch = 'c';
 
//При арифметике с указателями они наращиваются в зависимости от размера
//объекта, а размер определяется типом указателя. В нашем случае это указатель
//на Base. Но размер Base меньше чем размер Der.
//это значит, что указатель ptr будет указывать не на второй элемент исходного массива
//производных объектов Der, а куда-то в середину первого объекта этого массива. А
//это ошибка.
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
18.03.2013, 02:35     Почему не стоит создавать массив объектов? #3
Вот так ты должен был написать:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
class A
{
    public:
        virtual int f()=0;
};
 
class B: public A
{
    public:
        int f() {};
};
 
A *index(A* p,int i)
{
    return &p[i];
}
 
 
int main () {
 int Count= 10;
 B peet[Count];
 A* p=index(peet,2);
 p->f();
 return 0;
}
Учитывая, что код демонстрационный, он вполне себе нормален. Какие всего этого последствия? Ну-во первых у тебя на данный момент занята память под Count элементов типа B. Оно тебе надо? В то время как по факту нужен всего один элемент- второй. Поэтому делаем так:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <stdio.h>
 
class A
{
    public:
        virtual int f()=0;
};
 
class B: public A
{
    public:
        int f() {};
};
 
A *index(A** p,int i)
{
    return p[i];
}
 
 
int main () {
 
 int Count= 10;
 
 //Вот массив указателей
 B* peet[Count];
 A* p=index((A**)peet,2);
 
 
 //Теперь p должен указывать на элемент типа B, но этого элемента нет!
 //мы ведь не создали ни один элемент типа B, мы создали только указатели на них!
 //исправим это положение, создадим такой элемент
 p= new B;
 
 //И вызываем функцию
 p->f();
 return 0;
}
то есть сейчас строчкой B* peet[Count]; я создаю массив указателей, а он по определению не может быть больше массива элементов, а может быть меньше. То есть экономится память и время на конструирование. Ведь в дальнейшем я создаю всё-таки один элемент строчкой p= new B; но это ведь не 10!

Цитата Сообщение от dovskr Посмотреть сообщение
Можете пояснить подробно что тут такого происходит когда в функцию передаётся указатель на массив объектов
Ничё особенного не происходит-то. В функцию index передаёдся адрес нулевого объекта массива beep и этото же адрес и возвращается. А ты что думал?
dovskr
0 / 0 / 0
Регистрация: 13.03.2013
Сообщений: 18
18.03.2013, 03:19  [ТС]     Почему не стоит создавать массив объектов? #4
Спасибо, теперь вроде бы ясно почему такое происходит. Но теперь возник другой вопрос.
Пусть есть классы A и B.
В классе А объявлен public int Ax, В классе B объявлен public int Bx;
Класс C наследуется от них обоих. Насколько я понимаю в памяти объект класса C будет состоять из данных класса A, после которых идут данные класса B и после них уже идут данные класса С.

Напишем например




C++
1
2
3
4
5
6
7
8
9
C x;
A* PointerA=&x;
B* PointerB=&x;
int a,b;
a=PointerA->Ax;  // Тут всё должно по идее сработать
b=PointerB->Bx;// А вот тут сработает ли(действительно ли вернёт то что находится в Bx)? Ведь копия класса B 
                    // будет располагаться в памяти после копии класса А
                   // а указатель B фактически указывает на начало копии объекта класса С, но сам является указателем
                  // на объект класса B
Добавлено через 15 минут
kravam
быдлокодер
 Аватар для kravam
1512 / 872 / 44
Регистрация: 04.06.2008
Сообщений: 5,271
18.03.2013, 05:19     Почему не стоит создавать массив объектов? #5
Ну так ты возьми да проверь, чё гадать-то на кофейной гуще?
Цитата Сообщение от dovskr Посмотреть сообщение
указатель B фактически указывает на начало копии объекта класса С, но сам является указателем // на объект класса B
Мало ли что куда указывает, сказано же, раз класс наследник то он наследует поля родительского класса. Можно то есть считать, что в классе C есть поле Bx. PointerB на что указывает? На x. x имеет Bx? Имеет. Значит, b=PointerB->Bx; сработает правильно, беспокоиться не о чем.

Если чё: в памяти нет ни объекта класса B, ни объекта класса A. Есть объект класса C состоящий из двух полей: Ax и Bx. Проверь:

C++
1
 printf ("%d\n", sizeof (x));
DU
1477 / 1053 / 45
Регистрация: 05.12.2011
Сообщений: 2,279
18.03.2013, 09:52     Почему не стоит создавать массив объектов? #6
правильнее все таки говорить что класс C состоит из подобъектов A и B;
и если брать указатели на них, то они не будут одинаковыми:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
C c;
C* cptr = &c;
A* aptr = &c;
B* bptr = &c;
 
// кастим, чтобы можно было сравнивать указатели без ворнингов и ошибок
const void* cptr2 = cptr;
const void* aptr2 = aptr;
const void* bptr2 = bptr;
 
// для случая наследования: class C : public A, pubic B {};
cptr2 == aptr2; 
cptr2 != bptr2; 
aptr2 != bptr2;
Yandex
Объявления
18.03.2013, 09:52     Почему не стоит создавать массив объектов?
Ответ Создать тему
Опции темы

Текущее время: 01:47. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru