Zasadność rzutowania

3

Dostałeś już odpowiedzi na wszystkie te pytania. Obiekt po rzutowaniu jest obiektem nowego typu — tego, na który go zrzutowałeś.

  1. Możesz próbować użyć wszystkich składowych innej klasy — co najwyżej dostaniesz segfaultem na twarz, jak, powiedzmy, coś wirtualnego nie będzie działać.
  2. Wynikiem rzutowania jest obiekt nowego typu. Tak jak Ci pisałem wyżej.
  3. Rzutowanie robi nowy obiekt. Stary obiekt dalej istnieje na takich samych zasadach, jak wcześniej. Jak rzutujesz jakieś wskaźniki czy robisz inne cuda, to może Ci wybuchnąć na milion sposobów, ale to już inna kwestia.
1
class base {
public:
    std::string class_name() {
        return "base";
    }
};
class derived : public base {
public:
    std::string class_name() {
        return "derived";
    }
};

int main() {
    derived d;
    std::cout << d.class_name() << " " << static_cast<base>(d).class_name() << "\n";
}

Przemyśl ten przykład.

0

@Althorion:

rzutowanie jest po to, żeby udawać, że zmienna jest innego typu, niż jest. Bo, na przykład, klasa-syn przykrywa metodę klasy-ojca, a ty bardzo chcesz użyć metody klasy-ojca. To tego się tyczy to, o czym enedil pisał — tzn. dziedziczenie w kontekście rzutowania — bo wtedy, i praktycznie wyłącznie wtedy, ma ono sens.

ok, brzmi już rozumnie i zaczynam łapać. Zrobiłem kod

wykonuję rzutowanie w górę -CELOWO- ale obiekt obj1 nie ma metody z obiektu 2... - coś poyebayem ?

@kq: POPRAWIONE - o ile dobrze

class Obiekt1
{
public:
    void funkcja(){
        cout << "obiekt 1" << endl;
    }
};

class Obiekt2 : public Obiekt1
{
public:
    void funkcja(){
        cout << "obiekt 2" << endl;
    }
};

int main()
{
    Obiekt1 obj1;
    Obiekt2 obj2;

    static_cast<Obiekt1>(obj2).funkcja();
}
0

Tak. Robisz cast na typ Obiekt1 — typ, na który castujesz, ląduje w nawiasach ostrych <>. Więc zrobiłeś sobie w obj1 obiekt typu Objekt1, który przypomina obj2. Obiekty typu Obiekt1 mają funkcja() wypisującą obiekt 1. Więc i obj1 to robi.

Na przyszłość, uwzględnij też fakt, że jak masz raz zadeklarowany typ zmiennej, to ona już od tego momentu będzie takiego typu. Więc nawet, jak będziesz coś-tam castował, to kompilator Ci pewnie prze-castuje jeszcze raz, jeśli będzie potrzebował, by się typy zgadzały.

EDYCJA:
Wersja po poprawce powinna już robić dokładnie to, czego się spodziewałeś.

EDYCJA 2:
Moje angielskie wydanie „C++ Programming Language”, czwarta edycja, ma omówione rzutowanie na 298. stronie i dalej.

1

Ok, ale przestań spamować tematami na które odpowiedź możesz znaleźć w internecie i przestań nie odwoływać sie do rad które są Ci udzielane. Inaczej ludzie będą Ci pisać nieladnie, bo plujesz na ich poświęcony Tobie czas. Ktoś pomaga a Ty i tak swoje. Programowanie to głównie nauka na własnych błędach i własne badanie/cwiczenia. Ty jesteś rozpieszczonym bachorem i chcesz mieć wszystko na tacy podane myśląc, że siedzą tu sami twoi prywatni korepetytorzy. Najgorsze jest to, że jesteś stary. Stary a dziecko.
Jeżeli naprawdę chcesz się nauczyć a nie irytować innych, albo lechtac sobie ego to zacznij w końcu mozolnie pracować. Robić ćwiczenia notatki itd.
Innymi słowy, zacznij się w końcu uczyć.

0
enedil napisał(a):
class base {
public:
    std::string class_name() {
        return "base";
    }
};
class derived : public base {
public:
    std::string class_name() {
        return "derived";
    }
};

int main() {
    derived d;
    std::cout << d.class_name() << " " << static_cast<base>(d).class_name() << "\n";  // #1
}

Linijkę #1 można zastąpić tym:

std::cout << d.class_name() << ' ' << d.base::class_name() << '\n';

Generalnie przysłanianie niewirtualnych metod uważam za szkodliwe, więcej z tego problemów niż pożytku.
Przy virtual kompilator może pomóc (dzięki override/final/=0) a przysłanianie... szukaj wiatru w polu ;)

1
enedil napisał(a):
class base {
public:
    std::string class_name() {
        return "base";
    }
};
class derived : public base {
public:
    std::string class_name() {
        return "derived";
    }
};

int main() {
    derived d;
    std::cout << d.class_name() << " " << static_cast<base>(d).class_name() << "\n";
}

Przemyśl ten przykład.

Ogółem uważam, że takie przykłady robią więcej szkody niż pożytku. Ponawiając wcześniejszy cytat z Einsteina: "Make everything as simple as possible, but not simpler". Takiego kodu nie przepuściłbym przez code review, więc nie powinien on być pokazywany nowicjuszom (a na takim poziomie jest zkubiński).

  1. to jest efektywnie std::cout << d.class_name() << " " << base{d}.class_name() << "\n"; - czyli utworzenie nowego obiektu. O to praktycznie nigdy nie chodzi w rzutowaniu. Inaczej mówiąc, nie ma zasadniczej różnicy między
derived d;
base b = d;
std::cout << d.class_name() << " " << b.class_name() << "\n";

i

derived d;
base b = static_cast<b>(d);
std::cout << d.class_name() << " " << b.class_name() << "\n";

Za to, co ważniejsze, adresy obiektów d i b (lub utworzonego przez cast) się różnią.

  1. Jest to szczególnie istotne gdy weźmiemy pod uwagę polimorfizm dynamiczny
class base {
public:
    virtual std::string class_name() {
        return "base";
    }
};
class derived : public base {
public:
    std::string class_name() override {
        return "derived";
    }
};

int main() {
    derived d;
    std::cout << d.class_name() << " " << static_cast<base>(d).class_name() << "\n";
    // oops!
}
  1. W prawdziwym kodzie właściwie zawsze (pomijając typy wbudowane) operujemy na referencjach i wskaźnikach, i tam też jest to najbardziej istotne.
  2. Wcale nie jest to nawet prostsze w tłumaczeniu, wystarczy wyjść ciut dalej z przykładami i mamy np.
struct base
{
    std::string name = "base";
};
struct derived: base
{
    std::string name = "derived";
};

int main() {
    derived d1, d2;
    static_cast<base>(d1).name = "lol";
    static_cast<base&>(d2).name = "lol";
    DBG(d1.base::name)
    DBG(d1.derived::name)
    DBG(d2.base::name)
    DBG(d2.derived::name)
}

Tłumaczenie dlaczego wynik jest taki, jak poniżej jest wg mnie bardziej kłopotliwe niż nauczenie poprawnie od razu.

d1.base::name                                               base
d1.derived::name                                            derived
d2.base::name                                               lol
d2.derived::name                                            derived

Szczególnie, jeśli wcześniej mówiliśmy że castujemy obiekt i na nim pracujemy, a okazuje się, że po prostu utworzyliśmy sobie nowy poprzez slicing.

2

Nie trzeba do tych wszystkich rzeczy żadnego rzutowania!!!
https://wandbox.org/permlink/hUViWdnS6K60bJmz

#include <iostream>
using namespace std;

class base
{
    public:
    void foo() { cout<<"World"<<endl; }
};

class derived:public base
{
    public:
    void foo() { cout<<"Hello"<<endl; }
};

int main()
{
    derived d;
    d.foo();
    d.base::foo();
    return 0;
}
2
Riddle napisał(a):

Rzutowanie to nic innego jak zignorowanie systemu typów języka.

Mam problem z tym stwierdzeniem.

Powiedz mi, czym się rożni taki kod:

struct Typ1 {
    explicit operator Typ2() const {
        return Typ2(blablabla);
    }
}

Od takiego kodu:

struct Typ2 {
    Typ2(Typ1 typ1) {
        blablabla;
    }
}

Trudno powiedzieć, by ten drugi snippet w jakikolwiek sposób "ignorował system typów". Przeciwnie, w pełni szanując system typów tworzy nowy obiekt typu Typ2 na podstawie istniejącego obiektu typu Typ1. W żadnym punkcie system typów nie został naruszony nawet odrobinkę.

Z kolei ten pierwszy snippet definiuje rzutowanie, a zatem, idąc twoim tokiem rozumowania, pozwala na zignorowanie systemu typów. Tyle że... ten pierwszy snippet robi dokładnie to samo, co ten drugi snippet! Tworzy nowy obiekt typu Typ2 na podstawie istniejącego obiektu typu Typ1.

System typów rzeczywiście pozwala zignorować reinterpret_cast, ale przecież reinterpret_cast dalece nie wyczerpuje rzutowania, przeciwnie, powinno być używane jak najrzadziej i z najwyższą ostrożnością, bo łatwo o UB. Lwia część rzutowania to powinien static_cast, domyślnie rzutowanie powinno oznaczać static_cast właśnie, a static_cast nie może być IMO uważany za żaden wytrych w systemie typów.

2

Pokazałeś przykład konwersji. A to co innego niż rzutowanie, pomimo tego, że jedno i drugie można uzyskać za pomocą static_cast.

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