C++ - niezrozumiała asercja.

C++ - niezrozumiała asercja.
MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
0

Witam.

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

Kopiuj
 
#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.

edytowany 1x, ostatnio: MrMadMatt
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:3 minuty
  • Lokalizacja:Szczecin
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.


MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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?

mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:23 dni
  • Lokalizacja:Wrocław
  • Postów:399
0

Tak:

Kopiuj
#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.


Asm/C/C++
edytowany 1x, ostatnio: mwl4
MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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:

Kopiuj
 
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. ;)

Xupicor
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
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?


edytowany 1x, ostatnio: Xupicor
xfin
O to to! ;) Plus.
Xupicor
Ciekaw jestem co on tam narobił - bo coś mi się zdaje, że w klasie bazowej dodał konstruktor przyjmujący wskaźnik na bazową i być może nawet go ignoruje (albo go ignoruje jeszcze w konstruktorze pochodnej) - a w konsekwencji ma obiekt klasy bazowej na stosie i osobny obiekt klasy dziedziczącej zarządzany sprytnym wskaźnikiem. W każdym razie to jakieś jedno wielkie szaleństwo. :P
Sopelek
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 8 lat
  • Lokalizacja:Kraków
  • Postów:467
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

edytowany 1x, ostatnio: Sopelek
MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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.

Kopiuj
 
#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.

Sopelek
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 8 lat
  • Lokalizacja:Kraków
  • Postów:467
1
Kopiuj
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
MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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.
edytowany 1x, ostatnio: MrMadMatt
Xupicor
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
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.

MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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.
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:3 minuty
  • Lokalizacja:Szczecin
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

Xupicor
Gotta love Herb. Dobre linki.
Sopelek
Dobrze, że ktoś to mu podlinkował wreszcie, jeden post mniej będzie.
mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:23 dni
  • Lokalizacja:Wrocław
  • Postów:399
3

Ty kompletnie nie wiesz co robisz.

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

Memory leak i debilizm w jednym.

Kopiuj
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:

Kopiuj
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.


Asm/C/C++
edytowany 2x, ostatnio: mwl4
Xupicor
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
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.


MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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.

mwl4
Podałem Ci rozwiązanie w pierwszym moim poście, ale ty uznałeś to za oczywiście brzydkie.
Sopelek
Aż strach pomyśleć, że ty piszesz kod produkcyjny.
Xupicor
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
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?


edytowany 1x, ostatnio: Xupicor
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:3 minuty
  • Lokalizacja:Szczecin
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.


edytowany 2x, ostatnio: kq
MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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. ;)

kq
Czyli twierdzisz, że się nie znasz, ale mimo wszystko bierzesz się za udzielanie rad w tej dziedzinie?
mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:23 dni
  • Lokalizacja:Wrocław
  • Postów:399
0

Nie rozumiesz chyba po co istnieją metody wirtualne.

Kopiuj
#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);
}


Asm/C/C++
mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:23 dni
  • Lokalizacja:Wrocław
  • Postów:399
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.


Asm/C/C++
Sopelek
Może pisze? ;D
Xupicor
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 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?


MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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.

Kopiuj
 
#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);

}


Sopelek
  • Rejestracja:prawie 13 lat
  • Ostatnio:ponad 8 lat
  • Lokalizacja:Kraków
  • Postów:467
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

edytowany 3x, ostatnio: Sopelek
mwl4
  • Rejestracja:około 12 lat
  • Ostatnio:23 dni
  • Lokalizacja:Wrocław
  • Postów:399
0

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

Kopiuj
#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);
}

Asm/C/C++
MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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.

edytowany 2x, ostatnio: MrMadMatt
spartanPAGE
To zrób sobie jakiś render_interface? Szczerze mi, jako osoby z boku ciężko określić co właściwie potrzebujesz; i co dziwniejsze starasz się bronić całej tej niejasności. Opisz dokładnie co próbujesz uzyskać zamiast warczeć na każdym kroku :I
gośćabc
  • Rejestracja:około 11 lat
  • Ostatnio:ponad 3 lata
  • Lokalizacja:Szczecin
  • Postów:500
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ć.

Xupicor
Oj, nie damy. Znaczy, nie dadzą!
Xupicor
  • Rejestracja:ponad 16 lat
  • Ostatnio:ponad 8 lat
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:

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

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

Kopiuj
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:

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

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

Kopiuj
==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...


edytowany 1x, ostatnio: kq
kq
Że też Ci się chciało... PS: jak nie rozumiesz jego kodu to z pełną stanowczością można uznać, że jesteś za głupi aby go zrozumieć, to chyba jasne?
Xupicor
Jak supernowa.
MrMadMatt
  • Rejestracja:ponad 9 lat
  • Ostatnio:dzień
  • Postów:373
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ć.

Xupicor
"...dlaczego, skąd i jak? " - wskazał Ci konkretną linijkę i powiedział, że tam jest leak. Ja poszedłem o te parę kroków dalej - ale chyba nie oczekujesz, że każdy będzie się aż tak gimnastykował? "No bo tak to ma wyglądać" No to sobie tak zrób. A rok później przerobisz. I przynajmniej będzie co robić. ;)
Azarien
  • Rejestracja:ponad 21 lat
  • Ostatnio:około 2 godziny
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

Zobacz pozostały 1 komentarz
Azarien
@kq: jakiego referera? o czym ty mówisz?
Azarien
ja wiem co to jest HTTP referer, ale jak to się ma do mojego postu?
kq
Napisałeś posta o tym jak istotna jest poprawna pisownia, bo czasem może być za późno na jej zmianę i trzeba żyć z błędną. Dałem chyba najpopularniejszy tego przykład.

Zarejestruj się i dołącz do największej społeczności programistów w Polsce.

Otrzymaj wsparcie, dziel się wiedzą i rozwijaj swoje umiejętności z najlepszymi.