Dynamiczna tablica obiektów jednej klasy jako składowa klasy drugiej.

0

Witam , dla sportu postanowiłem zaimplementować "Grę w życie" i pierwsza ściana na jaką napotkałem , to problem z dynamicznym tworzeniem i usuwaniem obiektów. Otóż jest klasa Cell oraz klasa Playground. Wywołując konstruktor obiektu klasy Playground , chcę tworzyć tablicę obiektów klasy Cell. Następnie pracować na nich wewnątrz tej klasy a następnie w destruktorze obiektu klasy Playground zwolnić tą tablicę. Niestety problemem jest to , że o ile zaalokuję taką tablicę w konstruktorze , to już w żadnej metodzie ani destruktorze nie jest ona widoczna. Wpisałem więc jako składową klasy Playground wskaźnik na obiekt klasy Cell , lecz Visual Studio nie chce mi tego przepuścić.
Co robię źle?
Może niewłaściwie podszedłem do problemu?
Proszę uprzejmie o pomoc :)

Playground.cpp

#include "Playground.h"
#include "Cell.h"
#include <iostream>
using namespace std;

Playground::Playground(int inputSize)
{
	this->PlaygroundSize = inputSize;
	Cell *CellVector = new Cell[PlaygroundSize*PlaygroundSize];
	cout << "Utworzono Plansze";
}

Playground::~Playground()
{
	delete[]CellVector;
	cout << "Usunieto plansze";
}

void Calculate()
{
	cout << "Obliczono.";
}

void Display()
{
	cout << "Wyswietlono.";
}

Playground.h

#pragma once
class Playground
{
public:
	Playground(int);
	virtual ~Playground();
	void Calculate();
	void Display();
private:
	int PlaygroundSize;
	Cell *CellVector;
};
Error	2	error C4430: missing type specifier - int assumed. Note: C++ does not support default-int	c:\users\mariusz\skydrive\documents\projekty\gameoflife\gameoflife\playground.h	11	1	GameOfLife
Error	6	error C4430: missing type specifier - int assumed. Note: C++ does not support default-int	c:\users\mariusz\skydrive\documents\projekty\gameoflife\gameoflife\playground.h	11	1	GameOfLife
Error	4	error C2541: 'delete' : cannot delete objects that are not pointers	c:\users\mariusz\skydrive\documents\projekty\gameoflife\gameoflife\playground.cpp	15	1	GameOfLife
Error	1	error C2143: syntax error : missing ';' before '*'	c:\users\mariusz\skydrive\documents\projekty\gameoflife\gameoflife\playground.h	11	1	GameOfLife
Error	5	error C2143: syntax error : missing ';' before '*'	c:\users\mariusz\skydrive\documents\projekty\gameoflife\gameoflife\playground.h	11	1	GameOfLife
Error	3	error C2065: 'CellVector' : undeclared identifier	c:\users\mariusz\skydrive\documents\projekty\gameoflife\gameoflife\playground.cpp	15	1	GameOfLife

Podejrzewam , że błąd dotyczy tego , że nie mogę tak po prostu użyć nazwy jednej klasy jako typu w deklaracji innej klasy lecz nie wiem jak to lepiej zapisać.

1

Może niewłaściwie podszedłem do problemu?
Ogólnie tak, bo powinieneś korzystać z std::vector zamiast się bawić w alokację/dealokację.

Natomiast błąd w Twoim kodzie jest tutaj:

Cell *CellVector = new Cell[PlaygroundSize*PlaygroundSize];

Deklarujesz nową lokalną zmienną o nazwie CellVector przesłaniając jednocześnie pole o tej samej nazwie. Powinno być:

CellVector = new...
0

Właśnie o tym kontenerze pomyślałem , tylko czy obiekty wrzucone do niego zostaną zniszczone ,w którymś momencie czy jak?

@EDIT1: Druga sprawa , to jak wywoływać konstruktor obiektów w pętli? Chodzi o coś w stylu:

int i;
for(i=0;i<SIZE;i++)
{
     MyClass Nazwa_Obiektu+i;    //np. Obiekt0, Obiekt1 itp...
}
1
Stadzior napisał(a):

Właśnie o tym kontenerze pomyślałem , tylko czy obiekty wrzucone do niego zostaną zniszczone ,w którymś momencie czy jak?
Zostaną gdy kontener zostanie zniszczony.

vector<MyClass> v;
for (int i = 0; i < SIZE; ++i)
{
    v.push_back(MyClass());
    // ALBO
    v.emplace_back();
}
0

I wtedy obiekty o jakich nazwach się utworzą? MyClass1 MyClass2 itd.?

2

Żadnych nazw nie będą miały. Odwołać się do nich możesz poprzez v[0], v[1] albo przez iteratory wektora.

0

Wielkie dzięki za pomoc :) Jesteś wielki!

@Edit kolejny problem: obiekty w wektorze zwalniają się nie kaskadowo tylko w sposób niekontrolowany ponadto w Id wrzucają mi się śmieci.

Playground::Playground(int inputSize)
{
	this->PlaygroundSize = inputSize;
	vector<Cell>CellVector;
	int i;
	for (i = 0; i < PlaygroundSize*PlaygroundSize;i++)
	{
		CellVector.push_back(Cell());
		CellVector[i].Id = i;
	}
	cout << "Utworzono Plansze";
}

Spodziewam się wyświetlenia w konsoli:

Utworzono komorke nr.0
Utworzono komorke nr.1
Utworzono komorke nr.2
Utworzono komorke nr.3
Utworzono komorke nr.4
Utworzono komorke nr.5
Utworzono komorke nr.6
Utworzono komorke nr.7
Utworzono komorke nr.8
Utworzono plansze.
Skasowano komorke nr.0
Skasowano komorke nr.1
Skasowano komorke nr.2
Skasowano komorke nr.3
Skasowano komorke nr.4
Skasowano komorke nr.5
Skasowano komorke nr.6
Skasowano komorke nr.7
Skasowano komorke nr.8
Skasowano plansze.

A otrzymuje coś takiego:
user image

0

Pokaż klase Cell.

0

Cell.cpp

#include "Cell.h"
#include <iostream>
using namespace std;

Cell::Cell()
{
	cout << "\nUtworzono komorke nr."<<Id;
}


Cell::~Cell()
{
	cout << "\nSkasowano komorke." << Id;
}

Cell.h

#pragma once
class Cell
{
public:
	int Id;
	Cell();
	virtual ~Cell();
};
2

Po pierwsze zauważ, że jest ponad 2x więcej Skasowano niż Utworzono. Wynika to z faktu, że najpierw jest utworzony obiekt poprzez

Cell()

potem jest utworzony nowy obiekt w wektorze (ale konstruktorem kopiującym lub przenoszącym, więc nie widzisz komunikatu), usuwany jest pierwszy tymczasowy obiekt, a na samym końcu usuwany jest obiekt w wektorze.
Dlatego lepszym rozwiązaniem jest

CellVector.emplace_back()

który od razu tworzy obiekt w wektorze bez tworzenia tego tymczasowego.

Poza tym jeśli w którymś momencie wektor robi sobie realokację to też możesz zobaczyć dodatkowe Skasowano. Jak nie chcesz to dodaj

CellVector.reserve(PlaygroundSize*PlaygroundSize);

tuż po utworzeniu wektora.

W Id masz śmieci bo w chwili utworzenia Cell pewnie nie inicjalizowałeś tej wartości. No i zgaduję, że komunikat wypisywania jest w konstruktorze, czyli przed przypisaniem do tej zmiennej sensownej wartości.

I używaj ++i zamiast i++. Zamiast

int i;
for (i = 0; i < PlaygroundSize*PlaygroundSize;i++)

pisz

for (int i = 0; i < PlaygroundSize*PlaygroundSize; ++i)
2

Tworzysz nowy obiekt przed zainicjowaniem danych.
CellVector.push_back(Cell()); <-- Tu wypisujesz dane id, a one są wtedy niezainicjowane (ich wartość jest losowa).

0

Dzięki, z tymi śmieciami to faktyczne faux pas z mojej strony , ale jeszcze ciekawi mnie co robi metoda CellVector.reserve. Po prostu zabrania vectorowi się realokować , czy jak? A zamiana i++ na ++i nie sprawi , że będzie pętla iterowana od 1?

@Edit: Użycie emplace_back() nic nie zmieniło :/

@EDIT2: Po użyciu metody CellVector.reserve wyświetla się:

Utworzono komorke 
Skasowano komorke 
Utworzono komorke 
Skasowano komorke 
Utworzono komorke 
Skasowano komorke 
Utworzono komorke 
Skasowano komorke 
Utworzono komorke 
Skasowano komorke 
Utworzono komorke 
Skasowano komorke 
Utworzono komorke 
Skasowano komorke 
Utworzono komorke 
Skasowano komorke 
Utworzono komorke 
Skasowano komorke 
                 Utworzono plansze.
Skasowano komorke 
Skasowano komorke 
Skasowano komorke 
Skasowano komorke 
Skasowano komorke 
Skasowano komorke 
Skasowano komorke 
Skasowano komorke 
Skasowano komorke 
                Skasowano plansze.
1

Coś nie chce mi się wierzyć. Masz coś takiego?

Playground::Playground(int inputSize)
{
    PlaygroundSize = inputSize;
    vector<Cell>CellVector;
    CellVector.reserve(PlaygroundSize*PlaygroundSize);

    for (int i = 0; i < PlaygroundSize*PlaygroundSize; ++i)
    {
        CellVector.emplace_back();
        CellVector[i].Id = i;
    }
    cout << "Utworzono Plansze";
}

Swoją drogą ustawianie Id powinno być w konstruktorze, a nie w ten sposób.

co robi metoda CellVector.reserve
od razu rezerwuje odpowiednią ilość pamięci, więc wektor nie musi potem robić realokacji

A zamiana i++ na ++i nie sprawi , że będzie pętla iterowana od 1
NIE

0

Teraz działa :) A jeszcze podpowiedz mi proszę , w jaki sposób przekazać do konstruktora wartość "i" ? jeżeli zrobię konstruktor z parametrem to nie zostanie on wywołany przez emplace_back(). Więc jak?

@Edit: już sobie poradziłem , wielkie dzięki jeszcze raz!

0

Kolejny problem , utworzonego w konstruktorze vectora dalej nie widać w metodach i destruktorze klasy Playground. A w deklaracji w pliku Playground.h nie chce mi go przyjąć jako składową , tłumacząc się , że to nie jest poprawny typ i przyjmuje domyślnie inta.

@Edit: Dokładniej , vector z obiektami klasy Cell generuje się w konstruktorze obiektu klasy Playground, ale jak wyjdzie z konstruktora to już się zwalnia.
Jak sprawić , aby zwalniał się dopiero przy destruktorze obiektu klasy Playground?

@Edit: Problem rozwiązany. zapominałem użyć przestrzeni nazw std.

1 użytkowników online, w tym zalogowanych: 0, gości: 1