C++类型兼容规则
一、概念
类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。
再来看一下派生类中从基类中继承而来的成员和新增成员的关系:
虽然这个不是实际的物理模型,但是起码我们能大致明白两者的关系,同时也可以发现,派生类其实是可以被当作基类来使用的,因为它拥有基类的几乎所有成员(除了构造、析构函数)。
而实际上在基类与派生类之间存在一种转换,即派生类到基类类型转换。和其他类型转换一样,编译器会隐式地执行派生类到基类的转换。
存在如下三种转换:
- 派生类的对象可以隐含转换为基类对象。
- 派生类的对象可以初始化基类的引用。
- 派生类的指针可以隐含转换为基类的指针。
二、代码
#include <iostream>
using namespace std;
class A {
public:
A() = default;
A(int a1, int b1) : a(a1), b(b1) { }
~A() { }
void print1() const{cout << a << ' ' << b << endl;}
int a = 0, b = 0;
};
class B : public A {
public:
B() = default;
B(int a1, int b1, const string s1) : A(a1, b1), s(s1) { }
B(B &item) {
this->a = item.a;
this->b = item.b;
this->s = item.s;
}
~B() { }
void print2() const{this->print1(); cout << s << endl;}
private:
string s = "abc";
};
void display1(A &item) {
item.print1();
}
void display2(A *ptr) {
ptr->print1();
}
int main() {
A a1(1, 1);
B b1(1, 2, "def");
A a2(b1); //基类的对象可以用来初始化派生类的对象
display1(b1); //参数类型为基类对象的函数同样也可以使用派生类的对象作为参数
A "e1 = b1; //基类的引用可以绑定派生类对象
A *ptr1 = &b1; //基类的对象指针同样可以指向派生类对象的地址
display2(ptr1);
return 0;
}
该程序定义了两个类,基类A和派生类B。
然后还有两个函数display1和display2,两个函数的参数类型分别是基类对象的引用还有基类的对象指针。
在主函数中我们可以发现:
三、注意事项和总结
我们需要尤其注意的是,不存在从基类向派生类的隐式类型转换!
A base;
B *ptr = &base; //错误:不能将基类转换成派生类
B "e = base; //错误:不能将基类转换成派生类
上述的两种转换都想从基类向派生类转换,是错误的。如果上述赋值是合法的,则我们有可能会使用ptr指针或者quote引用访问base中本不存在的成员。
除此之外:
类型兼容的好处是对于基类及其公有派生类的对象,可以使用相同的函数统一进行处理。因为当函数的形参为基类的对象(或引用、指针)时,实参可以是派生类的对象(或指针),而没有必要为每一个类设计单独的模块,大大提高了程序的效率。
笔记中大部分内容引用自《C++语言程序设计》和《C++ Primer》