一、类的继承与类型转换
1.概述
一般情况,如果想把一个类的指针或引用绑定到另外一个对象上,需要指针或者引用的类型与指向对象一致。但是存在继承关系的类是个例外:可以将基类的指针或者引用指向子类
原因是因为:子类包含了父类非static成员和它本身的非static成员。因此,当一个基类的指针或引用指向子类对象时,所指向的真实对象有可能仅仅是父类,也有可能是完整的子类
上图中,指针或引用的类型叫静态类型,指针或引用指向的对象叫动态类型。静态类型在编译期就已经确定,而动态类型是不确定的(有可能是父类对象,也有可能是完整的子类),所以只能在运行时才能确定
当然,如果不适用指针或者引用,那么动态类型和静态类型永远一致,都是类名。
2.父子类的类型转换
正因为基类对象是完整的子类对象的一部分,而基类的指针或引用可以指向子类中基类的部分,所以,子类可以转化为基类,但是基类不能转化为子类,子类可以当做基类使用,而基类却不能当做子类使用。再简言之就是子类的中的成员基类没有,而基类的成员子类都有,所以基类不能转子类,子类能转基类
示例
class base1
{
public:
base1(){}
~base1(){}
static void sfunc() {cout<<__func__<<endl;}
static int st;
};
int base1::st=10;
class derive2:public base1
{
public:
derive2(){cout<<__func__<<endl;}
~derive2(){cout<<__func__<<endl;}
};
int main(int argc, char const *argv[])
{
derive2 d2;
base1 b1=d2;
base1 b1t;
b1t=d2;
derive2 t=b1;
return 0;
}
基类在给子类初始化时,需要转化为子类,又因为基类不能转子类,所以报错
无论是用子类对象给基类对象赋值,还是用子类对象给基类对象初始化,调用的都是基类的拷贝构造或者基类的operator=,而拷贝构造和operator=的形参是const 对象的引用,而基类的引用可以指向子类对象,所以可以将子类对象作为实参,而在基类的拷贝构造或者operator=中,只能对基类中的成员进行初始化或者赋值,无法处理子类中的成员,所以当使用子类对象对基类对象进行初始化时,除基类外的子类部分被切掉了。简言之就是基类中没有子类的成员,所以除基类外的子类部分被切掉了
此外,子类的指针或引用也不能指向基类。即使一个基类的指针或引用绑定在子类上,也不能让子类的指针或引用指向基类
示例1
int main(int argc, char const *argv[])
{
base1 b1;
derive2 *p=&b1;
return 0;
}
如果子类的指针可以指向基类,那么就可以通过子类的指针访问基类中原来没有的成员,所以,不允许子类的指针或引用指向基类对象
示例2
int main(int argc, char const *argv[])
{
base1 *pb=new derive2();
derive2 *p=pb;
return 0;
}
虽然基类的指针指向了一个子类,并且子类指针也指向了该子类对象,但是编译器在编译derive2 *p=pb;时发现:有子类的指针指向基类,于是编译器认为违反了子类的指针或引用不能指向基类的原则,所以报错
总之,不能用子类的指针或者引用指向基类
二、protected关键字的其他性质
protected是专为继承而生的,基类中的protected成员对于除子类的其他类和作用域来说是不可访问的;对于子类的成员和友元是可以访问的
注意:在子类中访问父类的protected成员时,只能通过子类对象访问,不能通过父类对象访问
示例
class base3
{
public:
base3(){cout<<__func__<<endl;}
~base3(){cout<<__func__<<endl;}
protected:
int i;
};
class derive3:public base3
{
public:
derive3(){cout<<__func__<<endl;}
~derive3(){cout<<__func__<<endl;}
//void func(base3 &b) {b.i=3;} //需要注释掉
void func2(derive3 &d) {d.i=3;}
};
上述代码的第15行如果不注释掉,会报错,因为在子类中访问父类的protected成员时,只能通过子类对象访问,不能通过父类对象访问
三、类的继承与友元
如果一个类的可以做基类,那么,当该类被继承时,基类中的友元不会被继承,不能成为子类的友元,所以基类的友元不能访问子类对象的成员。同样,子类中的友元也不能访问基类中的protected和private成员。简言之,子类和基类中的友元没有任何关系
示例
class base4
{
friend class friendclass;
public:
base4(){cout<<__func__<<endl;}
~base4(){cout<<__func__<<endl;}
protected:
int i1;
private:
int i2;
};
class derive4:public base4
{
friend void setfunc(derive4 &b);
friend void setfunc(base4 &b);
public:
derive4(){cout<<__func__<<endl;}
~derive4(){cout<<__func__<<endl;}
protected:
int i3;
private:
int i4;
};
class friendclass {
public:
friendclass() {cout<<__func__<<endl;}
~friendclass() {cout<<__func__<<endl;}
void set(derive4 &d) {d.i1=10;d.i2=10;d.i3=10;d.i4=10;}
};
void setfunc(derive4 &d) {
d.i1=10;d.i2=10;d.i3=10;d.i4=10;
}
void setfunc(base4 &d) {
d.i1=10;d.i2=10;
}
上述代码有编译无法通过的原因:
1、friendclass是base4的友元而不是derive的友元,所以friendclass中的set函数无法访问子类derive中的成员(i3, i4)
2、void setfunc(derive4 &b);是子类的友元,但是不是基类的友元,所以无法访问基类中的private成员(i2)。但是由于derive4是base4的子类,所以可以通过子类访问基类中的protected成员i1
3、friend void setfunc(base4 &b);虽然是子类derive的友元,但是参数是基类base4的引用,所以无法在子类中通过基类base4访问基类的protected成员i1,更无法访问基类的private成员i2
参考
《C++ Primer》
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出