#include<iostream>classBase{protected:intm_value{};public:Base(intvalue):m_value{value}{}virtualconstchar*getName()const{return"Base";}intgetValue()const{returnm_value;}};classDerived:publicBase{public:Derived(intvalue):Base{value}{}constchar*getName()constoverride{return"Derived";}};intmain(){Derivedderived{5};std::cout<<"derived is a "<<derived.getName()<<" and has value "<<derived.getValue()<<'\n';Base&ref{derived};std::cout<<"ref is a "<<ref.getName()<<" and has value "<<ref.getValue()<<'\n';Base*ptr{&derived};std::cout<<"ptr is a "<<ptr->getName()<<" and has value "<<ptr->getValue()<<'\n';return0;}
在上面的例子中,ref引用和ptr指向 derived,它有一个 Base 部分和一个 derived 部分。因为ref和ptr是Base类型,所以ref和ptr只能看到 derived 的 Base 部分——derived 的 derived 部分仍然存在,但不能通过 ref 或 ptr 看到。但是,通过使用虚函数,我们可以访问函数的最后派生的版本。因此,上面的程序输出:
123
derived is a Derived and has value 5
ref is a Derived and has value 5
ptr is a Derived and has value 5
intmain(){Derivedderived{5};Basebase{derived};// what happens here?std::cout<<"base is a "<<base.getName()<<" and has value "<<base.getValue()<<'\n';return0;}
在编写这个程序时,您可能没有注意到base是一个值形参,而不是引用。因此,当printName(d)调用时,虽然我们可能期望base.getName()调用虚函数getName()并打印“I am a Derived”,但实际上并不是。相反,Derived对象d被切片,只有Base部分被复制到base参数中。当base.getName()执行时,即使对getName()函数进行了虚化,也没有类的Derived部分供它解析。因此,这个程序输出:
voidprintName(constBase&base)// note: base now passed by reference{std::cout<<"I am a "<<base.getName()<<'\n';}intmain(){Derivedd{5};printName(d);return0;}
COPY
This prints:
1
I am a Derived
vector 切片
新程序员时常在使用std::vector实现多态时遇到问题。考虑下面的程序:
1 2 3 4 5 6 7 8 91011121314
#include<vector>intmain(){std::vector<Base>v{};v.push_back(Base{5});// 添加一个 Base 对象到 vectorv.push_back(Derived{6});// 添加一个 Derived 对象到 vector // 打印 vector 中的所有元素for(constauto&element:v)std::cout<<"I am a "<<element.getName()<<" with value "<<element.getValue()<<'\n';return0;}
编译运行程序,输出:
12
I am a Base with value 5
I am a Base with value 6
和之前的例子类似,因为 std::vector 被声明为了 Base 类型的容器,当添加 Derived(6) 时,它被切片了。
#include<iostream>#include<vector>intmain(){std::vector<Base*>v{};Baseb{5};// b 和 d 不能是匿名对象(指针不能指向匿名对象)Derivedd{6};v.push_back(&b);// add a Base object to our vectorv.push_back(&d);// add a Derived object to our vector// Print out all of the elements in our vectorfor(constauto*element:v)std::cout<<"I am a "<<element->getName()<<" with value "<<element->getValue()<<'\n';return0;}
打印:
12
I am a Base with value 5
I am a Derived with value 6
#include<functional> // for std::reference_wrapper#include<iostream>#include<vector>classBase{protected:intm_value{};public:Base(intvalue):m_value{value}{}virtualconstchar*getName()const{return"Base";}intgetValue()const{returnm_value;}};classDerived:publicBase{public:Derived(intvalue):Base{value}{}constchar*getName()constoverride{return"Derived";}};intmain(){std::vector<std::reference_wrapper<Base>>v{};// 存放 Base 的可赋值引用的容器Baseb{5};// b and d can't be anonymous objectsDerivedd{6};v.push_back(b);// add a Base object to our vectorv.push_back(d);// add a Derived object to our vector// Print out all of the elements in our vector// we use .get() to get our element out of the std::reference_wrapperfor(constauto&element:v)// element has type const std::reference_wrapper<Base>&std::cout<<"I am a "<<element.get().getName()<<" with value "<<element.get().getValue()<<'\n';return0;}