C++ - niezrozumiała asercja.

0

Witam.

Mam problem ze swoją aplikacją, oto jej kod:

 
#include <iostream>
#include <vector>
#include <string>
#include <memory>


using namespace std;


class BaseClass
{
public:
	BaseClass(std::string name, std::string secondName) :
		m_name(name), m_secondName(secondName)
	{

	}

	void log()
	{
		std::cout
			<< "Name: "
			<< this->m_name
			<< " SecondName: "
			<< this->m_secondName
			<< std::endl;
	}

protected:
	std::string m_name;
	std::string m_secondName;
};


class SubClass : public BaseClass
{
public:
	SubClass(BaseClass* baseItemPtr) :
		BaseClass(*baseItemPtr),
		m_age(0)
	{

	}

	void setAdditionalValue(const int value)
	{
		this->m_age = value;
	}

	void logX()
	{
		std::cout
			<< "Name: "
			<< this->m_name
			<< " SecondName: "
			<< this->m_secondName
			<< " Age: "
			<< this->m_age
			<< std::endl;
	}

private:
	int m_age;
};


void symulation(std::vector<std::shared_ptr<BaseClass>>& vectorItem)
{
	for (std::shared_ptr<BaseClass>& item : vectorItem)
	{
		
		SubClass* ptr = (SubClass*)item.get();
		ptr->logX();
	}
}

int main(int argc, char *argv[])
{
	BaseClass item("Bolek", "Lolek");

	SubClass itemSub(&item);
	itemSub.setAdditionalValue(32);

	SubClass itemSubB(&item);
	itemSubB.setAdditionalValue(44);

	std::vector<std::shared_ptr<BaseClass>> myVec;
	myVec.push_back(std::shared_ptr<BaseClass>(&itemSub));
	myVec.push_back(std::shared_ptr<BaseClass>(&itemSubB));

	symulation(myVec);

	return 0;
}

No i moje pytanie brzmi: skąd bierze się ta asercja pod koniec wykonywania programu? O czym zapomniałem?
Screen w załączniku.

Będę niezmiernie wdzięczny za szybką odpowiedź.
Pozdrawiam.

5

Tak się nie korzysta ze shared_ptr. shared_ptr przejmuje własność przekazanego wskaźnika, a ty przekazujesz adres zmiennej o automatycznym czasie życia. To nie ma sensu.

0

No to jak inaczej twoim zdaniem umieszczać obiekty klasy pochodnej, w wektorze klasy bazowej tak aby przy rzutowaniu nie obcinało mi danych z klasy pochodnej?

0

Tak:

#include <iostream>
#include <vector>
#include <string>
#include <memory>
 
using namespace std;

class BaseClass
{
public:
    BaseClass(std::string name, std::string secondName) :
        m_name(name), m_secondName(secondName)
    {
 
    }
 
    virtual void log()
    {
        std::cout
            << "Name: "
            << this->m_name
            << " SecondName: "
            << this->m_secondName
            << std::endl;
    }
 
protected:
    std::string m_name;
    std::string m_secondName;
};
 
 
class SubClass : public BaseClass
{
public:
    SubClass(const BaseClass &base) :
        BaseClass(base),
        m_age(0)
    {
 
    }
 
    void setAdditionalValue(const int value)
    {
        this->m_age = value;
    }
 
    virtual void log() override
    {
        std::cout
            << "Name: "
            << this->m_name
            << " SecondName: "
            << this->m_secondName
            << " Age: "
            << this->m_age
            << std::endl;
    }
 
private:
    int m_age;
};
 
 
void symulation(std::vector<std::shared_ptr<BaseClass>>& vectorItem)
{
    for (std::shared_ptr<BaseClass>& item : vectorItem)
    {
        item->log();
    }
}
 
int main(int argc, char *argv[])
{
    BaseClass item("Bolek", "Lolek");
 
    auto itemSub = std::make_shared<SubClass>(item);
    itemSub->setAdditionalValue(32);

    auto itemSubB = std::make_shared<SubClass>(item);
    itemSubB->setAdditionalValue(44);
 
    std::vector<std::shared_ptr<BaseClass>> myVec;
    myVec.push_back(itemSub);
    myVec.push_back(itemSubB);
 
    symulation(myVec);
 
    return 0;
}

I tak słabym pomysłem jest tworzenie obiektu klasy BaseClass, żeby go zaraz przesłać do pochodnych.

0

No nie, przerobienie funkcji log() na wirtualna - nie o to co mi chodziło. Dla rozjaśnienia kontekstu: jak mamy w grze obiekt "statyczny" i tworzymy do niego dekorator "statyczny kręcący" to jednak chcemy mieć w Subclass coś w stylu RenderRotate(). Można nazwa funkcji była myląca, mniejsza o to.

Co do kodu, przerobiłem go zgodnie z sugestią w tym miejscu:

 
int main(int argc, char *argv[])
{
	BaseClass item("Bolek", "Lolek");

	auto itemSub = std::make_shared<SubClass>(&item);
	itemSub->setAdditionalValue(32);

	auto itemSubB = std::make_shared<SubClass>(&item);
	itemSubB->setAdditionalValue(44);

	std::vector<std::shared_ptr<BaseClass>> myVec;
	myVec.push_back(itemSub);
	myVec.push_back(itemSubB);

	symulation(myVec);

	return 0;
}

I poszło, dzięki wielkie. ;)

1

Zaraz, tworzysz obiekt automatyczny (lokalny, automatic lifetime) klasy bazowej i robisz z niego sprytny wskaźnik na klasę dziedziczącą? Czy tylko mi się wydaje, że coś tutaj jest zdrowo poplątane? WTF?

Możesz podrzucić aktualny kod?

1
MrMadMatt napisał(a):

No to jak inaczej twoim zdaniem umieszczać obiekty klasy pochodnej, w wektorze klasy bazowej tak aby przy rzutowaniu nie obcinało mi danych z klasy pochodnej?

przekazuj zwyły wskaźnik

nie ma ABSOLUTNIE potrzeby, żeby korzystać tutaj z shared_ptr

0

Jako że pojawiło się wyżej pytanie w stylu: a po co to komu, już śpieszę z wyjaśnieniem, może przy okazji ktoś mi wytknie jakiś kluczowy błąd.
Otóż, mam sytuacje w swojej grze w której to mam obiekt świata, który zawiera wektor wszystkich obiektów które się w nim znajdują (pola, lasy, góry), ale cześć z tych obiektów musi mieć specjalne uprawnienia np. (góra jest obiektem świata ale ma mieć opcje lawiny) tak więc w tym wektorze chce zapisywać dekoratory dla zwykłych obiektów. Po czym w głównej symulacji tylko sobie rzutować obiekt z wektoru na ten „rozszerzony”.

Jako że w projekcie rozwiązanie to jest dość spore, umieszczam poniżej uproszczony kod.

 
#include <iostream>
#include <string>
#include <vector>
#include <memory>

namespace ExampleA
{
	class GameObj
	{
	public:
		GameObj() {}

		GameObj(std::string name) :
			m_name(name)
		{

		}

		void render()
		{
			std::cout << "RenderObj" << this->m_name << "\n";
		}

	protected:
		std::string m_name;
	};


	class GameObjRotate : public GameObj
	{
	public:
		GameObjRotate() {}

		GameObjRotate(GameObj* baseItem) : GameObj(*baseItem)
		{
		}

		void setPoints(int pointsValue)
		{
			this->m_points = pointsValue;
		}

		void renderWithPointsOrOtherShit()
		{
			std::cout << "RenderObj, " << this->m_name << " points: " << this->m_points << "\n";;
		}

	private:
		int m_points;
	};

	class GameWorld
	{
	public:
		void createWorld()
		{
			GameObj item("Mietek");

			GameObjRotate itemB(&item);
			itemB.setPoints(230);

			m_gameObjList.push_back(item);
			m_gameObjList.push_back(itemB);
		}

		std::vector<GameObj> m_gameObjList;
	};


	void symulation(GameWorld& gameWorld)
	{
		GameObjRotate* ptr = (GameObjRotate*)(&gameWorld.m_gameObjList[1]);
		ptr->renderWithPointsOrOtherShit();
	}
}

namespace ExampleB
{
	class GameObj
	{
	public:
		GameObj(std::string name) :
			m_name(name)
		{

		}

		void render()
		{
			std::cout << "Render: " << this->m_name << "\n";
		}

	protected:
		std::string m_name;
	};

	class GameObjRotate : public GameObj
	{
	public:
		GameObjRotate(GameObj* baseItem) : GameObj(*baseItem)
		{
		}

		void setPoints(int pointsValue)
		{
			this->m_points = pointsValue;
		}

		void renderWithPointsOrOtherShit()
		{
			std::cout << "RenderObjB, " << this->m_name << " points: " << this->m_points << "\n";
		}

	private:
		int m_points;
	};

	class GameWorld
	{
	public:
		void create()
		{
			GameObj item("Zenon");

			auto itemB = std::make_shared<GameObjRotate>(new GameObjRotate(&item)); // GameObjRotate itemB(&item);
			itemB->setPoints(100);

			m_gameObjList.push_back(std::make_shared<GameObj>(item));
			m_gameObjList.push_back(itemB);

		}

		std::vector < std::shared_ptr<GameObj>> m_gameObjList;
	};

	void symulation(GameWorld& world)
	{
		GameObjRotate* ptr = (GameObjRotate*)world.m_gameObjList[1].get();
		ptr->renderWithPointsOrOtherShit();

	}
}


void main()
{
	/* Wadliwe stare rozwiazanie. */
	ExampleA::GameWorld world;
	world.createWorld();
	ExampleA::symulation(world);

	
	/* Nowe ktore chyba bedzie git. */
	ExampleB::GameWorld worldB;
	worldB.create();
	ExampleB::symulation(worldB);

}


jakby ktoś miał jakieś uwagi to chętnie przeczytam o ile faktycznie będą sensowne. ;)

Btw. Nawoływanie do porzucenia smart pointerów na rzecz surowych wskaźników – nawet tego nie skomentuje.

1
Btw. Nawoływanie do porzucenia smart pointerów na rzecz surowych wskaźników – nawet tego nie skomentuje.

ale powiedz mi dlaczego jest ci potrzebny AŻ shared_ptr?
W dodatku w kodzie który podałeś wcześniej ten vector nie powinien 'ownować' żadnych obiektów więc zwykły wskaźnik był najlepszym rozwiązaniem.
Teraz jest troche inna sprawa, ale shared_ptr to dalej overkill.

  1. pisze się simulation
0

Dlaczego nie zalecam używania surowych wskaźników? Ano, przykładowy powód brzmi:

  1. Mamy 2016 rok a panujący standard języka jest z 2011 roku, nie widzę absolutnie żadnej potrzeby aby utrzymywać przy życiu rozwiązania z lat siedemdziesiątych przy życiu które swoje źródło mają w języku C.
  2. Na youtubie jest taki świetny kanał jak CppCon, na którym jest masa filmów które lepiej niż ja wyjaśniają dlaczego nie powinno się używać char[], rzutowania w stylu C oraz wielu innych.
2

Phew, jesteś jednak mniej szalony niż myślałem. : )

Po kolei:

  • int, fucking, main. Czym to kompilujesz?
  • simulation! (no wyrwało mi się :P)
  • sprytne wskaźniki to nie jest jakaś magiczna złota strzała czy inna fontanna młodości. Dobrze użyte "non-owning raw pointers" są jak najbardziej wporządku. Nie bądź jednym z tych co to wypili Kool-Aid i nagle wszędzie używają shared_ptr. To już zakrawa o "cargo cult programming". :P
  • dlaczego nie zrobić po prostu wirtualnej metody render()?
  • aktualny standard C++ jest z 2014 roku, nie 2011. Następny będzie najpewniej w 2017.
  • CppCon jest świetne, dlatego nie mam pojęcia skąd Ci się wzięła Twoja postawa. Właśnie na CppCon usłyszysz, że jak najbardziej nie powinno się "ślepo" używać sprytnych wskaźników i że jak najbardziej "surowe" wskaźniki mają swoje miejsce.
0
  1. Kompiluje sobie kompilatorem Microsoftowym. : )
  2. Serio? Literka?
  3. No nie, surowe wskaźniki to zło konieczne. To tak samo jak ze wzorcami projektowymi, że niby odpowiednio pisana apka ich nie potrzebuje. Mhm, nie kupuje tego.
  4. A po co mam robić wirtualną funkcje render() skoro zależy mi na tym aby wywoływać inną funkcję która nie może być wirtualna? "OrOtherShit" zbyt mało wymowne?
  5. Mój błąd aczkolwiek C++11 jest must use więc do niego się odwołałem.
  6. CppCon - właśnie stamtąd wziąłem sobie pogląd że jak mamy standard to piszemy tak aby być na bieżąco ze standardem.
8

Dlaczego nie zalecam
Patrząc po zamieszczonym kodzie i zrozumieniu tego co tam się dzieje, na Twoim miejscu mniej bym zalecał, a bardziej brał sobie zalecenia bardziej doświadczonych ludzi do serca. Na razie nauczyłeś się, że istnieje młotek i wszystko zamieniasz na gwoździe.

Podstawowym problemem nagich wskaźników w C i w (teraz) nieidiomatycznym C++ nie jest ich nagość, tylko fakt, że nie wiadomo czy są one wyłącznie obserwatorami, czy też kontrolują czas życia obiektu. Jeśli nagie wskaźniki traktujesz wyłącznie jako obserwatory to jest to jak najbardziej poprawne. C++≈20 prawdopodobnie będzie miał std::observer_ptr oferujący praktycznie identyczną funkcjonalność. Teraz to możesz sobie zasymulować pisząc template<typename T> using observer_ptr = T*;, albo template<typename T> using observer_ptr = std::add_pointer_t<T>; jeśli chcesz zaszpanować.

Co do samego shared_ptr, polecam obejrzeć tę prelekcję. Zauważ, że shared_ptr jest ostatecznością, a nie pierwszym zalecanym rozwiązaniem.

Ponadto polecam zajrzeć do C++ Core Guidelines:

  1. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#i11-never-transfer-ownership-by-a-raw-pointer-t
  2. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#r3-a-raw-pointer-a-t-is-non-owning
  3. https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#r30-take-smart-pointers-as-parameters-only-to-explicitly-express-lifetime-semantics
3

Ty kompletnie nie wiesz co robisz.

auto itemB = std::make_shared<GameObjRotate>(new GameObjRotate(&item)); // GameObjRotate itemB(&item);

Memory leak i debilizm w jednym.

GameObjRotate* ptr = (GameObjRotate*)world.m_gameObjList[1].get();
ptr->renderWithPointsOrOtherShit();

Kretynizm numer 2. Skoro tak po prostu castujesz (w stylu C, o którym gdzieś tu pisałeś, że nie powinno się go używać), to dlaczego m_gameObjList nie jest typu std::vector<std::shared_ptr<GameObjRotate>> ? Jeśli w vectorze będzie wskaźnik na obiekt innej klasy, to już się wszystko wysypie.

EDIT:
Zresztą, jest wskaźnik na obiekt, który nie obiektem klasy GameObjRotate a jest dodawany do vectora tutaj:

GameObj item("Mietek"); 
m_gameObjList.push_back(item);

To czego Ci trzeba, to są właśnie metody wirtualne a nie jakieś chore rozwiązania, które się wykrzaczą lada chwila.

1

Całą swoją istotą popieram to co kq napisał. Ba, gdyby były wybory prezydenckie to bym na niego zagłosował. ; )

1 & 6. To weź powiedz temu Microsoftowemu kompilatorowi, że od C++98 main ma zwracać int. :P Cytując klasyka: "Jak mamy standard, to piszemy tak, aby być na bieżąco ze standardem." Wypadałoby, bo jesteś jakieś 18 lat do tyłu w tak podstawowej kwestii.

0

kq Czy to nie Ty przypadkiem w pierwszej, mało pomocnej wypowiedzi zresztą, uznałeś że mój patent jest kompletnie bez sensu? Ciężko brać na poważnie słowa człowieka który wydaje się mieć, na celu tylko i wyłącznie, pokazanie na każdym kroku swojej wyższości intelektualnej.

Co do mwl4. W kodzie swoim "produkcyjnym" nie używam raw pointer'ów. A jak Cię tak to zabolało cóż... Wytłumaczę się swoim lenistwem do pisania static_casta. A co do memory leaka, być może ale w dalszym ciągu nie zaproponowałeś poprawki do tego jak powinno być bez niego, więc twój komentarz do wycieku pamięci też trudno uznać za pomocny, i co więcej można z pełną stanowczością uznać za zbędny.

0

20.11.2.2.6 shared_ptr creation [util.smartptr.shared.create]
template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args);

1 Requires: The expression ::new (pv) T(std::forward<Args>(args)...), where pv has type void*
and points to storage suitable to hold an object of type T, shall be well formed. A shall be an
allocator (17.6.3.5). The copy constructor and destructor of A shall not throw exceptions.
2 Effects: Allocates memory suitable for an object of type T and constructs an object in that memory
via the placement new-expression ::new (pv) T(std::forward<Args>(args)...).

Jesteś specjalistą, więc samo podświetlenie powinno Cię naprowadzić dlaczego masz w swoim kodzie wyciek.

edit: Za sucho? To zapytaj się - kto woła delete w odpowiedzi na Twoje nagie new?

1

Przepraszam bardzo, spodziewałem się, że ktoś ze stażem na forum będzie rozróżniał dział Newbie od C i C++, więc dostosowałem odpowiedź do spodziewanego poziomu odbiorcy. Cytując klasyka, najwyraźniej Pana przeszacowałem, panie MadMatt. A patent jest bez sensu, jak zresztą zauważyli kolejni użytkownicy piszący w temacie.

Powtórzę to co napisałem w pierwszym poście, gdzie ująłem sedno sprawy: shared_ptr przejmuje własność przekazanego wskaźnika, a Ty przekazujesz adres zmiennej o automatycznym czasie życia. W efekcie tego obiekt ten jest niszczony dwa razy: w destruktorze ostatniego shared_ptr wskazującego na niego oraz w } - przy końcu zakresu. Inaczej mówiąc masz UB, co naprawdę nie ma sensu.

0

Xupior, znajdź w mojej wypowiedzi auto-stwierdzenie że jestem specjalista. Wyraziłem tylko swoje zdanie że osobiście uważam używanie surowych wskaźników za zło i nikomu tego nie polecam, widziałem sprytny kod ze sprytnym "odpowiednim" ich użyciem i więcej nie chce z czymś takim pracować. Od tego jest forum, prawda? Od wyrażania opinii.

W zamian za własne zdanie, skoczyła na mnie "stara gwardia" forum, cóż, dość powszechne zjawisko w internecie: kółeczka wzajemnej adoracji i oburzenia jak ktoś nowy na forum wyraża nieskrępowanie swoje opinie w konfrontacji ze stałych użytkowników.

No cóż, trochę przykre zjawisko, szczególne że forum dla programistów-intelektualistów. Cóż mi rzecz. Dzięki za komentarze. ;)

0

Nie rozumiesz chyba po co istnieją metody wirtualne.

#include <iostream>
#include <string>
#include <vector>
#include <memory>
 
namespace ExampleB
{
    class GameObj
    {
    public:
        GameObj(std::string name) :
            m_name(name)
        {
 
        }
 
        virtual void render()
        {
            std::cout << "Render: " << this->m_name << "\n";
        }
 
    protected:
        std::string m_name;
    };
 
    class GameObjRotate : public GameObj
    {
    public:
        GameObjRotate(const GameObj &baseItem) : GameObj(baseItem)
        {
        }
 
        void setPoints(int pointsValue)
        {
            this->m_points = pointsValue;
        }

        void renderRotate()
        {
            std::cout << "RenderObjB, " << this->m_name << " points: " << this->m_points << "\n";
        }

        virtual void render() override
        {
            renderRotate();
        }

    private:
        int m_points;
    };
 
    class GameWorld
    {
    public:
        void create()
        {
            GameObj item("Zenon");
 
            auto itemB = std::make_shared<GameObjRotate>(item);
            itemB->setPoints(100);
            m_gameObjList.push_back(itemB);

            auto itemC = std::make_shared<GameObjRotate>(item);
            itemC->setPoints(250);
            m_gameObjList.push_back(itemC);
        }
 
        std::vector<std::shared_ptr<GameObj>> m_gameObjList;
    };
 
    void simulation(GameWorld &world)
    {
        for(const auto &obj : world.m_gameObjList)
        {
            obj->render();
        }
    }
}
 
int main()
{ 
    ExampleB::GameWorld worldB;
    worldB.create();
    ExampleB::simulation(worldB);
}

1
MrMadMatt napisał(a):

Dlaczego nie zalecam używania surowych wskaźników?

MrMadMatt napisał(a):
  1. No nie, surowe wskaźniki to zło konieczne. To tak samo jak ze wzorcami projektowymi, że niby odpowiednio pisana apka ich nie potrzebuje. Mhm, nie kupuje tego.
  2. A po co mam robić wirtualną funkcje render() skoro zależy mi na tym aby wywoływać inną funkcję która nie może być wirtualna? "OrOtherShit" zbyt mało wymowne?

Wyrażasz się jak ekspert, który pisze kod już z co najmniej 20 lat.

0

OP: Jak już się zastanowisz kto zawoła delete na Twoje nagie new w tym kodzie ostatnim, to może podrzucił byś jakiś przykład, który by dobitniej ilustrował dlaczego wirtualna metoda to nieodpowiednie dla Ciebie rozwiązanie?

0

mwl. Mhm, i jak w dekoratorze do GameObj będziesz miał 10 nowych funkcji to też je wsadzisz w jedną nadpisaną bo się niezmiernie uparłeś na metody wirtualne w dodatku zaśmiecając funkcjami abstrakcyjnymi klasę GameObj byleby tylko w klasach pochodnych je nadpisywać? No nie, nie o takie coś mi chodziło w głównym temacie.

Co do twojego sposobu odbierania moich wypowiedzi, nie jest tak że każdy ocenia innych swoją miarą?

Xupicior, ano dlatego.

 
#include <iostream>
#include <string>
#include <vector>
#include <memory>

namespace ExampleB
{
	class GameObj
	{
	public:
		GameObj(std::string name) :
			m_name(name)
		{

		}

		void render()
		{
			std::cout << "Render: " << this->m_name << "\n";
		}

	protected:
		std::string m_name;
	};

	class GameObjChmura : public GameObj
	{
	public:
		GameObjChmura(GameObj* baseItem) : GameObj(*baseItem)
		{
		}

		void setIntensywnoscOpadow(int pointsValue)
		{
			this->m_points = pointsValue;
		}

		/* GameObj może być drzewo, las, trawa, czy z trawy pada deszcz?
			Nie, wiec potrzebujemy metody niezaleznej zupelnie od GameObj.
			Poza tym nie zaśmieca się abstrakcji klasy w tym przypadku GameObj*/
		void padaDeszcz()
		{
			std::cout << "Pada deszcz";
		}


	private:
		int m_points;
	};

	class GameWorld
	{
	public:
		void create()
		{
			GameObj item("Game obj");

			auto itemB = std::make_shared<GameObjChmura>(new GameObjChmura(&item)); 
			itemB->setIntensywnoscOpadow(100);

			m_gameObjList.push_back(std::make_shared<GameObj>(item));
			m_gameObjList.push_back(itemB);

		}

		/* Jak kogos boli ten nagi new */
		~GameWorld()
		{
			for (std::shared_ptr<GameObj>& item : this->m_gameObjList) {
				item.reset();
			}
			m_gameObjList.clear();
		}

		std::vector < std::shared_ptr<GameObj>> m_gameObjList;
	};

	void work(GameWorld& world)
	{
		/* Lenistwa ciag dalszy - alert! */
		GameObjChmura* ptr = (GameObjChmura*)world.m_gameObjList[1].get();
		ptr->padaDeszcz();

	}
}


void main()
{
	/* Nowe ktore chyba bedzie git. */
	ExampleB::GameWorld worldB;
	worldB.create();
	ExampleB::work(worldB);

}


0

To, że ręcznie zrobiłeś to co normalnie RAII zrobi za ciebie nie znaczy, że już nie masz memleaka.

Kompletnie nie rozumiesz w jaki sposób działa std::make_shared
polecam http://en.cppreference.com/w/cpp/memory/shared_ptr/make_shared

0

Ale rozumiesz, że z metody wirtualnej da się wywołać metodę niewirtualną?

#include <iostream>
#include <string>
#include <vector>
#include <memory>
 
namespace ExampleB
{
    class GameObj
    {
    public:
        GameObj(std::string name) :
            m_name(name)
        {
 
        }
 
        virtual void render()
        {
            std::cout << "Render: " << this->m_name << "\n";
        }
 
    protected:
        std::string m_name;
    };
 
    class GameObjChmura : public GameObj
    {
    public:
        GameObjChmura(GameObj *baseItem) : GameObj(*baseItem)
        {
        }
 
        void setIntensywnoscOpadow(int pointsValue)
        {
            this->m_points = pointsValue;
        }

        virtual void render() override
        {
            padaDeszcz();
        }
 
        /* GameObj może być drzewo, las, trawa, czy z trawy pada deszcz?
            Nie, wiec potrzebujemy metody niezaleznej zupelnie od GameObj.
            Poza tym nie zaśmieca się abstrakcji klasy w tym przypadku GameObj*/
        void padaDeszcz()
        {
            std::cout << "Pada deszcz" << std::endl;
        }
 
 
    private:
        int m_points;
    };

    class GameObjTrawa : public GameObj
    {
    public:
        GameObjTrawa(const GameObj &obj) : GameObj(obj)
        {
        }

        void setHeight(int h) { m_h = h; }

        virtual void render() override
        {
            renderTrawa();
        }

        void renderTrawa()
        {
            std::cout << "Render trawa, wysokosc: " << m_h << std::endl;
        }
    private:
        int m_h;
    };
 
    class GameWorld
    {
    public:
        void create()
        {
            GameObj item("Game obj");
            auto itemB = std::make_shared<GameObjChmura>(&item); 
            itemB->setIntensywnoscOpadow(100);
            m_gameObjList.push_back(itemB);

            auto itemC = std::make_shared<GameObjTrawa>(GameObj{"Trawa"}); 
            itemC->setHeight(200);
            m_gameObjList.push_back(itemC);
        }

        std::vector < std::shared_ptr<GameObj>> m_gameObjList;
    };
 
    void work(GameWorld& world)
    {
        for(const auto &obj : world.m_gameObjList)
        {
            obj->render();
        } 
    }
}
 
 
int main()
{
    ExampleB::GameWorld worldB;
    worldB.create();
    ExampleB::work(worldB);
}
0

mlw A rozumiesz że nie chce w jednej metodzie wirtualnej zamieszczać wywołań do np. 20 innych metod bo mi to najzwyczajniej w świecie niszczy abstrakcję klasy? Bo jak się ma taki render() do padaDeszcz() albo wywolajLawine(), a co jak chce renderowac bez ktores z funckji? Mam w argumentach render() dac 10 parametrow ktore beda mi sterowac poszczególnymi wywołaniami? Co jak takich funkcji będzie 10 albo 20? Hm?

No nie, tak się nie programuje. Proste polecenie, dodaj do swojego rozwiązania opcje wyłączenia deszczu.

Edit:
Co do tego nieszczęsnego memory leak'a. Dekonstruktor wirtualny zalatwil mi sprawę, to tak dla kogoś kto by czytał temat a był zainteresowany wytykanym ale nie sprecyzowanym wyciekiem pamieci.

0

@MrMadMatt wpadłeś w wir błędnych założeń. Nie wiesz jak działa konstrukcja obiektów wirtualnych. No i denerwujesz moderatorów a nie nazywasz się gośćabc.

W świecie programowania nie wolno Ci używać tez bezwzględnych jeżeli nie masz 100% pewności, że masz rację, oducz się tego bo te forumowe hieny Ci nie dadzą żyć.

2

Co do wycieku pamięci... W którym momencie Ci to załatwi jakikolwiek destruktor? Ten nowy kod niczego nie zmienia. make_shared bierze swoje argumenty i przekazuje je do konstruktora T, tak? Jeśli masz T(T*), to super, możesz zrobić make_shared(new T); tylko wtedy wypadałoby gdzieś zrobić delete - a skoro do wskaźnika będącego wynikiem new T nie masz dostępu...

Po kolei:

auto itemB = std::make_shared<GameObjChmura>(new GameObjChmura(&item));
                                             ^^^^^^^^^^^^^^^^^^^^^^^^

W make_shared wynik tego wyrażenia zostanie przekazany do konstruktora GameObjChmura.

GameObjChmura(GameObj* baseItem) : GameObj(*baseItem) { }
                                           ^^^^^^^^^

Tutaj robisz dereferencję i odpalasz konstruktor kopiujący GameObj. W konstruktorze kopiującym oczywiście nie bierzesz adresu i nie zwalniasz tej pamięci, bo to by było kompletnie poronione, ale nie zwalniasz też tej pamięci w powyższym konstruktorze (co też pewnie nie byłoby najlepszym pomysłem), a kiedy się on kończy - wskaźnik jest już nieosiągalny.
No i mamy wyciek.

W ogóle przemyślałbym ten konstruktor ze wskaźnikiem...

Kompilacja Twojego nowego kodu pod GCC:

leak.cpp:88:11: error: ‘::main’ must return ‘int’
 void main()
           ^

Po naprawieniu powyższego "łyka", ale valgrind pokazuje:

==845== Memcheck, a memory error detector
==845== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==845== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==845== Command: ./a.out --leak-check=full -v
==845== 
==845== 
==845== HEAP SUMMARY:
==845==     in use at exit: 49 bytes in 2 blocks
==845==   total heap usage: 6 allocs, 4 frees, 153 bytes allocated
==845== 
==845== LEAK SUMMARY:
==845==    definitely lost: 16 bytes in 1 blocks
==845==    indirectly lost: 33 bytes in 1 blocks
==845==      possibly lost: 0 bytes in 0 blocks
==845==    still reachable: 0 bytes in 0 blocks
==845==         suppressed: 0 bytes in 0 blocks
==845== Rerun with --leak-check=full to see details of leaked memory
==845== 
==845== For counts of detected and suppressed errors, rerun with: -v
==845== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Wyciek jak byk. Po usunięciu tego nagiego new - czary magia - wyciek zniknął.

więc twój komentarz do wycieku pamięci też trudno uznać za pomocny, i co więcej można z pełną stanowczością uznać za zbędny.

Pełna stanowczość < wiedza i test. mwl4 miał jak najbardziej rację odnośnie wycieku.

Co do reszty - ciągle nie bardzo rozumiem czemu nie wywołasz sobie swoich specifycznych dla klasy pochodnej metod w wirtualnej simulate/render czy co tam potrzebujesz. Bazowej nie zaśmiecasz, pochodne implementują jedną wirtualną i mogą mieć ile tam chcą specyficznych dla siebie metod, robią co trzeba.

Powiedzmy, że masz kontener sprytnych wskaźników, które trzymają wszystkie Twoje obiekty - na razie to wygląda tak, że chcesz każdy z tych wskaźników rzutować na odpowiedni wskaźnik do pochodnej i wtedy wywoływać niewirtualne metody...

0

Xupicor
"mwl4 miał jak najbardziej rację odnośnie wycieku."

No i co z tego jak nie raczył wyjaśnić dlaczego, skąd i jak? "Jesteś w błędzie ale nie powiem Ci gdzie - heheszki". No cóż napisać? Jeżeli myliłem się z de-konstruktorem i tym new, cóż, nie można było napisać od razu? Swoje zdanie na temat kultury forum zamieściłem w którejś ze swoich poprzednich wypowiedzi.

"Powiedzmy, że masz kontener sprytnych wskaźników, które trzymają wszystkie Twoje obiekty - na razie to wygląda tak, że chcesz każdy z tych wskaźników rzutować na odpowiedni wskaźnik do pochodnej i wtedy wywoływać niewirtualne metody.."

No bo tak to ma wyglądać.

2
Xupicor napisał(a):
  • simulation! (no wyrwało mi się :P)
MrMadMatt napisał(a):
  1. Serio? Literka?

Serio. Bo jak się źle nauczysz i napiszesz tak w czymś co pójdzie w większy nakład to będzie wtopa.

Kilkaset osób z całej Europy posiada pewien „certyfikat” na którym jest napisane wielkimi literami CERTYFICATE zamiast CERTIFICATE. Bo mi się źle napisało a zanim ktoś poprawił to trochę tego poszło... ;D

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