[c++] przeciazenie i zaprzyjaznienie

0

Cześć

Dlaczego ten kod wyrzuca blad:

fragment kalsy:
    Time operator*(double n) const;

    friend Time operator*(double n, const Time &t)
    {
        return t*n;
    } //inline

    friend ostream & operator<<(ostream & os, Time & t); //zamiast show przeciazenie <<

definicja metod:
Time Time::operator *(double n) const
{
    Time result;
    long totalminutes = hours * n * 60 + minutes * n;
    result.hours = totalminutes / 60;
    result.minutes = totalminutes % 60;
    return result;
}

ostream & operator<<(ostream & os, Time & t)
{
    os << t.hours << " godzin, " << t.minutes << " minut";
}

kod w mainie:

int main(int argc, char** argv)
{
    using std::cout;
    using std::endl;

    Time aida(3, 35);
    Time tosca(2, 48);
    Time temp;

    cout << "Aida i Tosca:\n";
    cout << aida << "; " << tosca << endl;
    temp = aida + tosca;
    cout << "Aida i Tosca: " << temp << endl;
    temp = aida * 1.17;

    cout << "Aida * 1.17: " << temp << endl;

   
 cout << "10 * Tosca: " << 10*tosca<< endl; //Tu Blad
    return (EXIT_SUCCESS);
}

blad: main.cpp:29: error: no match for ‘operator<<’ in ‘std::operator<< [with _Traits = std::char_traits<char>](((std::basic_ostream<char, std::char_traits<char> >&)(& std::cout)), ((const char*)"10 * Tosca: ")) << operator*(1.0e+1, ((const Time&)((const Time*)(& tosca))))’

0

zauwazylem, ze jak zastapie kod w mainie to jest ok:

temp=10*tosca;

 cout << "10 * Tosca: " << temp<< endl;

moje pytanie, dlaczego taki kod, wyrzuca blad:

 cout << "10 * Tosca: " << 10 * tosca << endl;
</cpp>

Nawiasem mowiac, ta bledna skladnia byla w przykladzie w ksiazce ;/

0

dokladniej moje pytanie brzmi, dlaczego do mojego przeciazenia << kompilator tego wlasciwie nie interpretruje (tylko rzuca ten blad)

0

popraw prototyp:

friend ostream & operator<<(ostream & os, const Time & t); // brakowało const

Poza tym w tym operatorze zgubiłeś return!

0

ok, dzieki, slepy jestem

mam jeszcze pytanie, dlaczego to const przed elementem klasy przekazywanym do funkcji jest konieczne?

przeciez ono zapewnia tylko to, ze nie bedziemy modyfikowac tego elementu...

0

no właśnie!
Popatrz jaką masz konstrukcję!
wywołujesz operator<< z parametrem, który jest zmienną automatyczną, a parametr deklaruje, że będziesz go modyfikował (brak const w referencji).
Modyfikowanie parametru automatycznego oznacza utratę danych - czyli jest błąd!

0

ok, brak consta oznacza ze moge modyfikowac ten obiekt (ale przeciez nie musze), myslalem ze const sluzy tylko do ochrony danych i operacji na stalych obiektach...

jezeli przesle ten obiekt bez const, to myslalem ze moge go normalnie uzywac (jezeli bym chcial to sobie go moge zmodyfikowac, ale przeciez nie musze)

szczerze :) to dalej nie rozumiem dlaczego ten const jest tu niezbedny, obiekt klasy Time, ktory stworzylem nie zostal zdefiniowany u mnie jako const...

0

Jeśli nie dasz consta to nie gwarantujesz kompilatorowi, że nie chcesz zmieniać argumentu, tzn. wyrażenie "10 * tosca" zwraca jakiś obiekt, który jest tymczasowy (za chwilę go nie będzie), jest on przechowywany w specjalnym miejscu w pamięci i nie można go modyfikować (w sumie i tak to nie ma sensu, bo go za chwilę nie będzie), więc kompilator nie może dopuścić do jego zmiany, aby wykonać to zadanie wymaga właśnie słówka const. Może trochę rozjaśniłem, napisałem to samo co MarekR22.

0

dzieki ze mi to w ogóle tlumaczycie xD (ale cos dalej mam problemy ze zlapaniem xD)

const stoi przy referencji do obiektu Time (dzieki temu nie moge modyfikowac danego egzemplarza klasy)

Jednak co to obchodzi kompilatora? Na moje rozumowanie gdybym nie dal tego consta on powinien dopuscic takie rozwiazanie, w tej funkcji przeciazajecej operator, jest to w sumie bez sensu (aby zmieniac ten obiekt), ale w innych funkcjach przekazujac cos przez referencje, a pozniej to w niej modyfikuje...

tu wydaje sie modyfikowanie tego obiektu glupkowate (ale dlaczego pod zadnycm pozorem nie mozna tego zrobic?)
przeciez jakby bardzo chcial to moglbym ten obiekt Time zmienic wtedy w tej przeciazajacej funkcji - zaciemnilo by to troche kod, ale co tam...(dlaczego kompilator mi to ingeruje?)

podsumowajac, w noirmalnej funkcji moge sobie przesylac referencje do obiektu bez const (o ile ten obiekt nie byl zdefiniowany jako staly) i moge lub nie musze sobie go we funkcji modyfikowac (moge przeslac bez consta obiekt i mimo ze np. funkcja go tylko wyswietla, const nie jest konieczne, aczkolwiek wskazane...)

tu mam funkcje przeciazajaca operator (dlaczego tu kompilatora ten const przy obiekcie przekazywanym przez referencje tak bardzo interesuje ?)

Czyzby to jakis odgorny przymus, aby obiekty klasy przekazywane przez referencje do funkcji przeciazajacej operator byly zawsze z const? (aby nikogo nie pokusilo modyfikowac obiekt w funkcji przeciazajecj operator - co skutkuje zaciemniem kodu...)

0

No to może kod pomoże:

int f0() {
  int a = 5;
  return a; // <- to zwraca kopie obiektu (obiekt tymczasowy, ktory jest staly, a wiec nie mozna go zmieniac)
}

void f1(int& ref) { }

void f2(const int& ref) { }

int main(int argc, char* argv[]) {
  const int i = 10;
  //! int& ref = i; <- tak nie wolno, poniewaz po to sa stale, aby nie mozna ich byla zmieniac
  // a wiec trzeba tak
  const int& ref = i;
  // to samo tylko z funkcjami
  //! f1(f0()); <- znowu nie wolno, tak naprawde to jest to samo co wyzej, funkcja f0() zwraca obiekt staly
  // a teraz poprawnie
  f2(f0());
  return 0;
}

Po prostu zapamiętaj sobie, że instrukcja return tworzy kopię obiektu i ten obiekt przez nią utworzony jest stałą.
Kompilator nie jest tak zaprogramowany, żeby sprawdzał czy ty rzeczywiście po przypisaniu zmieniasz w jakiś sposób wartość stałej, czy nie, musisz mu jasno powiedzieć, że nie chcesz nic z tym obiektem robić, czyli aby uzyskać adres stałej trzeba utworzyć referencję do stałych.

0
noh4h_ss napisał(a)

Po prostu zapamiętaj sobie, że instrukcja return tworzy kopię obiektu i ten obiekt przez nią utworzony jest stałą.
Oj, mylisz się, to nie o stałość tu chodzi. Problemem jest to, że wywołanie funkcji to rwartość (chyba, że funkcja zwraca referencję, wtedy wywołanie jest lwartością). Referencja do zmiennej może odnosić się tylko do modyfikowalnych lwartości, referencja do stałej do wszystkiego (modyfikowalnych lwartości, stałych lwartości, modyfikowalnych rwartości, stałych rwartości).

...zaraz, napisałem "modyfikowalnych rwartości"? Jak się okazuje, obiekty zwrócone przez funkcję jako wartość można zmieniać (czyli wywołać ich metody nieoznaczone jako const, nie dotyczy to oczywiście typów wbudowanych, bo nie mają żadnych metod):

#include <cstdio>

struct Foo {
  void sth() { // note the lack of "const" here
    std::puts("hello!");
  }
};

Foo doSomething() {
  return Foo();
}

int main() {
  doSomething().sth();
}

Tak, ten kod jest poprawny. Standard dopuszcza wywoływanie niestałych metod na modyfikowalnych rwartościach.

...ale, co się stanie, jeśli dopiszemy teraz jeden krótki wyraz do typu zwracanego funkcji doSomething?

const Foo doSomething() {
  return Foo();
}

Tym razem kompilator tego nie przepuści, narzekając na to, że próbujemy wywołać nie-const metodę na rzecz obiektu, który jest const. I jak najbardziej ma rację. Dopisując to const spowodowaliśmy, że doSomething() zwraca stałą rwartość, a jak wiadomo, próba zmiany stałej nie jest dobrym pomysłem.

0

Dalej uważam, że obiekt tymczasowy jest stałą, jednak twój kod zostanie skompilowany poprawnie, a z tym standardem to nie jestem pewien, ponieważ taki zapis jest zazwyczaj uznawany za błędny, opieram się na książce "Thinking In C++" B. Eckel (a dokładniej rozdzial 8, podrozdział "Temporaries", książka jest dostępna darmowa w internecie)

Oto treść:
Sometimes, during the evaluation of an expression, the compiler must create temporary objects. These are objects like any other: they require storage and they must be constructed and destroyed. The difference is that you never see them – the compiler is responsible for deciding that they’re needed and the details of their existence. But there is one thing about temporaries: they’re automatically const. Because you usually won’t be able to get your hands on a temporary object, telling it to do something that will change that temporary is almost certainly a mistake because you won’t be able to use that information. By making all temporaries automatically const, the compiler informs you when you make that mistake.

In the above example, f5( ) returns a non-const X object. But in the expression:

f7(f5());

the compiler must manufacture a temporary object to hold the return value of f5( ) so it can be passed to f7( ). This would be fine if f7( ) took its argument by value; then the temporary would be copied into f7( ) and it wouldn’t matter what happened to the temporary X. However, f7( ) takes its argument by reference, which means in this example takes the address of the temporary X. Since f7( ) doesn’t take its argument by const reference, it has permission to modify the temporary object. But the compiler knows that the temporary will vanish as soon as the expression evaluation is complete, and thus any modifications you make to the temporary X will be lost. By making all temporary objects automatically const, this situation causes a compile-time error so you don’t get caught by what would be a very difficult bug to find.

However, notice the expressions that are legal:

  f5() = X(1);
  f5().modify();

Although these pass muster for the compiler, they are actually problematic. f5( ) returns an X object, and for the compiler to satisfy the above expressions it must create a temporary to hold that return value. So in both expressions the temporary object is being modified, and as soon as the expression is over the temporary is cleaned up. As a result, the modifications are lost so this code is probably a bug – but the compiler doesn’t tell you anything about it. Expressions like these are simple enough for you to detect the problem, but when things get more complex it’s possible for a bug to slip through these cracks.

0

Po pierwsze, kod przedstawiony przez Fanaela jest w 100% poprawny. Zreszta, nawet w przytoczonym przez Ciebie fragmencie nikt nie twierdzi ze modyfikacja temporar'a to błąd. Jest w tym fragmencie zaś błąd: "But there is one thing about temporaries: they’re automatically const." - nie jest to prawda, chyba ze jakis kompilator ma taka niestandardowa opcje, wlaczalna jakims przelacznikiem.. "telling it to do something that will change that temporary is almost certainly a mistake because you won’t be able to use that information" - to też nie jest prawdą! zerknij chociażby na 'idiom' named-parameters, czy method-chaining, o ile dobrze nazwy zapamietalem.

Druga rzecz, tymczasówki zwracane z funkcji/metod NIE sa stałymi, chyba ze je tak zadeklarujesz w naglowku funkcji metody. Obiekty tymczasowe są r-value, a to nie jest to samo co const. Nie można do nich przypisywać poprzez operator=, ale można je zmieniać.

Po prostu, jest ogromna roznica pomiedzy funkcja:

Foo doSomething();
oraz funkcja:
Foo const doSomething(); // const oczywiscie odnosi sie do Foo, czyli zwracanej wartosci
//edit, zreszta to juz Fanael napisal..

jesli chcesz klocic sie, ze nie powinno sie modyfikowac obiektow tymczasowych i temu podone, zwroc uwage - ze jesli to by byla prawda, to jezyk/kompolator wymuszalby const-ność zwracanych wartosci i spaliloby się w ten sposob bardzo wiele fajnych opcji zwiazanych z RAII i automatycznym sprzataniem przy wychodzeniu za scope... albo wymuszaloby notoryczne uzywanie "mutable" zeby dalo sie sensownie destruktory pisac

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