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?

- Rejestracja:ponad 10 lat
- Ostatnio:około 2 lata
- Postów:293
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:
CLista lista;
CLista powielona = *(lista);
Kod w klasie zaś jest taki:
CLista & CLista::operator * ()
{
CLista * temp = new CLista(*this);
return (*temp);
}

- Rejestracja:prawie 12 lat
- Ostatnio:dzień
- Lokalizacja:Szczecin
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!
CLista CLista::operator*()
{
return *this;
}
Ale lepiej to nazwać clone()
.
A jeszcze lepiej, po prostu użyć obiektów tak jak się powinno:
CLista lista;
CLista powielona = lista;

- Rejestracja:ponad 10 lat
- Ostatnio:około 2 lata
- Postów:293
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.
- Rejestracja:prawie 11 lat
- Ostatnio:prawie 3 lata
- Postów:1493
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.

- Rejestracja:prawie 12 lat
- Ostatnio:dzień
- Lokalizacja:Szczecin
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ę.
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 ;)



- Rejestracja:ponad 10 lat
- Ostatnio:około 2 lata
- Postów:293
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ć?
- Rejestracja:prawie 11 lat
- Ostatnio:prawie 3 lata
- Postów:1493
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.

- Rejestracja:ponad 10 lat
- Ostatnio:około 2 lata
- Postów:293
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
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

- Rejestracja:prawie 12 lat
- Ostatnio:dzień
- Lokalizacja:Szczecin
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

- Rejestracja:ponad 10 lat
- Ostatnio:około 2 lata
- Postów:293
Mam taką oto funkcję:
macierz & operator = (const macierz & x)
{
return (macierz &)x; // rzutowanie żeby się constansa pozbyć
}
To dlaczego jeśli potem mamy:
// 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?

- Rejestracja:prawie 12 lat
- Ostatnio:dzień
- Lokalizacja:Szczecin
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:
string a, b;
a = b = "42";
gdzie ostatnia linia jest równoznaczna z
a = (b = "42");
albo jeszcze dobitniej:
a.operator=(b.operator=("42"));
Inaczej mówiąc: w tym przypadku traktuj operator jako zwykłą funkcję. Wracając do Twojego przykładu:
macierz & do_nothing(const macierz & x)
{
return (macierz &)x; // rzutowanie żeby się constansa pozbyć
}
y.do_nothing(x);
Nie ma się tutaj co spodziewać jakiegokolwiek efektu.

- Rejestracja:ponad 10 lat
- Ostatnio:około 2 lata
- Postów:293
Dlaczego w takim razie jeśli wykonam podmianę samej tylko funkcji operatorowej na:
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?

- Rejestracja:prawie 12 lat
- Ostatnio:dzień
- Lokalizacja:Szczecin
Nic się nie dzieje, bo nie modyfikujesz obecnego obiektu (dostępnego pod this
).
Analogicznie
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)
?


- Rejestracja:ponad 11 lat
- Ostatnio:około godziny
- Postów:1027
Adamos19 napisał(a):
Dlaczego w takim razie jeśli wykonam podmianę samej tylko funkcji operatorowej na:
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.






- Rejestracja:ponad 10 lat
- Ostatnio:około 2 lata
- Postów:293
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 :
x = y = z;
?

- Rejestracja:ponad 10 lat
- Ostatnio:około 2 lata
- Postów:293
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...

- Rejestracja:prawie 12 lat
- Ostatnio:dzień
- Lokalizacja:Szczecin
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.