在c++中,派生类向基类的自动类型转换只对指针或引用有效,在派生类对象和基类对象之间不存在这样的转换。将一个派生类对象初始化和赋值给一个基类对象,事实上调用的是基类的拷贝构造函数和赋值操作符重载函数,因为这两个函数的参数是基类类型的const引用,所以还是基于引用的自动类型转换。
派生类向基类的自动类型转换时c++类继承的难点之一,能力有限,先尝试表达自己的看法。转换操作主要发生在:
(1)在派生类的使用者的使用代码中
(2)在派生类中的成员函数或者派生类的友元中
(3)在继承自派生类的新类中
下面测试代码中的Base类原型为:
class Base
{
public:
int pub_mem;
protected:
int pro_mem;
private:
int pri_mem;
public:
Base(int a = 1, int b = 2, int c = 3) : pub_mem(a), pro_mem(b), pri_mem(c) {}
};
1. 在派生类的用户代码中实现的类类型自动类型转换
1.1 public继承
class Pub_Derv : public Base
{
public:
void out_put_base_data()
{
cout << pub_mem << endl;
cout << pro_mem << endl;
}
Pub_Derv() : Base(2, 4, 6) {}
};
int main(void)
{
Base* p = NULL;
Pub_Derv d1;
p = &d1; //派生类类型自动转换为基类类型
return 0;
}
如上代码能正确编译,其实这就是前面说的赋值兼容原则。赋值赋值兼容是指在需要基类的地方可以使用派生类代替,实现赋值赋值兼容 最重要的一点就是派生类需要以public方式继承基类。这样派生类被隐式转换为基类。
1.2 protected继承
class Pro_Derv : protected Base
{
public:
void out_put_base_data()
{
cout << pub_mem << endl;
cout << pro_mem << endl;
}
Pro_Derv() : Base(2, 4, 6) {}
};
int main(void)
{
Base* p = NULL;
Pro_Derv d2;
p = &d2;
return 0;
}
编译报错:
提示转换时不可达的。这是因为protected继承自Base的派生类Pro_Derv将自身的Base类中的public成员的访问属性设置为了protected。假设这样的编译能过,也就意味着能实现赋值兼容原则–需要基类的地方可以使用派生类代替,自然而然用户可以通过p指针访问派生类中基类的成员pub_mem,即p->pub_mem,但是它的属性现在是protected了,不能被访问。正是这样的悖论,c++编译器直接视为语法错误。换句话说,现在的基类已经不能用派生类来代替。
1.3 private继承
private继承,那么派生类中所有public、protected基类成员都是private的,和protected类似,派生类也不能代替基类,所以派生类对象的地址将不会初始化或者赋值给基类的指针。
2. 在派生类中的成员函数或者派生类的友元中的自动类型转换
通过c++类的不同继承方式一文知道,派生类的继承方式并不影响派生类中对基类成员的操作属性,对继承而来的成员只和该成员在基类中的访问权限说明符决定。所以下面代码编译通过的:
class Pro_Derv : protected Base
{
public:
void out_put_base_data()
{
cout << pub_mem << endl;
cout << pro_mem << endl;
}
Pro_Derv() : Base(2, 4, 6) {}
void func() { Pro_Derv d2; Base* p = &d2;} //类型转换成功
};
虽然也是protected成员,但是在派生类中仍然可以支持上述的赋值兼容原则–需要基类的地方可以使用派生类代替,通过p指针去访问派生类中基类的成员与直接通过基类指针是访问,去访问权限是一致的,所以编译通过。private继承也是如此。
3. 在继承自派生类中的新类成员函数或者友元中的自动类型转换
以继承Pro_Derv为例:
class Dev_from_pro : public Pro_Derv
{
public:
void m_func() {Pro_Derv d1; Base* p = &d1;}//类型转换成功
};
同理,在Dev_from_pro中所有Base类的public成员都是protected的,在Dev_from_pro类内部,将Pro_Derv的地址初始化/赋值给Base指针,同样满足赋值兼容原则–需要基类的地方可以使用派生类代替,所以上述转换成功。
最后引用c++ primer 5e中的一句话:
对于代码中的某个给定节点(就是上述的三种情况)来说,如果基类的公有成员是可访问的,则派生类向基类的类型转换也是可访问的;反之不行。
因为可转换意味着外界可以使用基类指针访问派生类中基类的数据成员,而能被外界访问的只能是派生类中的公有成员。