继承
单继承下基类与派生类之间转化
-
派生类适应于基类,派生类的对象能直接作用于基类对象所能应用的场合
-
派生类对象赋值给基类对象
-
基类引用绑定到派生类对象
-
基类的指针指向派生类对象
-
向上转型:安全
(派生类转为基类,上一辈)
-
向下转型:很不安全,C++不支持,只能强转
(基类转为派生类,下一辈)
//不安全的向下转型 Derived *pderived1 = static_cast<Derived *>(&base1); //安全的向下转型 Base *pbase2 = &derived1; Derived *pderived2 = static_cast<Derived *>(pbase2);
-
代码示例:
Base base(11.11);
Derived derived(11.11,22,22);
//base.operator=(derived);
//Base &operator=(const Base &rhs)
//const Base &rhs=derived;
base = derived;
Base &ref = derived;
Base *pbase = &derived;//引用实际实际是指针
内存示例:
派生类16字节,派生类指针可以操纵16个字节,如果转为基类会越界
基类8字节,基类指针可以操纵8个字节,如果转为派生类可以对应部分
多基继承(多基派生)
-
默认继承方式私有,派生类继承基类时,每个基类前都要写
-
派生类创建对象时,基类的初始化顺序
-
首先要执行所有基类的构造函数,再执行派生类构造函数中初始化表达式的其他内容和构造函数体
-
基类构造函数的执行顺序与其在初始化表中的顺序无关,只与派生类定义时,基类被(继承)声明顺序有关
-
-
二义性问题与解决方案
-
成员函数访问冲突——类名加作用域限定符
-
存储二义性——虚拟继承
(基类继承前面加上virtual,在对象的数据成员存储中会多一个虚基指针)
//地址不同,所以吸收过来存储的位置不同,是多个不同的数据成员
-
派生类对象间的复制控制
-
拷贝构造或者赋值运算符函数情况:派生类+基类→
-
无显式定义,有显式定义
- 那么在用一个派生类对象初始化新的派生类对象时,两对象间的派生类部分执行缺省的行为,而两对象间的基类部分执行用户定义的基类拷贝构造函数
- 那么在用一个派生类对象给另一个已经存在的派生类对象赋值时,两对象间的派生类部分执行缺省的赋值行为,而两对象间的基类部分执行用户定义的重载赋值函数
-
有显式定义
- 在用已有派生类对象初始化新的派生类对象时,或者在派生类对象间赋值时,将会执行用户定义的派生类的拷贝构造函数或者重载赋值函数,而不会再自动调用基类的拷贝构造函数和基类的重载对象赋值运算符,这时,通常需要用户在派生类的拷贝构造函数或者派生类的赋值函数中显式调用基类的拷贝构造或赋值运算符函数
-
set/map
集合
- 关键字唯一
- 默认情况下按照关键字升序排列
#include <set>
using namespace std;
//初始化1
set<int> number={1,2,3};
//初始化2
int a[10]={1,2,3,4};
set<int> number(a,a+10);
//遍历1
for(auto &p:number)
cout<<p<<" ";
//遍历2
set<int>::iterator it;//迭代器可抽象的认为是一个指针
for(it=number.begin();it!=number.end();++it)
cout<<*it<<" ";
//查找
number.count(key);//返回匹配的个数
number.find(key);//返回迭代器,与number.end()比较是否存在
//插入
auto ret=number.insert(key);//返回pair一个是迭代器,一个是 bool类型
number.insert(a,a+10);//返回void
//没有重载[],不支持下标访问
//set不能通过*it进行修改,只读,底层实现是红黑树
//可以先删除再插入
映射(键值对,高级的set)
- 键唯一,值可不同
- 默认按键升序排列
#include <map>
using namespace std;
//初始化
map<string,string> mp={
{"021","上海"},{"027","武汉"}
std::pair<string,string>("030","南京")};
//遍历
for(auto &p:mp)
cout<<p.first<<" "<<p.second<<endl;
//查找
auto it=mp.find(key);
if(it==mp.end())
cout<<"not exist"<<endl;
else
cout<<it->first<<" "<<it->second<<endl;
//插入
auto it=mp.insert({"090","长沙"});
pair<map<string,string>::iterator,bool>ret
=mp.insert(pair<string,string>("090","长沙"));
if(ret.second){
cout<<"success "
<<ret.first->first<<" "
<<ret.first->second<<endl;
}else{
cout<<"failed"<<endl;
}
//重载了[]可以通过下标操作
mp[key]=value;
pair
//标准库中头文件在utility,map、set也包含它
std::pair<int,string> number={123,"wuhan"};
cout<<number.first<<number.second<<endl;
tuple
- 元组
红黑树
它具有以下 5个属性:
- 节点是红色或黑色。
- 根节点是黑色。
- 所有叶子都是黑色。(叶子是 NULL节点)
- 每个红色节点的两个子节点都是黑色。
- 从任一节点到其每个叶子的所有路径都包含相同数目的黑色节点
多态
-
同一指令,针对不同对象,产生不同行为
(一个接口多种方法)
-
多态除了代码的复用性外,还可以解决项目中紧耦合的问题,提高程序的可扩展性
基本概念
-
C++支持两种多态性:编译时多态和运行时多态
-
编译时多态:也称为静态多态,函数重载、运算符重载、模板都是采用的静态多态,在编译时发生,根据传递给函数的参数和函数名决定具体要使用哪一个函数
-
运行时多态:在程序运行时完成选择,通过虚函数来实现
(虚函数不等于多态,不满足多态激活条件)
虚函数
- 虚函数就是在基类中被声明为virtual,并在一个或多个派生类中被重新定义的成员函数
- 如果一个基类的成员函数为虚函数,它的所有派生类中也保持为虚函数,即使省略virtual关键字
//类内部
class 类名
{
virtual 返回类型 函数名(参数列表)
{
//...
}
};
//类之外
virtual 返回类型 类名::函数名(参数列表)
{
//...
}
派生类重定义的格式
-
函数同名
-
返回值类型相同
-
函数参数列表完全相同
-
隐藏、覆盖、重载
动态多态实现机制
- 当基类定义了虚函数之后,就会在该类的对象的存储布局的前面产生一个虚函数指针,该虚函数指针用来指向一张虚函数表,该虚表存放的是基类虚函数的入口地址
- 当派生类继承基类的时候,会吸收基类的虚函数,然后在派生类所创建的对象的存储布局之中产生派生类的虚函数指针,该虚函数指针指向派生类的虚表,该虚表存放的是派生类虚函数的入口地址(有重定义)
- 如果没有多态virtual就没有虚表
动态多态被激活的条件
-
基类要定义虚函数
-
派生类重定义(重写、覆盖)虚函数
-
创建派生类对象
-
基类的指针(引用)指向(绑定)到派生类对象
(函数调用形参结合时)
-
使用基类指针(引用)调用虚函数
无法定义为虚函数
-
普通函数
(全局,发生在编译)
-
静态成员函数
(this指针,被共享,发生在编译)
-
内联函数
(发生在编译,加上virtual之后编译器会不再视为内联函数)
-
友元函数
(普通友元函数不行,成员友元函数可以)
-
构造函数
(没有对象,虚函数可以被继承构造函数不可被继承)
今天的文章linux编写c++程序_linux系统编程第二版pdf分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/89146.html