|
|
|
| Есть несколько вопросов:
1.
Я хочу создать очередь 3Д-объектов, причем количество вершин не определено.
Можно ли сделать так:
struct point{float x,y,z;};
class 3dobject
{
public:
point* vertex;
3dobject* next;
3dobject* prev;
/*тут типа функции создания, конструкторы-деструкторы, и т.д.*/
};
3dobject*first;
3dobject*last;
3dobject*current;
|
Могу ли я создать сначала очередь, а потом каждому элементу считывать вершины из файла, определять длину массива vertex и потом выделять память под сам массив:
current=new 3dobject; // создали объект
last->next=current; // связали указатели
last=current; // сделали новый элемент последним
last->vertex=new point[10]; //создали 10 вершин. можно ли так делать???
|
------------------------
И еще вопрос: нужно сделать очередь разных объектов, например
в одном объекте 1 массив вершин, а в другом - 2 массива. То есть нужен универсальный указатель на 2 разных объекта. Возможно ли такое? | |
|
|
|
|
|
|
|
для: alex19921992
(18.03.2007 в 08:26)
| | В общем можно, но надёжнее и быстрее было бы воспользоваться готовыми контейнерами STL, например, <list>. | |
|
|
|
|
|
|
|
для: alex19921992
(18.03.2007 в 08:26)
| | >> ... нужен универсальный указатель на 2 разных объекта. Возможно ли такое?
Здесь надо воспользоваться наследованием. Пусть эти 2 разных объекта будут производными от
одного базового класса. Тогда этим универсальным указателем будет указатель на базовый класс.
В очередь или список надо помещать именно его.
struct Base
{
enum Type {TYPE_ONE, TYPE_TWO};
Type m_type;
static Base *Create();
virtual ~Base();
};
struct Derived1 : public Base
{
Derived1();
virtual ~Derived1();
}
struct Derived2 : public Base
{
Derived2();
virtual ~Derived2();
}
|
Здесь очень важной является функция Create(). Это так называемая фабрика классов. Именно
она создает объекты классов Derived1 и Derived2 (считывая информацию для создания с диска,
например).
Работать с этими разными классами можно либо прочитав реальный тип объекта из поля m_type
и сделав явное приведение к нужному типу, либо полностью скрыть все данные объекта
(объявив их приватными) и работать с объектом только через виртуальные методы.
Хотя, если объекты отличаются только числом элементов в массиве, иерархия классов не
нужна. Но все равно придется завести поле, в котором хранится тип объекта.
И функции конструирования и рисования должны работать по разному, в зависимости от
типа объекта. | |
|
|
|
|
|
|
|
для: oleg_alexeev
(18.03.2007 в 19:32)
| | принцип в общем понятен, спасибо.
но есть некоторые неясности...
слово virtual перед деструктором - это обязательно? что значит это слово? И вот struct Derived2 : public Base - типа public - это предок, да? и наверно не структ, а класс? | |
|
|
|
|
|
|
|
для: alex19921992
(19.03.2007 в 09:05)
| | 1. struct и class на деле отличаются в основном уровнем доступа по умолчанию (public для struct и private для class). Struct - объявление в стиле СИ, оно используется достаточно часто
2. Деструкторы лучше делать виртуальными, чтобы при уничтожении объекта был вызван именно деструктор подкласса, а не родителя. | |
|
|
|
|
|
|
|
для: Фитч
(19.03.2007 в 09:20)
| | Вот я знаю, что конструктор инициализирует объект, т.е. дает начальные значения переменным, ну и если объект динамический, то выделяет память под него. А деструктор? Только удалить память и перенаправить указатели в очереди?(если объект динамический).
Так что же значит слово virtual? | |
|
|
|
|
|
|
|
для: alex19921992
(19.03.2007 в 11:19)
| | > Так что же значит слово virtual?
Суть такая: если в базовом классе есть функции, объявленные с virtual (необязательно конструктор/деструктор), а в подклассе они переопределены, то если вызвать этот метод базового класса, то на этапе исполнения будет решаться, метод какого класса (базового или кого-то из наследников) будет вызван (полиморфизм, позднее связывание - знакомы?) Для ясности пример:
struct Base
{
virtual void showMessage()
{
fprintf(stdout,"Message from Base!");
}
};
struct SubClass : public Base
{
virtual void showMessage()
{
fprintf(stdout,"Message from Subclass");
}
};
void alert(Base* o)
{
o->showMessage();
}
/* ... */
Base* b = new Base;
SubClass* s = new SubClass;
alert(b);
alert(s);
}
|
Будет выведено:
Message from Base!
Message from subclass
В случае с вашими классами это будет нужно для корректной работы деструктора в завизимости от подкласса, если вы все объекты будете обрабатывать как Base | |
|
|
|
|
|
|
|
для: alex19921992
(19.03.2007 в 11:19)
| | >> Так что же значит слово virtual?
Это ключевое слово означает, что данная функция вызывается не непосредственно, а через
указатель, который находится внутри объекта. Когда функция Create создает объект типа Derived1
оператором new, то он помещает в объект указатель на ~Derived1 (для Derived2 - соответственно
на ~Derived2). После использования объектов мы их удаляем оператором delete. Он считывает из
объекта указатель на деструктор и вызывает именно тот деструктор, который соответствует типу
объекта (~Derived1 или ~Derived2).
Base *pobj = Base::Create();
pobj->Draw(); // нарисуется нужная фигура (если Draw виртуальная функция)
delete pobj; // здесь вызовется именно нужный деструктор
|
Такой прием программирования называется полиморфизмом (изменение поведения в
производном классе)
>> Вот я знаю, что конструктор инициализирует объект, т.е. дает начальные значения переменным,
>> ну и если объект динамический, то выделяет память под него. А деструктор? Только удалить
>> память и перенаправить указатели в очереди?
Деструктор только освобождает занятые объектом ресурсы, такие как выделенная память (когда
в объекте есть указатели и память выделяется при вызове конструктора или где-то в процессе
работы), дескрипторы файлов, сокетов и т.п.
Если объект входит в какие-либо списки или очереди, то изменение указателей в них - это не
задача деструктора, а задача функции удаления элемента из этой структуры данных. | |
|
|
|
|
|
|
|
для: oleg_alexeev
(19.03.2007 в 11:53)
| | Но по идее при удалении объекта из очереди надо в самом конце вызвать деструктор? | |
|
|
|
|
|
|
|
для: alex19921992
(19.03.2007 в 14:05)
| | Нет, не надо. Более того - нельзя самому вызывать деструктор объекта. Вызов конструктора и
деструктора генерируется компилятором автоматически при объявлении объекта на стеке или
при создании и удалении его в глобальной памяти операторами new и delete.
На начальном этапе обучения можно запомнить это в качестве правила - нельзя самому вызывать
конструктор и деструктор.
На самом деле вызывать их самому можно, но только в очень специфическом случае. Не буду
про него сейчас говорить, дабы не усложнять тему. | |
|
|
|
|
|
|
|
для: oleg_alexeev
(19.03.2007 в 14:26)
| | То есть я могу сделать конструктор и деструктор пустыми и заботиться обо всем сам? | |
|
|
|
|
|
|
|
для: alex19921992
(19.03.2007 в 15:48)
| | Конечно можно. Но это уже больше похоже на стиль языка Си. В большой программе окажется, что
код, работающий с внутренними данными объектов будет разбросан по разным местам и, при
внесении изменений в структуру объекта придется вносить изменения во множество мест. На
практике стараются сделать объекты и их методы такими, чтобы при изменении структуры объекта
поменялись только его методы, но не код, использующий эти объекты. | |
|
|
|
|
|
|
|
для: oleg_alexeev
(19.03.2007 в 16:18)
| | >На начальном этапе обучения можно запомнить это в качестве правила - нельзя самому вызывать
>конструктор и деструктор.
Нужно вызывать конструктор явно,хотя бы для того чтобы потом не делать кучу вызовов типа
| |
|
|
|