Destruktor - samoczynne odpalanie

Destruktor - samoczynne odpalanie
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

No dobrze ale jeśli funkcja zwraca referencję i jeśli ten zwracany obiekt jest przepisywany (odpalając mi przeładowany operator = lub konstruktor kopiujący a załóżmy że obie te rzeczy dokonują kopii obiektu) to może szybciej się to skopiuje niż zostanie zniszczone? Co się szybciej wykona, przepisanie tej referencji do czegoś czy może zniszczenie tego obiektu?

kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
0
Kopiuj
foo& make_foo()
{
    foo f;
    return f;
}

Użycie wartości zwróconą przez tę funkcję to UB.


Adamos19
Ok, kumam. Dzięki. Nie ma pytań!
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Mam jednak takie zadanko żeby przeładować operator * tak aby zwracał nową ale taką samą listę. Więc logicznym jest że aby mógł to zrobić to wewnątrz tej operatorowej funkcji musi być instrukcja która tworzy słówkiem "new" nową taką listę (z wykorzystaniem np. konstruktora kopiującego).
Aby jednak taka lista (zwrócona z funkcji przez referencję) mogła być użyta muszę ją gdzieś przepisać, i tutaj pojawia się pewien problem. Mam przeładowane operatory (= oraz napisany konstruktor i destruktor zgodnie z zasadą rule of three) jednakże mam to zrobione w taki sposób że konstruktor kopiujący jak i przeładowany operator = tworzą znowu kopie tego no więc jak to przepisuję (tą referencję zwróconą przez funkcję) to robi mi się kolejna kopia a tamta którą zrobiła se funkcja nie ginie i mam wyciek.
Kod:

Kopiuj
    CLista lista;
	CLista powielona = *(lista);

Kod w klasie zaś jest taki:

Kopiuj
CLista & CLista::operator * ()
{
	CLista * temp = new CLista(*this);
	return (*temp);
}
edytowany 3x, ostatnio: Adamos19
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
0

Pomijając, że jest to kompletnie antyidiomatyczne zachowanie dla operatora *, dlaczego zwracasz referencję, a nie kopię? Twoja funkcja powoduje wyciek, bo nikt nie będzie się spodzeiwał, że należy zwolnić pamięć po otrzymanej referencji!

Kopiuj
CLista CLista::operator*()
{
    return *this;
}

Ale lepiej to nazwać clone().

A jeszcze lepiej, po prostu użyć obiektów tak jak się powinno:

Kopiuj
    CLista lista;
    CLista powielona = lista;

edytowany 1x, ostatnio: kq
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Dzięki za sugestię, zwracałem referencję bo nie wpadłem na to że można zwrócić kopię, debil ze mnie.

Co rozumiesz pod pojęciem "antyidiomatyczne" ?

Co do propozycji to wiem że można było to tak rozwiązać, jednakże mam taką listę zadań którą robię i tam jest żeby się nauczyć przeładowywać i o różnych konstrukcjach językowych.
Jak sami widzicie mocno szkolę się w temacie programowania i tak to wygląda dlatego... Wasze wskazówki jednak są bardzo cenne dla mnie i na pewno wykorzystam je w przyszłości co pozwoli mi pisać soft na odpowiednio wysokim poziomie.

edytowany 1x, ostatnio: Adamos19
AL
  • Rejestracja:prawie 11 lat
  • Ostatnio:prawie 3 lata
  • Postów:1493
1

Antyidiomatyczne, ponieważ operator * z reguły ma zwrócić obiekt spod adresu, czyli obiekt przeciążający ten operator ma się zachowywać jak wskaźnik; to co Ty robisz jest totalnie nieintuicyjne.
Można tak zrobić, vide boost::spirit, gdzie gwiazdka udaje regexa, tam jest to...hmmm idiomatyczne dla idiomu "robimy bieda regex w składni cpp", ale z reguły nie jest to wskazane.

edytowany 2x, ostatnio: alagner
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
1

Co rozumiesz pod pojęciem "antyidiomatyczne" ?

Operatory mają przypisane do nich zachowanie, którego spodziewają się programiści. + dodaje, - odejmuje itd. * dwuargumentowy mnoży, a jednoargumentowy wyłuskuje (czyli traktuje obiekt jak wskaźnik i uzyskuje jego wartość). Tak działa to dla wskaźników, smart pointerów lub std::optional. Tutaj zachowanie tego operatora kompletnie łamie konwencję.


Zobacz pozostałe 4 komentarze
AL
Mnie przekonuje podniesiony na stackoverflow argument, że ten const pomaga na literówki typu if ((a+b) = 34), sam często piszę w stylu (o ile reguły kodowania pozwalają) if (123 == x), bo jak kiedyś przypadkowo backspace mi się omsknie to kompilator od razu pacnie mnie po łapach. A ile tu Murphy'ego a Machiavellego to już nieco filozofia ;)
kq
Z drugiej strony, zwracanie const (chyba) uniemożliwia różne optymalizacje - czy się mylę?
AL
Podstawowym problemem z const jest chyba fakt, że nie zrobisz na nim move.
kq
A RVO/NRVO?
AL
o ile dobrze zrozumiałem https://stackoverflow.com/questions/14429988/can-a-c-compiler-perform-rvo-for-a-const-return-value to nie jest to problem. Musiałbym się ze standardu podoktoryzować dla pewności.
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Panowie, co zrobić jeśli jakaś funkcja zwraca referencję do obiektu jakiejś tam klasy a chciałbym żeby w danym momencie zwróciła jakiś kod błędu, próbowałem zwrócić NULL ale error, próbowałem zwrócić nullptr - to samo. Co należałoby w tym wypadku zwrócić?

AL
  • Rejestracja:prawie 11 lat
  • Ostatnio:prawie 3 lata
  • Postów:1493
1

Rzucić. Albo zwracać std::optional. Albo inny boost::variant. Ew. zwracać wskaźnik.
EDIT: albo w stylu C: zwracać kod błędu typu int czy enum, a wypełniać referencję przekazywaną przez argument.
Zależy co chcesz uzyskać i jakiego typu api projektujesz.

edytowany 2x, ostatnio: alagner
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Nie rozumiem jak można zwracać wskaźnik w przypadku jak "trzeba" zwrócić referencję? Bezsensu. Przecież to dwie różne rzeczy i pewnie właśnie dlatego system nie pozwala mi zwrócić nullptr.
I czy jeśli wrzucę do kodu

Kopiuj
throw;

to funkcja zakończy swoje działanie od razu napotykając tą instrukcję i rzuci wyjątek dla kodu nadrzędnego?
I co to ten : std::optional i boost... zabijacie mnie :D

edytowany 1x, ostatnio: Adamos19
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
2

std::optional i std::variant są typami których powinieneś użyć w takim przypadku. Zwracany typ funkcji implikuje kontrakt. Jak zwracasz referencję, to gwarantujesz, że zwrócisz referencję do istniejącego obiektu. Gdy zwracasz wskaźnik to właściwie nie wiadomo co gwarantujesz, obecnie przyjmuje się to jako nullable referencję, a jak chcesz zawrzeć w kontrakcie kto jest odpowiedzialny za usunięcie obiektu to zwracasz unique_ptr. Jak chcesz zwrócić obiekt lub kod błędu, to poczytaj o różnych typach jak expected z biblioteki outcome


Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Mam taką oto funkcję:

Kopiuj
	macierz & operator = (const  macierz & x)
	{
		return (macierz &)x; // rzutowanie żeby się constansa pozbyć
	}

To dlaczego jeśli potem mamy:

Kopiuj
        // zdefiniowanie macierzy
	macierz x(2, 3);
	x.set(0, 0, 1);
	x.set(0, 1, 2);
	x.set(1, 2, 3);

	// wywołanie konstruktora kopiującego
	macierz y = x;

	// odczyt tych elementów
	cout << y.get(0, 0) << endl;
	cout << y.get(0, 1) << endl;
	cout << y.get(1, 2) << endl;
	
	// zmiana elementu ostatniego na 33
	x.set(1, 2, 33);

	// wywołanie przeładowanego operatora przypisania
	y = x;

	cout << y.get(0, 0) << endl;
	cout << y.get(0, 1) << endl;
	cout << y.get(1, 2) << endl;

Dlaczego ostatnie trzy cout'y nie wypisują mi nowych wartości czyli 1,2,33 tylko ciągle 1,2,3 ?
Przecie przekazałem referencję do środka i dodatkowo zwróciłem ją na zewnątrz więc y powinno być referencją do x po instrukcji y = x; Dlaczego nie jest?

edytowany 1x, ostatnio: Adamos19
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
0

Przede wszystkim, takie rzutowanie to bardzo często zło i nie powinno się go używać (zarówno C-casta jak i pozbywania się consta).

Twój operator= nic nie robi. Wartość zwracana z operatora jest po to, aby następujące wyrażenie działało:

Kopiuj
string a, b;
a = b = "42";

gdzie ostatnia linia jest równoznaczna z

Kopiuj
a = (b = "42");

albo jeszcze dobitniej:

Kopiuj
a.operator=(b.operator=("42"));

Inaczej mówiąc: w tym przypadku traktuj operator jako zwykłą funkcję. Wracając do Twojego przykładu:

Kopiuj
    macierz & do_nothing(const  macierz & x)
    {
        return (macierz &)x; // rzutowanie żeby się constansa pozbyć
    }
Kopiuj
y.do_nothing(x);

Nie ma się tutaj co spodziewać jakiegokolwiek efektu.


Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Dlaczego w takim razie jeśli wykonam podmianę samej tylko funkcji operatorowej na:

Kopiuj
	macierz operator = (const  macierz & x)
	{
		macierz temp = x;
		return temp;
	}

to również nie daje to żadnych rezultatów pomimo że mam dowody na to że ruszył dwukrotnie konstruktor kopiujący (proszę o wyjaśnienie dlaczego tak się dzieje).
Moim zdaniem do tej funkcji operatorowej przekazuję referencję na podmienioną macierz x a następnie robię (już wewnątrz tej funkcji) kopię i zwracam ją na zewnątrz. Dlaczego w takim razie nie otrzymuję także i w tym wypadku c'outem podmienionej macierzy?

kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
0

Nic się nie dzieje, bo nie modyfikujesz obecnego obiektu (dostępnego pod this).

Analogicznie

Kopiuj
struct foo
{
    int a;
    foo bar(foo& o) {
        foo ret = o;
        return ret;
    }
};

// ...

foo a{0}, b{42};

b.bar(a);

czy tutaj spodziewasz się modyfikacji b w b.bar(a)?


edytowany 2x, ostatnio: kq
Adamos19
Absolutnie nie spodziewam się w tym wypadku tego. Dzięki za ten przykład. A jeszcze bardziej za wyjaśnienia w bardzo logiczny sposób! Thx!
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

No dobrze, ale dlaczego muszę to zrobić jeśli przecież do środka funkcji operatorowej trafia już zmodyfikowana macierz?
Dlaczego ona nie jest w stanie operatorem przypisania przypisać się do lewej strony przed znakiem "równa się" ?

kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
0

W tym kontekście operator to zwykła funkcja. Nie modyfikujesz w niej obiektu klasy, na którym ją wywołujesz to i nic się nie dzieje.


enedil
  • Rejestracja:ponad 11 lat
  • Ostatnio:około godziny
  • Postów:1027
0
Adamos19 napisał(a):

Dlaczego w takim razie jeśli wykonam podmianę samej tylko funkcji operatorowej na:

Kopiuj
	macierz operator = (const  macierz & x)
	{
		macierz temp = x;
		return temp;
	}

to również nie daje to żadnych rezultatów pomimo że mam dowody na to że ruszył dwukrotnie konstruktor kopiujący (proszę o wyjaśnienie dlaczego tak się dzieje).
Moim zdaniem do tej funkcji operatorowej przekazuję referencję na podmienioną macierz x a następnie robię (już wewnątrz tej funkcji) kopię i zwracam ją na zewnątrz. Dlaczego w takim razie nie otrzymuję także i w tym wypadku c'outem podmienionej macierzy?

No i chyba już było wytłumaczone, że taki kod to UB.

kq
Akurat tutaj wygląda ok
Adamos19
To nie UB bo skasowałem referencję z wartości zwracanej...
enedil
Upewnię się: bo to dlatego że zwracamy wartość, tak?
kq
Tak, gdyby była zwrócona referencja to by było UB.
Adamos19
Tak. I dodatkowo dlatego że mam zdefiniowany konstruktor kopiujący który ruszy po zakończeniu tej funkcji. Pytanie dlaczego on rusza? Jeśli się mylę, poprawcie mnie proszę. Odpowiadam zgodnie z moją najlepszą wiedzą...
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Ahaaaa. Kurde, dobra, kumam.
Ale w takim razie po co twórcy języka wymyślili żeby funkcja operatorowa coś zwracała jeśli przecież wystarczyłoby aby modyfikowała po prostu obiekt pod wskaźnikiem this ?
Czy to po to aby móc wykonać podwójne przypisanie właśnie typu :

Kopiuj
x = y = z;

?

edytowany 1x, ostatnio: Adamos19
enedil
Albo sprawdzać wartość przypisania np w warunku logicznym.
Adamos19
Jasna sprawa, dziękuję uprzejmie za pomoc.
Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Zadałem to pytanie już wcześniej w komentarzu ale chciałbym jeszcze raz.
Dlaczego przy zwracaniu przez wartość funkcja operatorowa woła konstruktor kopiujący ?
... ustaliłem ponadto że odpala się on po zakończeniu tej funkcji czyli w chwili gdy ta wartość jest niejako wytransferowywana na zewnątrz...

flowCRANE
Nie zadawaj pytań w komentarzach, bo te nie są zbyt widoczne i nie nadają się do tego.
Adamos19
Przepraszam, nie będę.
kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
0

Po co wymyślać nową funkcjonalność, jeśli można zastosować już istniejącą? Kompilator i tak nie jest w stanie zgadnąć jaka jest poprawna implementacja operatora=, jeśli jest inna niż ta domyślna - a jeśli taka jest to zgodnie z rule of zero nie musisz go implementować w ogóle.


Adamos19
  • Rejestracja:ponad 10 lat
  • Ostatnio:około 2 lata
  • Postów:293
0

Pytanie nieaktualne. To przecie logiczne że jak po wartości przekazujemy to będzie się wywoływać konstruktor kopiujący - to cała przecie istota przekazywania przez wartość - że robi się kopia.
Czyż nie?

kq
Moderator C/C++
  • Rejestracja:prawie 12 lat
  • Ostatnio:dzień
  • Lokalizacja:Szczecin
1

Tak właśnie jest, przy czym musisz wziąć pod uwagę RVO i NRVO, ponieważ od C++17 są one nie tylko możliwe, ale w niektórych przypadkach wymagane przez standard.


Kliknij, aby dodać treść...

Pomoc 1.18.8

Typografia

Edytor obsługuje składnie Markdown, w której pojedynczy akcent *kursywa* oraz _kursywa_ to pochylenie. Z kolei podwójny akcent **pogrubienie** oraz __pogrubienie__ to pogrubienie. Dodanie znaczników ~~strike~~ to przekreślenie.

Możesz dodać formatowanie komendami , , oraz .

Ponieważ dekoracja podkreślenia jest przeznaczona na linki, markdown nie zawiera specjalnej składni dla podkreślenia. Dlatego by dodać podkreślenie, użyj <u>underline</u>.

Komendy formatujące reagują na skróty klawiszowe: Ctrl+B, Ctrl+I, Ctrl+U oraz Ctrl+S.

Linki

By dodać link w edytorze użyj komendy lub użyj składni [title](link). URL umieszczony w linku lub nawet URL umieszczony bezpośrednio w tekście będzie aktywny i klikalny.

Jeżeli chcesz, możesz samodzielnie dodać link: <a href="link">title</a>.

Wewnętrzne odnośniki

Możesz umieścić odnośnik do wewnętrznej podstrony, używając następującej składni: [[Delphi/Kompendium]] lub [[Delphi/Kompendium|kliknij, aby przejść do kompendium]]. Odnośniki mogą prowadzić do Forum 4programmers.net lub np. do Kompendium.

Wspomnienia użytkowników

By wspomnieć użytkownika forum, wpisz w formularzu znak @. Zobaczysz okienko samouzupełniające nazwy użytkowników. Samouzupełnienie dobierze odpowiedni format wspomnienia, zależnie od tego czy w nazwie użytkownika znajduje się spacja.

Znaczniki HTML

Dozwolone jest używanie niektórych znaczników HTML: <a>, <b>, <i>, <kbd>, <del>, <strong>, <dfn>, <pre>, <blockquote>, <hr/>, <sub>, <sup> oraz <img/>.

Skróty klawiszowe

Dodaj kombinację klawiszy komendą notacji klawiszy lub skrótem klawiszowym Alt+K.

Reprezentuj kombinacje klawiszowe używając taga <kbd>. Oddziel od siebie klawisze znakiem plus, np <kbd>Alt+Tab</kbd>.

Indeks górny oraz dolny

Przykład: wpisując H<sub>2</sub>O i m<sup>2</sup> otrzymasz: H2O i m2.

Składnia Tex

By precyzyjnie wyrazić działanie matematyczne, użyj składni Tex.

<tex>arcctg(x) = argtan(\frac{1}{x}) = arcsin(\frac{1}{\sqrt{1+x^2}})</tex>

Kod źródłowy

Krótkie fragmenty kodu

Wszelkie jednolinijkowe instrukcje języka programowania powinny być zawarte pomiędzy obróconymi apostrofami: `kod instrukcji` lub ``console.log(`string`);``.

Kod wielolinijkowy

Dodaj fragment kodu komendą . Fragmenty kodu zajmujące całą lub więcej linijek powinny być umieszczone w wielolinijkowym fragmencie kodu. Znaczniki ``` lub ~~~ umożliwiają kolorowanie różnych języków programowania. Możemy nadać nazwę języka programowania używając auto-uzupełnienia, kod został pokolorowany używając konkretnych ustawień kolorowania składni:

```javascript
document.write('Hello World');
```

Możesz zaznaczyć również już wklejony kod w edytorze, i użyć komendy  by zamienić go w kod. Użyj kombinacji Ctrl+`, by dodać fragment kodu bez oznaczników języka.

Tabelki

Dodaj przykładową tabelkę używając komendy . Przykładowa tabelka składa się z dwóch kolumn, nagłówka i jednego wiersza.

Wygeneruj tabelkę na podstawie szablonu. Oddziel komórki separatorem ; lub |, a następnie zaznacz szablonu.

nazwisko;dziedzina;odkrycie
Pitagoras;mathematics;Pythagorean Theorem
Albert Einstein;physics;General Relativity
Marie Curie, Pierre Curie;chemistry;Radium, Polonium

Użyj komendy by zamienić zaznaczony szablon na tabelkę Markdown.

Lista uporządkowana i nieuporządkowana

Możliwe jest tworzenie listy numerowanych oraz wypunktowanych. Wystarczy, że pierwszym znakiem linii będzie * lub - dla listy nieuporządkowanej oraz 1. dla listy uporządkowanej.

Użyj komendy by dodać listę uporządkowaną.

1. Lista numerowana
2. Lista numerowana

Użyj komendy by dodać listę nieuporządkowaną.

* Lista wypunktowana
* Lista wypunktowana
** Lista wypunktowana (drugi poziom)

Składnia Markdown

Edytor obsługuje składnię Markdown, która składa się ze znaków specjalnych. Dostępne komendy, jak formatowanie , dodanie tabelki lub fragmentu kodu są w pewnym sensie świadome otaczającej jej składni, i postarają się unikać uszkodzenia jej.

Dla przykładu, używając tylko dostępnych komend, nie możemy dodać formatowania pogrubienia do kodu wielolinijkowego, albo dodać listy do tabelki - mogłoby to doprowadzić do uszkodzenia składni.

W pewnych odosobnionych przypadkach brak nowej linii przed elementami markdown również mógłby uszkodzić składnie, dlatego edytor dodaje brakujące nowe linie. Dla przykładu, dodanie formatowania pochylenia zaraz po tabelce, mogłoby zostać błędne zinterpretowane, więc edytor doda oddzielającą nową linię pomiędzy tabelką, a pochyleniem.

Skróty klawiszowe

Skróty formatujące, kiedy w edytorze znajduje się pojedynczy kursor, wstawiają sformatowany tekst przykładowy. Jeśli w edytorze znajduje się zaznaczenie (słowo, linijka, paragraf), wtedy zaznaczenie zostaje sformatowane.

  • Ctrl+B - dodaj pogrubienie lub pogrub zaznaczenie
  • Ctrl+I - dodaj pochylenie lub pochyl zaznaczenie
  • Ctrl+U - dodaj podkreślenie lub podkreśl zaznaczenie
  • Ctrl+S - dodaj przekreślenie lub przekreśl zaznaczenie

Notacja Klawiszy

  • Alt+K - dodaj notację klawiszy

Fragment kodu bez oznacznika

  • Alt+C - dodaj pusty fragment kodu

Skróty operujące na kodzie i linijkach:

  • Alt+L - zaznaczenie całej linii
  • Alt+, Alt+ - przeniesienie linijki w której znajduje się kursor w górę/dół.
  • Tab/⌘+] - dodaj wcięcie (wcięcie w prawo)
  • Shit+Tab/⌘+[ - usunięcie wcięcia (wycięcie w lewo)

Dodawanie postów:

  • Ctrl+Enter - dodaj post
  • ⌘+Enter - dodaj post (MacOS)