C++中的赋值兼容性和重写
1,父子间的赋值兼容:
1,子类对象可以当做父类对象使用(赋值兼容性):
1,子类对象可以直接赋值给父类对象;
2,子类对象可以直接初始化父类对象;
3,父类指针可以直接指向子类对象(得到的是子类对象);
4,父类引用可以直接引用子类对象;
2,子类对象的兼容性编程实验:
#include <iostream>
#include <string> using namespace std; class Parent
{
public:
int mi; void add(int i)
{
mi += i;
} void add(int a, int b)
{
mi += (a + b);
}
}; class Child : public Parent
{
public:
int mv; void add(int x, int y, int z)
{
mv += (x + y + z);
}
}; int main()
{
Parent p;
Child c; p = c; // 赋值兼容性的第一种体现; Parent p1(c); // 赋值兼容性的第一种体现; Parent& rp = c; // 赋值兼容性的第二种体现;
Parent* pp = &c; // 赋值兼容性的第二种体现; rp.mi = ;
rp.add(); // 这里 rp 指向子类,应该是不可以访问被同名覆盖的 add(int),但是没有报错;也就是可以访问 add(int),这里没有发生同名覆盖,子类对象已经变成父类对象,只能访问父类当中的成员;
rp.add(, ); // 这里 rp 指向子类,应该是不可以访问被同名覆盖的 add(int, int),但是没有报错;也就是可以访问add(int, int),这里没有发生同名覆盖,子类对象已经变成父类队像,只能访问父类中的成员; /* 为什么编译不过?子类对象已经变成父类对象,没办法访问子类对象中的成员 */
// pp->mv = 1000; // 'class Parent' has no member named 'mv'
// pp->add(1, 10, 100); // no matching function for call to 'Parent::add(int, int, int)'
// candidates are: int Parent::add(int)
// int Parent::add(int, int) return ;
}
3,当使用父类指针(引用)指向子类对象时:
1,子类对象完全退化为父类对象;
2,只能访问父类中定义的成员;
1,通过这个指针或者引用只能访问父类当中的成员,绝对不可以访问子类当中的成员;
3,可以直接访问被子类覆盖的同名成员;
4,特殊的同名函数:
1,子类中可以重定义父类中已经存在的成员函数;
2,这种重定义发生在继承中,叫做函数重写;
3,函数重写是同名覆盖的一种特殊情况;

1,子类当中定义的函数原型和父类当中定义的函数原型一模一样,这叫函数重写;
2,意味着在子类当中重新实现了父类当中的函数;
3,有必要重写实现吗?
1,有必要,如果定义了子类对象,调用了 print() 后,显然的期望打印的是 "I'm Child",证明自己是子类对象,如果打印的是 "I'm Paretn."就会很奇怪,明明是子类对象,但是却打印父类信息;
2,所以这个函数到了子类当中必须重写,必须重定义,不能再用父类中的函数版本了;
4,以后这种情况统一叫做“函数重写”;
5,思考:
1,当函数重写遇上赋值兼容会发生什么?
1,只能是退化为父类指针或引用;
6,赋值兼容的问题编程实验:
#include <iostream>
#include <string> using namespace std; class Parent
{
public:
int mi; void add(int i)
{
mi += i;
} void add(int a, int b)
{
mi += (a + b);
} void print()
{
cout << "I'm Parent." << endl;
}
}; class Child : public Parent
{
public:
int mv; void add(int x, int y, int z)
{
mv += (x + y + z);
} void print()
{
cout << "I'm Child." << endl;
}
}; void how_to_print(Parent* p)
{
p->print(); // 当程序编译到这里了,需要通过 p 指针调用 print() 函数,编译器不知道参数 p 指针指向的是父类对象还是子类对象,因为程序还没运行,实参数还没传进来;于是编译器这样假设认为:根据赋值兼容性原则,p 指针所指向的都是父类对象;
} int main()
{
Parent p;
Child c; p.print(); // 打印 I'm Parent.
c.print(); // 当子类中不重写 print() 函数时,打印 I'm Parent.;当子类中重写 print() 函数时,打印 I'm Child.;因此重写很有必要; how_to_print(&p); // Expected to print: I'm Parent. 打印 I'm Parent;
how_to_print(&c); // Expected to print: I'm Child. 打印 I'm Parent; return ;
}
7,父子间的赋值兼容:
1,问题分析:
1,编译期间,编译器只能根据指针的类型判断所指向的对象;
2,根据赋值兼容,编译器认为父类指针指向的是父类对象;
3,因此,编译结果只可能是调用父类中定义的同名函数;
2,实例分析:
void how_to_print(Parent* p)
{
P->print();
}
1,在编译这个函数的时候,编译器不可能知道指针 p 究竟指向了什么,但是编译器没有理由报错(因为单看这个函数语法实现没有错误);于是,编译器认为最安全的做法是调用父类的 print 函数,如果运行时实参是子类对象,根据赋值兼容性原则,将 p->print() 函数解释为父类函数也是合理的,父类和子类肯定都有相同的 print 函数;
3,编译器的处理方法是合理的吗?是期望的吗?
1,同名覆盖带来的问题,当子类重写了父类的某一个函数时,就有可能带来这样的问题;
2,这种处理方法是合理的;
3,这种结果不是我们期望的,之所以重写父类中的函数,是因为父类中的 函数版本不能满足我们的需求了,所以我们重写了父类中的版本,但是 运行的时候还是调用父类中实现的函数,显然不是我们期望的结果;
8,小结:
1,子类对象可以当作父类对象使用(赋值兼容);
1,赋值或初始化,指针或引用;
2,父类指针可以正确的指向子类对象;
3,父类引用可以正确的代表子类对象;
4,子类中可以重写父类中的成员函数;
C++中的赋值兼容性和重写的更多相关文章
- 【Java EE 学习 70 下】【数据采集系统第二天】【Action中User注入】【设计调查页面】【Action中模型赋值问题】【编辑调查】
一.Action中User注入问题 Action中可能会经常用到已经登陆的User对象,如果每次都从Session中拿会显得非常繁琐.可以想一种方法,当Action想要获取User对象的时候直接使用, ...
- js中setAttribute 的兼容性
js中setAttribute 的兼容性class和className兼容方法: object.setAttribute("class","content") ...
- C++ 抽象类一(多继承与赋值兼容性原则)
//多继承与赋值兼容性原则 #include<iostream> using namespace std; class Point{ public: Point(){ a = ; b = ...
- 【转】Python中的赋值、浅拷贝、深拷贝介绍
这篇文章主要介绍了Python中的赋值.浅拷贝.深拷贝介绍,Python中也分为简单赋值.浅拷贝.深拷贝这几种"拷贝"方式,需要的朋友可以参考下 和很多语言一样,Python中 ...
- python中的赋值操作和复制操作
之前一直写C#,变量之间赋值相当于拷贝,修改拷贝变量不会改变原来的值.但是在python中发现赋值操作本质是和C++中的引用类似,即指向同一块内存空间.下面通过一个例子说明: p=[0,1,2,3,4 ...
- python中的赋值操作
参考:https://www.cnblogs.com/andywenzhi/p/7453374.html?tdsourcetag=s_pcqq_aiomsg(写的蛮好) python中的赋值操作“=” ...
- php中对象赋值问题
今天遇到一个问题, 一开始拼接的SQL语句,然后想多次使用时发现会被重置,然后想到给重新赋值一次,但是发现这样赋值会出问题,百思不得其解,最后经过搜索,发现PHP中对象赋值给一个变量之类的赋值的其实是 ...
- (网页)Angular.js 中 copy 赋值与 = 赋值 区别
转自st.gg Angular.js 中 copy 赋值与 = 赋值 区别 为什么用 $scope.user = $scope.master; $scope.master 会跟着 $scope.use ...
- ajax实现给JavaScript中全局变量赋值(转)
原文地址:ajax实现给JavaScript中全局变量赋值 问题简化: <script type="text/javascript"> var a=1 ; functi ...
随机推荐
- ADC(简易的DMA传输)的认识
ADC(简易的DMA传输)的认识 首先看到是ADC的特性 1.ADC的12位分辨率.不能直接测量负电压,然后是最小量程化单位是LSB=Vref+/212 2.单次和转换模式的使用 3. 从通道0到通道 ...
- Java 8实战之读书笔记五:超越Java 8
四.超越Java 8 第13章 函数式的思考 下面是这一章中你应该掌握的关键概念. 从长远看,减少共享的可变数据结构能帮助你降低维护和调试程序的代价. 函数式编程支持无副作用的 ...
- Python中字符串的格式化
字符串的格式化 格式化是对字符串进行格式表示的方式.使用槽({})按顺序格式化字符串. 格式化方式 <模板字符串>.format(<逗号分割参数>) "{ }:计算机 ...
- rabbitmq windows安装 及 centos安装
windows安装如下: 安装方法如下网址: https://baijiahao.baidu.com/s?id=1605656085633071281&wfr=spider&for= ...
- smbmnt - 装载 SMB 文件系统的协助工具
总览 smbmnt mount-point [ -s share ] [ -r ] [ -u uid ] [ -g gid ] [ -f mask ] [ -d mask ] 描述 smbmnt 用于 ...
- Tomcat架构与原理
Tomcat架构与原理 架构图 原理 ①.用户点击网页内容,请求被发送到本机端口8080,被在那里监听的Coyote HTTP/1.1 Connector获得. ②.Connector把该请求交给它所 ...
- Djano中static和media文件路径的设置
对于常用的css.js.image和常用的工具类在django项目中要设置一个全局的路径,对所有的app都可以访问到这个路径下的文件 1在django项目的setting文件中设置对应的static和 ...
- mysql小知识
char(10): 换行符 char(13): 回车符 UPDATE tablename SET field = REPLACE(REPLACE(field, CHAR(10), ”), CHAR(1 ...
- 1、pip不是内部运行程序 解决方法
一.方式一 1.切换到pip所在路径: shit+ 右键. 再此处打开运行窗口 2.执行 pip install pytest 脚本即可. 二.方式二,添加环境变量 1.将pip所在的文件路径 添加到 ...
- Cytoscape软件简介
• Cytoscape一款开源的网络显示和分析软件. 软件的核心部分提供了 网络显示.布局.查询等方面的基本功能. • Cytoscape源自系统生物学,通过Cytoscape,用户可以在可视化的 环 ...