指针和引用的upcasting
除了在函数调用时编译器可以实现自动转换外,在进行指针或者引用赋值时也可以自动转换。和函数调用一样,这两种情况都不需要显式的强制转换。
Wind w;
Instrument* ip = &w; // Upcast
Instrument& ir = w; // Upcast
危机
在upcast的时候会失去类型信息,如下编译器只能将ip作为Instrument的指针来调用
Wind w;
Instrument* ip = &w;
ip->play(middleC);
此时调用的是基类的Instrument::play( )而非本意Wind::play( ).,这种问题可以通过面向对象的编程技术的第三个里程碑多态polymorphism来解决,在C++中是通过虚函数来实现的。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
采用多态技术是值传递和地址传递有很大的区别,因为地址大小总是一样的;传递派生类的地址和基类的地址是一样的,因为派生类中包含了基类。值传递时,派生对象将剥离只剩下基类部分。
//: C15:ObjectSlicing.cpp #include <iostream> #include <string> using namespace std; class Pet { string pname; public: Pet(const string& name) : pname(name) {} virtual string name() const { return pname; } virtual string description() const { return "This is " + pname; } }; class Dog : public Pet { string favoriteActivity; public: Dog(const string& name, const string& activity) : Pet(name), favoriteActivity(activity) {} string description() const { return Pet::name() + " likes to " + favoriteActivity; } }; void describe(Pet p) { // Slices the object cout << p.description() << endl; } int main() { Pet p("Alfred"); Dog d("Fluffy", "sleep"); describe(p); describe(d); } ///:~
|
This is Alfred This is Fluffy
|
两个原因:
首先,当调用describe时,只有Pet大小的东西在栈上分配释放,因此多余的部分将被剥离;
其次,因为是值传递,编译器知道具体的数据类型,将调用pet的拷贝构造函数,其将VPTR初始化为PET的VTABLE,然后将dog的基类部分拷贝至p中,因此p将变成完完全全的pet对象。