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).
- 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ą.
- 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!
}
- W prawdziwym kodzie właściwie zawsze (pomijając typy wbudowane) operujemy na referencjach i wskaźnikach, i tam też jest to najbardziej istotne.
- 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.