C++ 重载运算符 继承 多态 (超详细)
(一)重载运算符:
(1)声明与定义格式
一般是类内声明,类外定义,虽然可以在类内定义,但 写前面堆一堆不好看!!!
类内声明:
class Demo
{
返回值类型 operator 运算符(形参表);
}
类外定义:
返回类型 Demo(类名)::operator运算符(形参表)
{
函数体
}
(2)双目运算符重载为成员函数
当重载运算符为双目运算符时,形参表中只有一个参数作为右操作数。当前对象作为左操作数,通过this指针隐式传递给函数,一个例子来介绍。
实例:
写到最后突然想起来,用int不能实现浮点数的全部特性0.03就不能实现,所以仅作为一个例子。
class Myfloat
{
int inter;
int deci;
public:
Myfloat(int a,int b):inter(a),deci(b){}
Myfloat operator+(Myfloat const &temp) const;
Myfloat operator-(Myfloat const &temp) const;
};
Myfloat Myfloat::operator+(Myfloat const &temp) const
{
return Myfloat(inter+temp.inter,deci+temp.deci);
}
Myfloat Myfloat::operator-(Myfloat const &temp) const
{
return Myfloat(inter-temp.inter,deci-temp.deci);
}
现在只是重载了加减号,实现了自定义浮点数的运算,但是还不成熟,咱们一点一点来丰满这个代码,这个类。(3)单目运算符重载为成员函数
此时参数表中没有参数,只有当前对象作为运算符的一个操作数。
实例:
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
class Myfloat
{
int inter;
int deci;
public:
Myfloat(int a,int b):inter(a),deci(b){}
Myfloat operator+(Myfloat const &temp) const;
Myfloat operator-(Myfloat const &temp) const;
Myfloat operator--();
Myfloat operator++();
Myfloat operator--(int); //补充一个虚操作数,表示前置操作
Myfloat operator++(int);
};
Myfloat Myfloat::operator+(Myfloat const &temp) const
{
return Myfloat(inter+temp.inter,deci+temp.deci);
}
Myfloat Myfloat::operator-(Myfloat const &temp) const
{
return Myfloat(inter-temp.inter,deci-temp.deci);
}
Myfloat Myfloat::operator--() {return Myfloat(inter--,deci);}
Myfloat Myfloat::operator++() {return Myfloat(inter++,deci);}
Myfloat Myfloat::operator--(int) {return Myfloat(--inter,deci);}
要区分前置与后置运算要加一个(需操作数)告诉机器是前置还是后置。
(3) 友元函数重载+重载输入输出流(用的稀烂用的多比较重要)
在左右操作数类型不同时上述重载方式都不能正常使用,这时候就需要两个操作数,在类外重载,因类外不能直接调用,所以要把该函数声明为类的友元。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
class Myfloat
{
int inter;
int deci;
public:
Myfloat(int a,int b):inter(a),deci(b){}
Myfloat operator+(Myfloat const &temp) const;
Myfloat operator-(Myfloat const &temp) const;
Myfloat operator--();
Myfloat operator++();
Myfloat operator--(int); //补充一个虚操作数,表示前置操作
Myfloat operator++(int);
friend ostream& operator<<(ostream out,Myfloat &w) ;
friend istream &operator>>(istream in,Myfloat &w);
};
Myfloat Myfloat::operator+(Myfloat const &temp) const{return Myfloat(inter+temp.inter,deci+temp.deci);}
Myfloat Myfloat::operator-(Myfloat const &temp) const{return Myfloat(inter-temp.inter,deci-temp.deci);}
Myfloat Myfloat::operator--() {return Myfloat(inter--,deci);}
Myfloat Myfloat::operator++() {return Myfloat(inter++,deci);}
Myfloat Myfloat::operator--(int) {return Myfloat(--inter,deci);}
Myfloat Myfloat::operator++(int) {return Myfloat(++inter,deci);}
ostream& operator<<(ostream out,Myfloat &w)
{
out<<w.inter<<'.'<<w.deci;
}
istream &operator>>(istream in,Myfloat &w)
{
in>>w.inter>>w.deci;
}
(4)赋值运算符重载用于对象数据的复制
用非类A类型的值为类A的对象赋值时(当然,这种情况下我们可以不提供相应的赋值运算符重载函数,而只提供相应的构造函数,如更有重载函数会优先调用重载后的赋值运算符)。
当用类A类型的值为类A的对象赋值,且类A的数据成员中含有指针的情况下,必须显式提供赋值运算符重载函数。
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
class Myfloat
{
int inter;
int deci;
public:
Myfloat(int a,int b):inter(a),deci(b){}
Myfloat operator+(Myfloat const &temp) const;
Myfloat operator-(Myfloat const &temp) const;
Myfloat operator--();
Myfloat operator++();
Myfloat operator--(int); //补充一个虚操作数,表示前置操作
Myfloat operator++(int);
friend ostream& operator<<(ostream out,Myfloat &w) ;
friend istream &operator>>(istream in,Myfloat &w);
Myfloat& operator=(const Myfloat &temp ) ;//写或不写都可以,这种如果按照默认的方式一一对应复制,可以不写。
Myfloat& operator=(const int &w ) ;
};
Myfloat Myfloat::operator+(Myfloat const &temp) const{return Myfloat(inter+temp.inter,deci+temp.deci);}
Myfloat Myfloat::operator-(Myfloat const &temp) const{return Myfloat(inter-temp.inter,deci-temp.deci);}
Myfloat Myfloat::operator--() {return Myfloat(inter--,deci);}
Myfloat Myfloat::operator++() {return Myfloat(inter++,deci);}
Myfloat Myfloat::operator--(int) {return Myfloat(--inter,deci);}
Myfloat Myfloat::operator++(int) {return Myfloat(++inter,deci);}
ostream& operator<<(ostream out,Myfloat &w)
{
out<<w.inter<<'.'<<w.deci;
}
istream &operator>>(istream in,Myfloat &w)
{
in>>w.inter>>w.deci;
}
Myfloat& Myfloat::operator=(const Myfloat &temp)
{
inter=temp.inter;
deci=temp.deci;
return *this;
}
Myfloat& Myfloat::operator=(const int &w)
{
inter=w;
return *this;
}
(二)基类与派生类
(1)继承语法形式:
class 派生类名:基类名表
{
数据成员和成员函数声明
};
基类类名表构成: 访问控制 基类名1 访问控制 基类名2…
继承多各类时叫做多继承,容易产生二义性,一般不用。
访问控制有三种
public:公有继承
private:私有继承
protected:保护继承
实例
class People { }
class Student:public People
(2)派生类的生成过程
- 吸收基类成员:除构造和析构函数外
- 改造基类成员:通过在派生类中定义同名成员屏蔽基类成员在派生类中直接调用,仍可以基类指针调用同名成员
- .添加新成员
(3)派生类特点
- 子类拥有父类除了父类构造和析构函数,所有的成员函数和成员变量;
- 2.子类就是一种特殊的父类;
- 子类对象可以当做父类的对象使用;
- 子类可以拥有父类没有的方法和属性。
(4)派生类中的静态数据成员
基类中定义的静态成员,将被所有派生类共享
2、基类初始化:
(5)派生类的初始化
派生类构造函数声明格式为:
派生类构造函数(变元表):基类(变元表)、对象成员1(变元表)
构造函数执行顺序:基类——对象成员(类对象成员的初始化)——派生类
//一开始不理解,现在理解了
举个栗子:
class People
{
protected:
string name;
string xb;
public:
People(string a,string b):name(a),xb(b){}
};
class Cloth
{
string color;
int mysize;
public:
Cloth(string c,int m):color(c),mysize(m){}
};
class Student:public People
{
string id;
Cloth coat;
public:
Student(string id,string name,string xh,string color,int size) :People(name,xb),coat(color,size),id(id){}
};
执行顺序跟我写的顺序一样,但不是因为按这个顺序写的原因,就像成员变量初始化,也是按这定义顺序初始化,与自己写的初始化顺序无关。
构造函数的执行顺序:基类→对象成员→派生类;
(6)派生类构造函数和析构函数的使用原则
基类的构造函数和析构函数不能继承
派生类是否定义析构函数与所属基类无关
如果基类没有定义构造函数或是定义无参构造函数,派生类可以不定义构造函数。
如果基类无无参构造函数,派生类必须定义构造函数
如果派生类基类为连续基类继承,每个派生类只负责直接基类的构造
(7)派生类析构函数
与构造函数执行顺序相反,派生-----对象-----基类
(8)赋值兼容原则
这个规则可以简述为能放基类的地方,放派生类一定可以使用,在程序中需要使用基类对象的地方都可以用公有派生类的对象代替。
例:
class Base{};
class Drived{};
Base demo1,Drived demo2;
demo1=demo2; //派生类对象可以赋值给基类对象:
Base&Bdemo3=demo2; //派生类对象可以初始化基类引用;
Base *Bpo=&demo2;//派生类对象可以赋给指向基类对象的指针;//多态实现的方法
主要是派生类中一定包含基类中所有成员,在使用中,一定可以找到对应成员。
赋值兼容应注意的问题:
- 指向基类的指针可以指向公有派生类的对象,但不允许指向它的私有派生类的对象。
- 允许将一个声明为指向基类的指针指向其公有派生类对象,但是不能将一个声明为指向派生类对象的指针指向基类对象。
- 声明为指向基类对象的指针,当其指向公有派生类对象时,只能用它来直接访问派生类中从基类继承来的成员,而不能直接访问公有派生类的定义的成员。
可以理解为派生类完全包含基类,指向基类的任何成员,都可以在公有派生类中找到对应的成员对象与之对应,如果是私有继承,能找到但是不能访问。但是派生类中有的对象,基类中不一定会有,所以不能这么操作。
(三)虚函数与多态:
(1)多态的概念:
一个接口,多种使用方法
(2)封装的作用:
封装可以是得代码模块化;继承可以扩展已经存在的代码,都是为了代代码重用;
**(3)多态的目的:**接口重用
(4)静态联编和动态联编分别表示什么?
在编译的时候能够确定对象所调用的成员函数的地址则为静态联编,一般的调用方式;
动态联编:指的是在程序运行的时候动态地进行,根据当时的情况来确定调用哪个同名函数,父类指针指向哪个子类,就调用哪个子类的同名函数,实际上是在运行的时候虚函数的实现;
#include<iostream>
using namespace std;
class Basic
{
public:
void oper1()
{
printf("1\n");
}
virtual void oper2()
{
printf("2\n");
}
};
class Direved : public Basic
{
public:
void oper1()
{
printf("3\n");
}
void oper2()
{
printf("4\n");
}
};
int main(void)
{
Basic a;
Direved b;
Basic *p = &a; // 定义一个基类指针
p->oper1(); //基类的oper1
p->oper2(); //基类的oper2
p = &b; //基类指针指向了派生类
p->oper1(); //不是虚函数,仍时基类oper1
p->oper2(); //是虚函数构成多态,是派生类的oper2
return 0;
}
运行结果过如下,重点关注是否为虚函数时函数调用的区别。
构成虚函数的必要条件:
函数返回类型一样,参数表一样,函数名一样,同时需要关键字vitrual,缺一不可。
class Basic
{
vitrual void p(){ 函数体};
}
class Direved :public Basic //构成虚函数的多态
{
void p(){ 函数体};
}
class Basic
{
void p(){ 函数体};
}
class Direved :public Basic //不构成虚函数
{
void p(){ 函数体};
}
class Basic
{
vitrual void p(){ 函数体};
}
class Direved :public Basic //不构成虚函数
{
void p(int a){ 函数体};
}
class Basic
{
vitrual void p(){ 函数体};
}
class Direved :public Basic //不构成虚函数
{
double p(){ 函数体};
}
(5)C++纯虚函数
1.纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现纯虚函数的方法是在函数原型后加“=0”
virtual void funtion()=0
1、为了方便使用多态特性,我们常常需要在基类中定义虚拟函数。
2、在很多情况下,基类本身生成对象是不合情理的。例如,动物作为一个基类可以派生出老虎、孔雀等子类,但动物本身生成对象明显不合常理。
Class animals
{
virtual void mingjiao() ; //动物基类,到底是什么动物呢,该怎么鸣叫?当无法合理的给予基类定义虚函数时,常用纯虚函数。
{
}
}
为了解决上述问题,引入了纯虚函数的概念
Class animals
{
virtual void mingjiao()=0;
}
则编译器要求在派生类中必须予以重写以实现多态性。同时含有纯虚拟函数的类称为抽象类,它不能生成对象。这样就很好地解决了上述两个问题。
注意含有纯虚函数的类,不能直接声明对象!!!!!!!!
(6)多态性 实现的两种方式
a、编译时多态性:通过重载函数实现
b、运行时多态性:通过虚函数实现。
C++ 重载运算符 继承 多态 (超详细)的更多相关文章
- C++ //多态 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 //动态多态:派生类和虚函数实现运行时多态
1 //多态 2 //静态多态:函数重载 和 运算符重载 属于静态多态 ,复用函数名 3 //动态多态:派生类和虚函数实现运行时多态 4 5 //静态多态和动态多态的区别 6 //静态多态的函数地址早 ...
- php面向对象 封装继承多态 接口、重载、抽象类、最终类总结
1.面向对象 封装继承多态 接口.重载.抽象类.最终类 面向对象 封装继承多态 首先,在解释面向对象之前先解释下什么是面向对象? [面向对象]1.什么是类? 具有相同属性(特征)和方法(行为)的一 ...
- C++运算符重载总结(真的很详细了w(゚Д゚)w)
C++运算符重载总结(真的很详细了w(゚Д゚)w) 概述 运算符重载可以使得一些特殊类型参与运算,我是这样理解的. 使用友元形式的运算符重载 //一般形式 class x{ friend 返回类型 o ...
- 继承&多态
继承: 概念: 基类,超累,父类 访问权限: Public :无限制,自由访问 Private:不可继承 protected :包内,包外,可访问,继承 default:没有指明任何权限下,默认在同一 ...
- C++ 基础语法 快速复习笔记(3)---重载函数,多态,虚函数
1.重载运算符和重载函数: C++ 允许在同一作用域中的某个函数和运算符指定多个定义,分别称为函数重载和运算符重载. 重载声明是指一个与之前已经在该作用域内声明过的函数或方法具有相同名称的声明,但是它 ...
- Java中的三大特性 - 超详细篇
前言 大家好啊,我是汤圆,今天给大家带来的是<Java中的三大特性 - 超详细篇>,希望对大家有帮助,谢谢 这一节的内容可能有点多,大家可以选择性的来看 简介 Java的三大特性:封装.继 ...
- Java开发知识之Java的继承多态跟接口*
Java开发知识之Java的继承多态跟接口 一丶继承 1.继承的写法 在Java中继承的 关键字是 extends 代表一个类继承另一个类. 继承的含义以及作用: 继承就是基于某个父类的扩展.制定出来 ...
- ArrayList源码分析超详细(转载)
ArrayList源码分析超详细 ArrayList源码分析超详解 想要分析下源码是件好事,但是如何去进行分析呢?以我的例子来说,我进行源码分析的过程如下几步: 找到类:利用 IDEA 找到所需要 ...
- Java多线程学习(吐血超详细总结)
Java多线程学习(吐血超详细总结) 林炳文Evankaka原创作品.转载请注明出处http://blog.csdn.net/evankaka 写在前面的话:此文只能说是java多线程的一个入门,其实 ...
随机推荐
- 数据科学 R语言速成
文章更新于:2020-03-07 按照惯例,需要的文件附上链接放在文首: 文件名:R-3.6.2-win.exe 文件大小:82.4M 下载链接:https://www.lanzous.com/i9c ...
- 震撼!全网第一张源码分析全景图揭秘Nginx
不管是C/C++技术栈,还是PHP,Java技术栈,从事后端开发的朋友对nginx一定不会陌生. 想要深入学习nginx,阅读源码一定是非常重要的一环,但nginx源码量毕竟还是不算少,一不小心就容易 ...
- Spring 中 用 ${xxx} 读取properties文件的说明
properties 如果在 spring 中通过 PropertyPlaceholderConfigurer 加载,当spring 中需要 用到 properties 中的一些 key 和value ...
- 数据结构和算法(Golang实现)(6)简单入门Golang-并发、协程和信道
并发.协程和信道 Golang语言提供了go关键字,以及名为chan的数据类型,以及一些标准库的并发锁等,我们将会简单介绍一下并发的一些概念,然后学习这些Golang特征知识. 一.并发介绍 我们写程 ...
- [题解]P1449 后缀表达式(栈)
题目链接:P1449 后缀表达式 题目描述: 所谓后缀表达式是指这样的一个表达式:式中不再引用括号,运算符号放在两个运算对象之后,所有计算按运算符号出现的顺序,严格地由左而右新进行(不用考虑运算符的优 ...
- @ModelAttribute 的使用
@ModelAttribute注解可被应用在 方法 或 方法参数 上. 对方法使用 @ModelAttribute 注解: 注解在方法上的@ModelAttribute说明了方法的作用是用于添加一个或 ...
- 用python为喜欢的人写一个程序,每天发送贴心的消息
消息内容 包括如下: 日期(阳历+阴历): 每日壹句(内容来自爱词霸[1]): 天气预报(内容来自中国天气网[2]): 天气情况: 温度情况: 穿衣指数: 减肥指数: 空气指数: 紫外线指数: 消息效 ...
- winfrom 基础
1 winfrom就是一种窗体开发端应用程序 2 窗体分类 1)记事本类:可以最大最小化,可以拖拽 窗体默 ...
- 如何将dotnet core webapi发布到docker中…
如何将dotnet core webapi发布到docker中 今天想起来撸一下docker,中途还是遇到些问题,但是这些问题都是由于路径什么的导致不正确,在这儿还是记录下操作过程,今天是基于wind ...
- 使用User Agent和代理IP隐藏身份
一.为何要设置User Agent 有一些网站不喜欢被爬虫程序访问,所以会检测连接对象,如果是爬虫程序,也就是非人点击访问,它就会不让你继续访问,所以为了要让程序可以正常运行,需要隐藏自己的爬虫程序的 ...