Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Центр помощи > [C++] Структура, хранящая обьекты разных типов


Автор: AlterMann 23.12.2006, 18:14
Здравствуйте, товарищи! (:

В общем объясняю проблему.
Есть абстрактный класс продукт, который наследуют ещё 4 класса (подтипы продуктов)...
Я сделал следующим образом: написал 2 конструктора у класса продукт. Один конструктор - спрашивает у пользователя параметры продукта, другой конструктор (полиморфизм) получает эти данные через сигнатуру (если так можно выразиться (: )...

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

class ProdList
{
 private:
  static ProdList *last;
  ProdList *next;

 public:
  static ProdList *begin;
  Product *Prod;

  ProdList(Product *);
  static void ShowAll();
};
ProdList *ProdList::begin = NULL;
ProdList *ProdList::last = NULL;

ProdList::ProdList(Product *p)
{
  if(begin == NULL) begin = this;
  else last->next = this;
  Prod = p;
  next = NULL;
  last = this;
}

void ProdList::ShowAll()
{
  ProdList *pp = begin;
  while(pp != NULL)
  {
    pp->Prod->Show(); // Show() - виртуальная функция у класса продукт
    pp = next->next;
  }
}

// и дальнейшее добавление объектов в такую структуру следующим образом:
PodProduct MyProduct(...); // создаёшь объект
ProdList AllProd(MyProduct); // запихиваешь объект в структуру

Но! Возникает такая некрасивая штука: Объект создаёшь в одной функции, потом передаёшь указатель на него в другую, где уже происходит тот самый ProdList AllProd(MyProduct);. Но после того как указатель (не тот, который *, а который перемещается по коду программы, обеспечивая её работу) выходит за пределы фунции, в которой создаётся обект класса подпродукт - объект уничтожается (область видимости переменной решает)... и получается, что указатель в структуре ProdList, указывает на область памяти, где уже нет объекта...

Можно сделать другую структуру, для сохранения, которая будет хранить не указатели на объекты, а сами объекты:
Код

template <class P> struct List
{
  P Prod;
  List *next;

};

List <PodProductT1> *begin1 = NULL, *last1 = NULL, *list1;
List <PodProductT2> *begin2 = NULL, *last2 = NULL, *list2;
List <PodProductT3> *begin3 = NULL, *last3 = NULL, *list3;
List <PodProductT4> *begin4 = NULL, *last4 = NULL, *list4;

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

AddProduct(Product *p)
{
  if(p->GetClass() == PodProuctT1T) // GetClass() - метод подпродукта, который возвращает какого типа подпродукт... с буквой Т на конце... (PodProuctT1T)
  {
    last1 = new(List <PodProductT1>);
    last1.Prod = p; // в классе продукт описан operator =
    last1->next = NULL;

    if(begin1 == NULL) begin1 = last1;
    else
    {
      list1 = begin1;
      while(list1->next) list1 = list1->next;
      list1->next = last1;
    }
  }
}

Ругается на пятую строчку, что: "компилятор не может сгенирировать дефолтовый конструктор для класса 'List<PodProductT1>'"

Хожу заморачиваюсь этой проблемой уже недели две... А всё никак не могу придумать как же это всё сохранить...

Наверняка многие уже сталкивались с такой проблемой и решили её для себя... не подскажите, что мне делать?..

ЗЫ Компилятор (Turbo C++ IDE), надо уже прогу сдавать... блин! ):
(:

Автор: Rockie 23.12.2006, 18:44
Цитата(AlterMann @  23.12.2006,  18:14 Найти цитируемый пост)
Нужно организовать сохранение объектов подтипов продуктов, в отдельный массив (структуру)... Так вот, я никак не могу придумать как это лучше сделать... По идее, когда объявляешь массив об]ектов подтипов продуктов, то ругается, что неможет найти дефолтовый конструктор, для инициализации элементов массива...


AlterMann, если есть базовый класс Base и его потомки Derived1 и Derived2, то можно создать массив указателей на базовый класс, а потом создавать объеты потомков. 
Код
Base* ptrArray[5];
ptrArray[0] = new pDerived1;
ptrArray[1] = new pDerived2;
// и так далее







Автор: jnb 23.12.2006, 18:56
Если у тебя проблема только в сохранении разнотипных объектов, имеющих одного предка, в каком-то массиве (векторе, списке), то можешь поступить следующим образом:
1. объявить массив указателей на свой абстрактный родитель
2. создавать объект не на стеке а в куче, через new: таким образом получаешь указатель на конретный объект
3. в массив добавляешь указатель на этот объект
...
4.  когда массив становиться  не нужен - удаляешь все указатели в нем оператором delete.


Код


//абстрактный класс
class Product 
{
//....
}

// конкретный класс
class Product1: public Product
{
//...
}

...

std::vector<Product*> products;

Product1* pProduct = new Product1();
products.push_back(pProduct);

// тут работаешь со своим массивом продуков через интерфейс абстрактного класса

// тут удаляешь указатели
for(int i = 0; i < products.size(); ++i)
{
   delete products[i];
}



Автор: Rockie 23.12.2006, 19:41
Цитата(jnb @  23.12.2006,  18:56 Найти цитируемый пост)
2. создавать объект не на стеке а в куче, через new: таким образом получаешь указатель на конретный объект

jnb, на стеке тожде можно, если вытащить ссылку:
Код
    Base *ptrArray[4];
    Derived1 obj1;
    ptrArray[0] = &obj1;


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

Код
#include "stdafx.h"

#include <iostream>

using std::cout;
using std::string;
using std::endl;

class Base
{
public:
    string productName;
    Base(string name = "default") {productName = name;}
    virtual void f()=0;    // pure virtual, declaration
};


class Derived1: public Base
{
public:
    Derived1(): Base() { };
    // realisation 
    virtual void f() { cout<<"Derived1 "<<productName.c_str()<<endl; };
};


class Derived2: public Base
{
public:
    Derived2(): Base("NamedProduct") { };
    // realisation 
    virtual void f() { cout<<"Derived2 "<<productName.c_str()<<endl; };
};


int main ()
{
    Base *ptrArray[4];
    
    ptrArray[0] = new Derived1();
    ptrArray[1] = new Derived2();
    
    for(int i=0; i<2; i++)
      ptrArray[i]->f();

    return 0;
}



Автор: jnb 23.12.2006, 20:45
Цитата(Rockie @ 23.12.2006,  19:41)
jnb, на стеке тожде можно, если вытащить ссылку:
Код
    Base *ptrArray[4];
    Derived1 obj1;
    ptrArray[0] = &obj1;


без сомнения можно и на стеке, но как мне показалось AlterMann жаловался именно на разрушение объекта: т.е. массив переживает объекты на которые ссылаются его элементы.

Автор: AlterMann 24.12.2006, 01:59
Спасибо большое! очень помогли...  smile 
Сильно загнался по поводу связных списков... и запутался...  smile 
Таки распутали вы меня!  smile 

Автор: AlterMann 24.12.2006, 19:33
Скажите, а могу ли я прописать в потомке класса продукт
Код

operator += (Product *); // с дальнейшим описанием данного оператора...


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

AddProduct(Product *p)
{
  ...
  ProductList[i] += p;
  ...
}

то не катит, ругается следующим образом:
Код

Invalid pointer addition


Подскажите пожалуйста, где я не прав.

Автор: jnb 24.12.2006, 20:31
AlterMann напиши, что  конкретно ты хочешь сделать: зачем к одному продукту прибавлять другой? Какого типа у тебя ProductList? Что должна выполнить по твоей задумке инструкция  ProductList[i] += p;?
А то в моем понимании твой operator+= выполняет добавление к кефиру скажем батона или селедки...

Автор: AlterMann 24.12.2006, 22:27
jnb, начну по порядку:
ProductList - у меня - массив указателей на объекты подпродуктов (наследников абстрактного класса Product). Вы же мне про этот массив и написали... (:

При добавлении продукта проверяется, есть ли уже такой продукт:
Код

void AddProduct(Product *p)
{
  int i;

  for(i=0;i<Nums;i++)
  {
    if(ProductList[i] == p)
    {
      ProductList[i] += p;
      break;
    }
  }
// ...
}

(т.е. по идее мне ещё нужно переопределить оператор ==, вернее я его переопределил, но ситуация такая же как и с +=)
И если продукт уже есть (по разным параметрам проверяет, типа дата производства, каллорийность), то просто прибавляем новый продукт к уже существующему...

Автор: AlterMann 25.12.2006, 04:42
Извините... спасибо... сделал уже...
Нужно ж обращаться по указателю
Код

ProductList[i]->operator == (p);

Автор: Rockie 25.12.2006, 15:45
Цитата(AlterMann @  24.12.2006,  22:27 Найти цитируемый пост)
И если продукт уже есть (по разным параметрам проверяет, типа дата производства, каллорийность), то просто прибавляем новый продукт к уже существующему...

А зачем?.. Нельзя просто счетчик продуктов где-то завести?

Автор: jnb 25.12.2006, 21:33
вот так тоже должно работать:

Код

(*ProductList[i]) == p
(*ProductList[i]) += p



Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)