#include
class A
{
public:virtual void print(){cout<<"A::print()"<
class B:public A
{
public:virtual void print(){cout<<"B::print()"<
class C:public A
{
public:virtual void print(){ cout<<"C::print()"<
void printfun(A a)
{
a.print();
}
void main()
{
A a;
B b;
C c;
printfun(a);
printfun(b);
printfun(c);
}
将派生类operator=给基类的一个引用,对象会被切割!导致派生类的信息会被切掉!
输出结果:
A::print()
A::print()
A::print()
因为printfun的参数是A,而不是A&,所以这里是object slice而不是多态起作用。
如果是void printfun(A& a)
{
a.print();
}
就会输出
A::print()
B::print()
C::print()
那就用到虚函数了,如果父类是虚函数,那么子类有的就调用子类,子类没有的就调用父类.
来看另一道题目:
#include
using namespace std;
class Base
{
public:
Base()
{
cout << "Base ctor() " << endl;
}
virtual void print()
{
cout << "Base::print()" << endl;
}
};
class Derived:public Base
{
public:
Derived()
{
cout << "Derived ctor()" << endl;
}
void print()
{
cout << "Derived::print()" << endl;
}
};
int main()
{
Base _base;
Derived _derived;
Base* pBase = new Derived();
pBase->Base::print();
pBase->print();
return 0;
}
输出结果:
Base ctor()
Base ctor()
Derived ctor()
Base ctor()
Derived ctor()
Base::print()
Derived::print()
Press any key to continue
若改成如下代码:
Base& pBase = _derived;
pBase.Base::print();
pBase.print();
//pBase.Derived::print(); 报错
输出结果:
Base ctor()
Base ctor()
Derived ctor()
Base::print()
Derived::print()
Press any key to continue
由此可见,
Base* pBase = new Derived(); 等价于Base& pBase = _derived;也等价于 Base* pBase = &_derived;
在看一道例题:
#include
class A
{
public:
void Mark() {cout<<"A::Mark"<
class B : public A
{
public:
void Mark() { cout<<"B::Mark"<
int main()
{
B b;
b.A::Mark();
b.B::Mark();
B* pB = &b;
pB->A::Mark();
pB->B::Mark();
A* pA = pB;
pA->A::Mark();
//pA->B::Mark();
return 0;
}
输出结果:
A::Mark
B::Mark
A::Mark
B::Mark
A::Mark
Press any key to continue
但通過派生類對象的指針或引用來調用虛函數﹐則不會發生object slice﹐但是若直接將派生類對象傳給基類型參數則會發生此事。
pA是指向A的指针,他不能操作其他类的成员(包括它的派生类成员) 不管是用指针,还是引用,只要是对成员的访问都要在编译期确定。 pA->B::Mark(); //出错的原因是找不到 A::B::Mark这个成员 即使Mark是虚函数,也不能这样使用 B::Mark始终不是A的成员 虚函数只是用于保留接口名。
如果Mark是虚函数 pA->Mark(); 在编译期看上去是调用的A::Mark,但是在运行期确是调用的B::Mark
可以看一下,下面的程序:
#include
using namespace std;
class Base
{
public:
Base()
{
}
};
class Derived:public Base
{
public:
Derived()
{
}
virtual void print()
{
cout << "Derived::print() called" << endl;
}
};
int main()
{
Base* pBase = new Derived();
pBase->print();
return 0;
}
结果如下:
error C2039: 'print' : is not a member of 'Base'
首先来看看代码
1、有Base::print()这个成员吗?
很明显,所以不能用Base*指针去访问print()。解决方法是,如果你不想给Base提供print()方法,那么就可以给Base定义一个纯虚函数,用以保留一个接口。但是在这里,你的Base看上去或在用途上不能保证是一个抽象的基类,所以最好定义成虚函数,而不是纯虚函数。
#include
using namespace std;
class Base{
public:
Base(){}
virtual void print(){} //这里
};
class Derived:public Base{
public:
Derived(){}
virtual void print(){
cout << "Derived::print() called" << endl;
}
};
//调用
Base* pBase = new Derived();
pBase->print(); 编译器找到Base::print()这个接口,所以正确。而对于到底是调用哪个print()会在运行期由vptr决定。
Q2:vptr和vtbl都是在compile期生成的,并且在执行期不能被改变,vptr的初始化一般发生在对象的ctor中;
Q3:必须提出一点,一般性的继承和虚拟继承在实现上有着较大的差别,所以必须分开讨论。
1.首先我们来看,一般性的继承。此时,我们可以说,每个对象只能拥有一对vtbl和vptr,所以,是不能单纯的使用copy得到的,两者将在对象的ctor中设置,并且对virtual function在vtbl中的索引位置以及值都会根据子类对父类中重新定义virtual function的情况加以调整,但有一点必须注意的是destructor,每个类的vtbl中,如果存在这一项,它都将指向被该类定义的destructor,而非父类的;如果没有定义,对不起,可能出错(视不同的编译器而定)
2.再来看虚拟继承和多继承的情况,嗯,这可是个“大”问题,呵呵,在cpp中,有很多的“凡是”规则可以遵循,比如凡是多态的实现必须借助于“指针”或“索引”完成等等,但其中很多一遇到这个“virtual base class”,尤其是带有“菱形”的继承结构,就玩完!:(在这样的继承系统之中,对象是可能拥有一个以上的vtbl的,呵呵应该是vtbls和vptrs了,一般是每个父类对应一个vtbl和一个vptr。说了怎么多废话,但关键的在下面:由于无论时多继承也好,虚拟继承也好,它们必须支持“多态”这个关键性质,这就意味着,它们必须能完成下面的任务:凭借指向父类的指针、引用能完成对子类中成员函数的调用。所有,此时,父类的虚表必须根据其它父类和子类的情况进行“调整”,这种调整将在子类的ctor中完成(可能会进一步借助与父类的ctor),也就是说拥有相同父类的子类对象其所含的父类对应的vtbl的内容将可能是不同的,必须在其各自的ctor中加以调整。
对于单一的继承并且是非virtual继承的类而言,其只能拥有一个vtbl,我想这是来自base的,但注意并不是原来的那个,而是经过调整过得,比如Derived class对base的声明或定义的virtual function进行了重载等,至少对应于destructor的那一项已经变成了Derived的了。所以,对应特定的类,这个表也只要有一个就可以了,因而不存在对象之间vtbl的copy问题,而vptr可以认为是类的一个“指针”成员,不过其在对象之间的拷贝由编译器插入一定的代码加以完成
“对象切割”是一个基于“内存”操作上的概念,楼主最初的程序之所以有问题,并非由于内存的变化引起的,引起变化的只是对某块内存的“解释”上;因而很难和切割有什么瓜葛。
当Base& pBase = _derived;被写下时,我们必须清楚从compiler的角度上是如果看待pBase的,它是类型Base的一个引用,使用它来标记_derived所对应的那块“内存”,我们将只能看到_derived中属于Base的那部分,由于vtpr一般位于Base所在的那块内存中,并且已经经过compiler的调整(根据Derived中对virtual function的重载情况),所以“多态”可以发生,但对应Base之外的那些内容,在compiler来说,对不起,单使用pBase是看不到的。