C++ 向上转型

在 C++ 中经常会发生数据类型的转换,例如将 int 类型的数据赋值给 float 类型的变量时,编译器会先把 int 类型的数据转换为 float 类型再赋值;反过来,float 类型的数据在经过类型转换后也可以赋值给 int 类型的变量。

数据类型转换的前提是,编译器知道如何对数据进行取舍。例如:

int a = 10.9;
printf("%d\n", a);

输出结果为 10,编译器会将小数部分直接丢掉(不是四舍五入)。再如:

float b = 10;
printf("%f\n", b);

输出结果为 10.000000,编译器会自动添加小数部分。

类其实也是一种数据类型,也可以发生数据类型转换,不过这种转换只有在基类和派生类之间才有意义,并且只能将派生类赋值给基类,包括将派生类对象赋值给基类对象、将派生类指针赋值给基类指针、将派生类引用赋值给基类引用,这在 C++ 中称为向上转型(Upcasting)。相应地,将基类赋值给派生类称为向下转型(Downcasting)。

向上转型非常安全,可以由编译器自动完成;向下转型有风险,需要程序员手动干预。

将派生类对象赋值给基类对象

下面的例子演示了如何将派生类对象赋值给基类对象:

#include <iostream>
using namespace std;//基类
class A{
public:A(int a);
public:void display();
public:int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){cout<<"Class A: m_a="<<m_a<<endl;
}//派生类
class B: public A{
public:B(int a, int b);
public:void display();
public:int m_b;
};
B::B(int a, int b): A(a), m_b(b){ }
void B::display(){cout<<"Class B: m_a="<<m_a<<", m_b="<<m_b<<endl;
}int main(){A a(10);B b(66, 99);//赋值前a.display();b.display();cout<<"--------------"<<endl;//赋值后a = b;a.display();b.display();return 0;
}

运行结果:

Class A: m_a=10
Class B: m_a=66, m_b=99
----------------------------
Class A: m_a=66
Class B: m_a=66, m_b=99

本例中 A 是基类, B 是派生类,a、b 分别是它们的对象,由于派生类 B 包含了从基类 A 继承来的成员,因此可以将派生类对象 b 赋值给基类对象 a。通过运行结果也可以发现,赋值后 a 所包含的成员变量的值已经发生了变化。

赋值的本质是将现有的数据写入已分配好的内存中,对象的内存只包含了成员变量,所以对象之间的赋值是成员变量的赋值,成员函数不存在赋值问题。

将派生类对象赋值给基类对象时,会舍弃派生类新增的成员,也就是“大材小用”,如下图所示:

在这里插入图片描述

可以发现,即使将派生类对象赋值给基类对象,基类对象也不会包含派生类的成员,所以依然不同通过基类对象来访问派生类的成员。

这种转换关系是不可逆的,只能用派生类对象给基类对象赋值,而不能用基类对象给派生类对象赋值

将派生类指针赋值给基类指针

除了可以将派生类对象赋值给基类对象(对象变量之间的赋值),还可以将派生类指针赋值给基类指针(对象指针之间的赋值)。我们先来看一个多继承的例子,继承关系为:

在这里插入图片描述
下面的代码实现了这种继承关系:

#include <iostream>
using namespace std;//基类A
class A{
public:A(int a);
public:void display();
protected:int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){cout<<"Class A: m_a="<<m_a<<endl;
}//中间派生类B
class B: public A{
public:B(int a, int b);
public:void display();
protected:int m_b;
};
B::B(int a, int b): A(a), m_b(b){ }
void B::display(){cout<<"Class B: m_a="<<m_a<<", m_b="<<m_b<<endl;
}//基类C
class C{
public:C(int c);
public:void display();
protected:int m_c;
};
C::C(int c): m_c(c){ }
void C::display(){cout<<"Class C: m_c="<<m_c<<endl;
}//最终派生类D
class D: public B, public C{
public:D(int a, int b, int c, int d);
public:void display();
private:int m_d;
};
D::D(int a, int b, int c, int d): B(a, b), C(c), m_d(d){ }
void D::display(){cout<<"Class D: m_a="<<m_a<<", m_b="<<m_b<<", m_c="<<m_c<<", m_d="<<m_d<<endl;
}int main(){A *pa = new A(1);B *pb = new B(2, 20);C *pc = new C(3);D *pd = new D(4, 40, 400, 4000);pa = pd;pa -> display();pb = pd;pb -> display();pc = pd;pc -> display();cout<<"-----------------------"<<endl;cout<<"pa="<<pa<<endl;cout<<"pb="<<pb<<endl;cout<<"pc="<<pc<<endl;cout<<"pd="<<pd<<endl;return 0;
}

运行结果:

Class A: m_a=4
Class B: m_a=4, m_b=40
Class C: m_c=400
-----------------------
pa=0x9b17f8
pb=0x9b17f8
pc=0x9b1800
pd=0x9b17f8

本例中定义了多个对象指针,并尝试将派生类指针赋值给基类指针。与对象变量之间的赋值不同的是,对象指针之间的赋值并没有拷贝对象的成员,也没有修改对象本身的数据,仅仅是改变了指针的指向。

将派生类引用赋值给基类引用

引用在本质上是通过指针的方式实现的,既然基类的指针可以指向派生类的对象,那么我们就有理由推断:基类的引用也可以指向派生类的对象,并且它的表现和指针是类似的。

修改上例中 main() 函数内部的代码,用引用取代指针:

int main(){D d(4, 40, 400, 4000);A &ra = d;B &rb = d;C &rc = d;ra.display();rb.display();rc.display();return 0;
}

运行结果:

Class A: m_a=4
Class B: m_a=4, m_b=40
Class C: m_c=400

ra、rb、rc 是基类的引用,它们都引用了派生类对象 d,并调用了 display() 函数,从运行结果可以发现,虽然使用了派生类对象的成员变量,但是却没有使用派生类的成员函数,这和指针的表现是一样的。

引用和指针的表现之所以如此类似,是因为引用和指针并没有本质上的区别,引用仅仅是对指针进行了简单封装。

最后需要注意的是,向上转型后通过基类的对象、指针、引用只能访问从基类继承过去的成员(包括成员变量和成员函数),不能访问派生类新增的成员。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/564241.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

java拆分任意五位数_五位数拆分出各位 - osc_foo7glsg的个人空间 - OSCHINA - 中文开源技术交流社区...

5、输入一个五位数&#xff0c;输出一个反转的五位数输入-》12345 输出-》54321//这是五位数字拆分方法。System.out.println("请输入五位数的数字&#xff1a;"); // 比如&#xff1a;12345int g1num%10;int g2num/10%10;int g3num/100%10;int g4num/1000%10;int g…

C++ 纯虚函数和抽象类

C中&#xff0c;可以将虚函数声明为纯虚函数&#xff0c;语法格式为&#xff1a; virtual 返回值类型 函数名 (函数参数) 0;纯虚函数没有函数体&#xff0c;只有函数声明&#xff0c;在虚函数声明的结尾加上0&#xff0c;表明此函数为纯虚函数。 最后的0并不表示函数返回值为…

python中保留两位小数的编写程序_P081 保留两位小数

所属年份&#xff1a;2011.3;2011.9请编一个函数 float fun(double h)&#xff0c;该函数的功能是&#xff1a;使变量h中的值保留两位小数&#xff0c;并对第三位进行四舍五入(规定h中的值为正数)。例如&#xff0c;若h 值为1234.567&#xff0c;则函数返回1234.570000&#xf…

C++ 获取类型信息

typeid 运算符用来获取一个表达式的类型信息。类型信息对于编程语言非常重要&#xff0c;它描述了数据的各种属性&#xff1a; 对于基本类型&#xff08;int、float 等C内置类型&#xff09;的数据&#xff0c;类型信息所包含的内容比较简单&#xff0c;主要是指数据的类型。对…

java 8 lambda 申明_2019-02-03——Java8 Lambda

一.认识LambdaLambda表达式专门针对只有一个方法的接口(即函数式接口)lambda表达式的基本格式为(x,y...)—>{表达式...};(1)可选类型声明&#xff1a;不需要声明参数类型&#xff0c;编译器可以统一识别参数值。(2)可选的参数圆括号&#xff1a;一个参数无需定义圆括号&…

C 数字排列组合

编辑程序让任意四个数字排列组合并且每一个排列组合的数字都不相同。 结果展示 完整代码 #include <stdio.h> //定义头文件 int main() {int i,j,k;printf("\n");for(i1;i<5;i){ //三重循环 for(j1;j<5;j){for(k1;k<5;k){ //确保 i j k 位置不同 i…

java 汇总_java基础汇总

1.关于Http和Hibernatet里面Session的区别HttpSessionHttpSession&#xff1a;是一个抽象接口&#xff0c;J2EE的Web程序在运行的时候&#xff0c;会给每一个新的访问者建立一个HttpSession&#xff0c;这个Session是用户身份的唯一标示&#xff0c;WEB 会话跟踪用的。【注:是容…

C 天数的计算

根据程序提示输入某年某月某日&#xff0c;判断这一天是这一年的第几天&#xff0c;并进行输出显示。 效果演示 完整代码 #include <stdio.h> int main() {int day,month,year,sum,leap;printf("\n请输入年、月、日&#xff0c;格式为&#xff1a;年,月,日&#…

java+filter加密_Javaweb之Filter案例练习-自动登录问题和MD5加密

自动登录问题和MD5加密前面已经完成了Filter的自动登录&#xff0c;但是有问题&#xff0c;我们在web.xml中Filter的url-mapping中配置的规则是/*, 也就是这个网站的所有请求都拦截。这肯定不合适。我们本来访问/login.jsp&#xff0c;本来就是去登录&#xff0c;结果也进行了拦…

C 从小到大排序

将数字按从小到大的的顺序进行排序输出&#xff0c;根据提示输入相应的数字个数&#xff0c;每输入一个数字要进行换行然后输入下一个数字&#xff0c;当三个数字完全输入之后点击回车&#xff0c;程序会根据从大到小的顺序进行数字的排序输出。 效果演示 完整代码 #include…

java class文件常量池_JAVA程序员谈谈class文件结构中的常量池-class文件

常量的类型有12种CONSTANT_Utf8_info1字面量UTF-8编码的字符串CONSTANT_Integer_info3字面量整型字面量CONSTANT_Float_info4字面量浮点型字面量CONSTANT_Long_info5字面量长整型字面量CONSTANT_Double_info6字面量双精度浮点型字面量CONSTANT_Class_info7符号引用类或接口的符…

C 输出图案

在C语言中使用 * 号输出各种图案。 用 * 输出字母C 效果 完整代码 #include <stdio.h>int main() {printf("用 * 号输出字母 C\n");printf(" ****\n");printf("*\n");printf("*\n");printf(" ****\n");} 用 * 输出…

C 多数排序

使用C语言编写程序对多个数字进行排序输出的操作。 根据提示输入十个数字并按照从小到大的顺序进行输出显示。 效果 完整代码 #include<stdio.h> #define N 10 int main() {int i,j,a[N],temp;printf("请输入 10 个数字&#xff1a;\n");for(i0;i<N;i)s…

java httppost wsdl_Java使用HttpUrlConnection调用webService(wsdl)

首先需要下载工具https://pan.baidu.com/s/1XQ-VubxcPFoqwGm7wierHg下载成功后解压打开exe程序&#xff0c;在wsdl endpoint中输入你wsdl的地址&#xff0c;点击get&#xff0c;等待一小会后会跳到invoke标签下的界面点击某个方法&#xff0c;例如上图的login&#xff0c;可以看…

php判断一个字符串是否为纯数字,php判断变量是否为纯数字字符串的方法

在php中有时候需要判断一个变量的值是否为数字或是否为数字字符串&#xff0c;而php中也提供了一个很好用的内置函数 is_numeric()&#xff0c;可以很轻松的来检测变量。php is_numeric() 函数介绍is_numeric()&#xff1a;检测一个php的变量是否为数字或数字字符串。语法&…

C 反向输出

使用C语言的递归方法对输入的字符进行反向输出。 效果 完整代码 #include <stdio.h>int main() {int i5;void palin(int n);printf("请输入5个字符\40:\40");palin(i);printf("\n"); } void palin(n) int n; {char next;if(n<1) {nextgetchar(…

C 数组逆序输出

编写程序对固定内容的数组进行逆序输出&#xff0c;第一个值和最后一个值的位置互换。 效果 完整代码 #include<stdio.h> #define N 10 int main() {int a[N]{10,100,20,43,54,15,6,77,82,91};int i,t;printf("原始数组是:\n");for(i0;i<N;i)printf(&quo…

php修改http header,php header函数的常用http头设置

//okheader(‘HTTP/1.1 200 OK);//设置一个404头:header(‘HTTP/1.1 404 Not Found);//设置地址被永久的重定向header(‘HTTP/1.1 301 Moved Permanently);//转到一个新地址header(‘Location: http://www.example.org/‘);//文件延迟转向:header(‘Refresh: 10; urlhttp://www…

C 论大小

比较两个数字的大小并进行判断输出。 效果 完整代码 #define LAG > #define SMA < #define EQ #include <stdio.h> int main() {int i,j;printf("请输入两个数字&#xff1a;\n");scanf("%d %d",&i,&j);if(i LAG j)printf("%…

php读这文件速度,php 测试硬盘读写-php 测试硬盘写速率

使用php写入一个1GB大小的文件&#xff0c;检查硬盘的写速率&#xff0c;可能有一定误差&#xff0c;建议还是使用专业的硬盘测试工具来检测一般电脑读写在20M/s&#xff0c;这个测试要执行大概40-50s仅供参考&#xff1a;set_time_limit(0);$str str_pad($str, 512, "0&…