• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Alokacja pamięci dynamicznej obiektowo (C++)

Object Storage Arubacloud
0 głosów
561 wizyt
pytanie zadane 21 września 2018 w C i C++ przez Sic Dyskutant (8,510 p.)
edycja 21 września 2018 przez Sic

Witam.

Napisałem krótki program w ramach ćwiczeń z alokacją pamięci. Program się skompilował, jednak posiada błędy których już nie wiem jak rozwiązać.

Oto kod:

#include <iostream>


using namespace std;
class Name
{
        private:
                int * va;
        public:
                Name(int * st);
                ~Name();
        friend ostream & operator<<(ostream &, const Name &);
        friend int operator*(const Name &);

};

Name::Name(int * st)
{
        va = new int;
        va = st;
}

Name::~Name() {delete va;}

ostream & operator<<(ostream & os, const Name & tak)
{
        os << tak;
        return os;
}

int operator*(const Name & ps)
{
        std::cout << *ps.va << std::endl;
        return * ps.va;
}
int main()
{
        int war = 5;
        int * ob = &war;
        Name vi(ob);
        cout << vi << "\n" << *vi;
        return 0;
}

Problem polega na błędzie w przeciążeniu operatora*.

std::cout << *ps.va << std::endl;

w tym momencie wyświetla wartość, a poźniej pokazuje "Segmentation fault (core dumped)".

std::cout << ps.va << std::endl;

W takim wariancie pokazuje adres, a poźniej "Segmentation fault (core dumped)".

Mam jeszcze dodatkowe pytania co do alokacji pamięci:

1. Czy jest możliwość skrócenia (optymalizacji) tego kodu ?

2. W jaki sposób mogę wykorzystać "va" jako zmienną i przydzielić do niej pamięć, zamiast "*va" ?

3. Czy mógłbym uzyskać jakieś źródło przedstawiające alokacje pamięci obiektowo, abym mógł się douczyć ?

4. Istnieje opcja alokacji pamięci na obiekt klasy ?

komentarz 21 września 2018 przez j23 Mędrzec (194,920 p.)
Name::Name(int * st)

Jak chcesz przekazać w parametrze jednego inta, to nie ma sensu przekazywać go przez wskaźnik czy referencję.

1 odpowiedź

+1 głos
odpowiedź 21 września 2018 przez RafalS VIP (122,820 p.)
wybrane 22 września 2018 przez Sic
 
Najlepsza
Name::Name(int * st)
{
    va = new int;
    va = st;
}

To jest czysty nonsens :P. Alokujesz pamięć, new zwraca Ci adres tej pamięci a potem zapominasz ten adres nadpisując go adresem podanym w argumencie.

int main()
{
	int war = 5;
	int * ob = &war;
	Name vi(ob);

Tutaj pod va ląduje adres inta zaalokowanego automatycznie na stosie, który o tutaj:

	return 0;

powoduje wywołanie destruktora:

Name::~Name() { delete va; }

który próbuje zrobić coś takiego:

int x;
int *ptr = &x;
delete ptr;

Pamięci alokowanej automatycznie na stosie nie można zwalniać na zawołanie przy pomocy delete.

Generalnie w C++ są dwa rodzaje zarządzania pamięcią (jej przydzielania i zwalniania): automatyczna i dynamiczna. Automatyczna alokacja (na stosie) rezerwuje Ci pamięć na jakąś zmienną i zwalnia tą pamięć po wyjściu z zakresu, np z funkcji, pętli czy jakiegokolwiek bloku:

	int *ptr;
	{
		int x = 10; //alokowane automatycznie na stosie
		ptr = &x;
	}
	//w tym miejscu pamiec zaalokowana na zmienna x zostala zwolniona
	//nie jest juz nasza, nie mozemy z niej korzystac
	//posiadamy jej adres, ale próby odczytu/zapisu powoduja
	//niezdefionowane zachowanie (czasem zadziałaja, czasem wywala program)
	cout << *ptr << endl;

Pamięć została zwolniona automatycznie na podstawie zakresu. Zmienna wyszła z zakresu więc pamięć zostaje zwolniona. Z takiej alokacji korzystasz cały czas. Ale czasem przychodzi potrzeba ręcznego decydowania kiedy pamięć ma być zwolniona. Jest to fajne, bo mamy większą kontrole, ale jednocześnie możemy spowodować wycieki pamięci:

	int *ptr;
	{
		int *dynamiczny_int = new int;
		*dynamiczny_int = 5;
		ptr = dynamiczny_int;
	}
	//zmienna lokalna dynamiczny int zostaje zwolniona
	//ale pamiec na ktora wskazuje nie
	//dlatego ponizszy kod jest jak najbardziej ok
	cout << *ptr << endl;

	delete ptr;
	//musimy pamietac o recznym zwolnieniu pamieci

Po co alokować dynamicznie? Po pierwsze - często jest potrzeba na stworzenie jakiegoś obiektu w funkcji, który nie zniknie po wyjsciu z niej. Po drugie stos jest dosyć mały. Rzędu wielkości 5MB. Na stercie (dynamicznie) możesz zaalokować nawet kilka GB. 
Przykład wycieku pamięci:

	for (int i = 0; i < 200000; i++) {
		double *x = new double[1000];
	}
	int x;
	cin >> x;

ten prosty kod zajmował 2GB pamięci.

1. Niech on najpierw zadziała. Potem będziesz myślał nad optymalizacją.

2. Nie rozumiem :D

3. Google "dynamiczna alokacja c++".

4. Jasne:

class A{};
int main(){
   A* = new A();
}

PS napisałem ostatnio coś o zwalnianiu pamięci. Może Ci się przyda: https://forum.pasja-informatyki.pl/380556/zwalnianie-pamieci-delete#a381318

komentarz 22 września 2018 przez Sic Dyskutant (8,510 p.)

Wprowadziłem małe zmiany, niestety dalej jestem w punkcie wyjścia.

#include <iostream>


using namespace std;
class Name
{       
        private:
                int * va;
        public: 
                Name();
                Name(int *);
                ~Name();
        Name & operator=(const Name &);
        friend ostream & operator<<(ostream &, const Name &);
        friend int operator*(const Name &);
  
};

Name::Name() { va = 0; }

Name::Name(int * st) { va = st; st = new int; }

Name::~Name() {delete va;}

ostream & operator<<(ostream & os, const Name & tak)
{
        os << tak;
        return os;
}

int operator*(const Name & ps)
{
        std::cout << *ps.va << std::endl;
        return * ps.va;
}

int main()
{
        int war = 5;
        int * ob = &war;
        Name vi(ob);
        cout << vi << "\n" << *vi;
        return 0;
}

Nie mogę pojąć samej alokacji sprawiłem, że pamięć została przypisana do va.

W funkcji main natomiast utworzyłem zmienną, która jest automatyczna(to w jaki sposób mam spradzić aby weszła na miejsce argumentu konstruktora ?)

komentarz 22 września 2018 przez j23 Mędrzec (194,920 p.)
edycja 22 września 2018 przez j23

Nie rozumiem twojego toku rozumowania. Najpierw przypisujesz wartość argumentu st do va, a później do st przypisujesz adres nowej pamięci. Gdzie tu logika?

Name::Name(int * st) { va = new int; *va = *st; }

 

W linii 27 masz niekończącą się rekurencję (operator <<, który wywołuje sam siebie).

komentarz 22 września 2018 przez Sic Dyskutant (8,510 p.)

Dziękuję bardzo działa jak należy.

Mam pytanie co do tej linii ( z tym miałem największy problem), jak ona działa:

Name::Name(int * st) { va = new int; *va = *st; }

Po zakończeniu popraw, chciałbym wrócić do pierwszego pytania, mianowicie optymalizacji kodu.

komentarz 22 września 2018 przez j23 Mędrzec (194,920 p.)
  • przydzielana jest pamięć o wielkości sizeof(int), adres przypisywany jest do wskaźnika va.
  • wartość spod adresu st jest przypisywana/kopiowana do pamięci wskazywanej przez va.

Co do optymalizacji. Tu w zasadzie nie ma czego optymalizować biorąc pod uwagę, że ćwiczysz sobie operowanie na pamięci dynamicznej (normalnie pole va powinno być zwykłą zmienną typu int). 

W innym wątku pisałem Ci, że taka klasa powinna mieć zdefiniowany ctor kopiujący i operator przypisania (Rule of three).

komentarz 22 września 2018 przez Sic Dyskutant (8,510 p.)
Dziękuję.

Tak wiem, zapoznałem się z tą regułą, jednak staram się zrozumieć wszystko po kolei.

Na razie zrozumiałem pisząc ten program, że każde działanie które chciałem wykonań za pomocą operatorów musiałem przeciążyć.
komentarz 22 września 2018 przez RafalS VIP (122,820 p.)
Ogólnie widzę, że w większości pytań które zadajesz wykorzystujesz przeciążanie operatorów, które nie są aż takie ważne jak być możę myślisz. Ponadto w wielu guidelines'ach są one raczej odradzane, bo o ile znaczenie takiego operatora dla danej klasy nie jest uderzająco oczywiste to wprowadza on sporo nieczytelności do kodu, a przypadków gdzie jest ono na tyle uderzająco oczywiste żeby użyć przeciążenia operatora zamiast nazwanej funkcji jest bardzo mało.
komentarz 23 września 2018 przez Sic Dyskutant (8,510 p.)
Nauczę się ich tworzenia i wykorzystania, mimo to istnieje możliwość ominięcia ich ?

Podobne pytania

0 głosów
1 odpowiedź 247 wizyt
0 głosów
1 odpowiedź 273 wizyt
pytanie zadane 7 kwietnia 2023 w C i C++ przez Zuzan Początkujący (390 p.)
0 głosów
1 odpowiedź 425 wizyt
pytanie zadane 7 maja 2020 w C i C++ przez Hubertius Bywalec (2,970 p.)

92,538 zapytań

141,377 odpowiedzi

319,456 komentarzy

61,923 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...