赋值兼容规则
在必定条件下,不一样类型的数据之间能够进行类型转换,如能够将整型数据赋给双精度型变量。在赋值以前,先把整型数据转换成双精度型数据,而后再把它赋给双精度型变量。这种不一样类型数据之间的自动转换和赋值,称为赋值兼容。在基类和派生类对象之间也存有赋值兼容关系,基类和派生类对象之间的赋值兼容规则是指在须要基类对象的任何地方,均可以使用其子类对象来代替
- 有基本的三类
- 派生类的对象可以赋值给基类对象。
- 派生类的对象可以初始化基类的对象或引用。
- 派生类对象的地址可以赋值给指向基类的指针。
1·派生类的对象能够赋值给基类对象
A a1; //定义基类A对象a1 B b1; //定义类A的公用派生类B的对象b1 a1=b1; //用派生类B对象b1对基类对象a1赋值
在赋值时舍弃派生类本身的成员,只进行数据成员的赋值。ide
实际上,所谓赋值只是对数据成员赋值,对成员函数不存在赋值问题,内存中数据成员和成员函数是分开的。函数
注意: 赋值后不能企图经过对象a1去访问派生类对象b1的成员,由于b1的成员与a1的成员是不一样的
假设age是派生类B中增长的公用数据成员,分析下面的用法:
a1.age=23;//错误,a1中不包含派生类中增长的成员 b1.age=21; //正确,b1中包含派生类中增长的成员
只能用子类对象对其基类对象赋值,而不能用基类对象对其子类对象赋值,理由是显然的,两种对象的大小是不一样的,由于基类对象不包含派生类的成员没法对派生类的成员赋值。同理,同一基类的不一样派生类对象之间也不能赋值
2·派生类的对象能够初始化基类的引用。
已定义了基类A对象a1,能够定义a1的引用变量:.net
A a1; //定义基类A对象a1 B b1; //定义公用派生类B对象b1 A &r=a1; //定义基类A对象的引用变量r(A的别名是r),并用a1对其初始化
这时,r和a1共享同一段存储单元。也能够用派生类对象初始化引用变量r,将上面最后一行改成指针
A& r=b1;//定义基类A对象的引用变量r,并用派生类B对象b1//对其初始化
注意: 此时r并非b1的别名,也不与b1共享同一段存储单元。它只是b1中基类部分的别名code
这里的r定义为A类的引用,因此它的有效范围就只有A类那么大,r与b1中基类部分共享同一段存储单元,r与b1具备相同的起始地址。 若是函数的参数是基类对象或基类对象的引用,相应的实参能够用子类对象。
3·派生类对象的地址能够赋给指向基类的指针。
也就是说,指向基类对象的指针变量也能够指向派生类对象。例定义一个基类Student(学生),再定义Student类的公用派生类Graduate(研究生), 用指向基类对象的指针输出数据。
例子
#include <iostream> #include <string> using namespace std; class Student//声明Student类 { public : Student(int, string,float );//声明构造函数 void display( );//声明输出函数 private : int num; string name; float score; }; Student::Student(int n, string nam,float s) //定义构造函数 { num=n; name=nam; score=s; } void Student::display( )//定义输出函数 { cout<<endl<<″num:″<<num<<endl; cout<<″name:″<<name<<endl; cout<<″score:″<<score<<endl; } class Graduate:public Student//声明公用派生类Graduate { public : Graduate(int, string ,float ,float );//声明构造函数 void display( );//声明输出函数 private : float pay;//工资 }; Graduate::Graduate(int n, string nam,float s,float p):Student(n,nam,s),pay(p){ }//定义构造函数 void Graduate::display() //定义输出函数 { Student::display(); //调用Student类的display函数 cout<<″pay=″<<pay<<endl; } int main() { Student stud1(1001,″Li″,87.5); //定义Student类对象stud1 Graduate grad1(2001,″Wang″,98.5,563.5); //定义Graduate类对象grad1 Student *pt=&stud1;//定义指向Student类对象的指针并指向stud1 pt->display( ); //调用stud1.display函数 pt=&grad1; //指针指向grad1 pt->display( ); //调用grad1.display函数 }
不少人会认为: 在派生类中有两个同名的display成员函数,根据同名隐藏的规则,被调用的应当是派生类Graduate对象的display函数,
在执行Graduate::display函数过程当中调用Student::display函数,输出num,name,score,而后再输出pay的值。blog
事实上这种推论是错误的,先看看程序的输出结果:
num:1001
name:Li
score:87.5
num:2001
name:wang
score:98.5
并无输出pay的值。
问题在于pt是指向Student类对象的指针变量,它的指类是Student类,即便让它指向了grad1,但实际上pt指向的是grad1中从基类继承的部分(它指向的空间只能是基类中数据成员那么大的空间)。经过指向基类对象的指针,只能访问派生类中的基类成员,而不能访问派生类增长的成员。因此pt->display()调用的不是派生类Graduate对象所增长的display函数,而是基类的display函数,因此只输出研究生grad1的num,name,score3个数据。
其实,经过强制转换也能够将Student类的地址赋值给Graduate类所定义的指针,可是,这样作不安全,会让使用者误觉得能够调用Graduate类中增长的成员,其实否则,因此不建议使用
综上所述,主要是由于基类和派生类中成员所占空间大小的不一样,所引起的赋值兼容问题,例如int类型赋值给double类型,就是赋值兼容问题,而double类型赋值给int类型,就是不兼容,必需要强转,否则会报错