C/C++复习 day2(模板,继承,多态)

news2024/11/15 10:00:14

C/C++复习 day2


文章目录

  • C/C++复习 day2
  • 前言
  • 一、模板
    • 1.模板的原理
    • 2.非类型模板参数
    • 3.模板的特化
      • a. 函数模板的特化
      • b. 类模板的特化
        • 1.全特化
        • 2.偏特化
    • 4.模板的分离编译
  • 二、继承
    • 1.继承的概念
    • 2.继承与派生类对象赋值转化
    • 3.隐藏
      • 1.成员变量的隐藏
      • 2. 成员函数的隐藏
    • 4.继承中的友元
    • 5.继承与静态变量
    • 6.多继承
      • 1.菱形继承
      • 2.菱形虚拟继承
    • 7.继承和组合
  • 三、多态
    • 1.什么是多态?
      • 1.静态多态(绑定)
      • 2.动态多态(继承中的多态,动态绑定)
        • 构成的条件
    • 2.虚函数
      • 1.虚函数的重写
      • 2.虚函数重写的意外
        • a.协变
        • b.析构函数的重写
      • 3.final和override
        • a.final
        • b.override
      • 4.重载,重写(覆盖),隐藏(重定义)
    • 3.抽象类
    • 4.多态的原理
    • 5.多态总结
  • 总结


前言

C/C++复习day02


一、模板

虽然模板的一些功能函数重载也可完成,但是。

  1. 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函
  2. 代码的可维护性比较低,一个出错可能所有的重载均出错。

为了实现泛型编程,因此引入模板。

1.模板的原理

在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的参数类型来推演生成对应类型的函数。
也就是说,函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。所以其实模板就是将本来应该我们做的重复的事情交给了编译器。

2.非类型模板参数

即在模板参数列表中,用一个常量作为类模板参数的值。

template<class T, size_t N = 10>
 class array
 {
 public:
 T& operator[](size_t index){return _array[index];}
 const T& operator[](size_t index)const{return _array[index];}
 
 size_t size()const{return _size;}
 bool empty()const{return 0 == _size;}
 
 private:
 T _array[N];
 size_t _size;
 }

注意

  1. 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
  2. 非类型的模板参数必须在编译期就能确认结果。

3.模板的特化

template<class T>
bool Less(T left, T right)
{
 return left < right;
}

比如这段代码,我们定义了一个比较函数。
当我们正常传值时会正常比较,但是如果我们传的是地址呢?
这个函数就会按照地址大小去比较,但我们希望按值去比。
因此,我们就需要对这些进行一个特殊处理。

a. 函数模板的特化

  1. 必须要先有一个基础的函数模板
  2. 关键字template后面接一对空的尖括号<>
  3. 函数名后跟一对尖括号,尖括号中指定需要特化的类型
  4. 函数形参表: 必须要和模板函数的基础参数类型完全相同,如果不同编译器可能会报一些奇怪的错误。
template<class T>
bool Less(T left, T right)
{
 return left < right;
}
template<>
bool Less<int*>(int* a,int* b)
{
	return *a<*b; 
}

b. 类模板的特化

1.全特化

即将类模板参数列表的所有参数都确定化。

template<class T1, class T2>
class Data
{
public:
 	Data() {cout<<"Data<T1, T2>" <<endl;}
private:
 	T1 _d1;
	 T2 _d2;
};
template<>
class Data<int, char>
{
public:
 	Data() {cout<<"Data<int, char>" <<endl;}
private:
 	int _d1;
 	char _d2;
}
2.偏特化

将第二个模板特化成int

template <class T1>
class Data<T1, int>
{
public:
 	Data() {cout<<"Data<T1, int>" <<endl;}
private:
	 T1 _d1;
 	int _d2;
};

除此之外还可进行进一步的限制,在此不过多赘述。

4.模板的分离编译

// a.h
template<class T>
T Add(const T& left, const T& right);

// a.cpp
template<class T>
T Add(const T& left, const T& right)
{
 return left + right;
}

// main.cpp
#include"a.h"
int main()
{
 Add(1, 2);
 Add(1.0, 2.0);

 return 0;
}

以上场景,模板函数定义写在.h头文件中,实现写在a.cpp文件中,而调用又在另一文件中,这样就会出现问题,编译报错。

解决的方法:最好是将其声明和定义写到一个文件中(xxx.hpp或者xxx.h)

二、继承

1.继承的概念

继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。
在这里插入图片描述

2.继承与派生类对象赋值转化

转化规则:

  1. 派生类对象可以赋值给基类的对象/指针/引用。这里有个形象的说法称为切片,比喻将派生类中父类的那一部分切出来,赋值给基类。
  2. 基类对象不能赋值给派生类。
  3. 基类的指针或者引用可以通过强制类型转化赋值给派生类的指针或者引用。但基类的指针或者引用必须是指向派生类对象才可以这样处理。
    可以使用RTTI(Run Time Type Information)的dynamic_cast识别后进行安全转化。

3.隐藏

1.成员变量的隐藏

子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)

class Person
{
protected :
 	string _name = "小李子"; // 姓名
 	int _num = 111;   // 身份证号
};
class Student : public Person
{
public:
 	void Print()
	 {
	 cout<<" 姓名:"<<_name<< endl;
	 cout<<" 身份证号:"<<Person::_num<< endl;
	 cout<<" 学号:"<<_num<<endl;
 }
protected:
 	int _num = 999; // 学号
};

这里Person类和Student类中 _num变量构成了隐藏。

2. 成员函数的隐藏

基类和子类中成员函数,只要函数名相同,并且不构成重写,则就构成隐藏。

B中的fun和A中的fun不是构成重载,因为不是在同一作用域
B中的fun和A中的fun构成隐藏,成员函数满足函数名相同就构成隐藏。
class A
{
public:
	 void fun()
	 {
	 cout << "func()" << endl;
	 }
};
class B : public A
{
public:
	 void fun(int i)
	 {
	 A::fun();
	 cout << "func(int i)->" <<i<<endl;
	 }
};

4.继承中的友元

  1. 友元关系不可被继承:基类的友元函数不能直接访问派生类的私有和保护成员。
    例如,如果 class A 有一个友元函数 friend void func(A& a) ,那么这个友元函数不能直接访问从 A 派生的 class B 的私有和保护成员。
  2. 友元函数不能被派生类继承为友元关系:派生类不能自动继承基类友元的友元关系。
    假设 func 是 A 的友元,对于派生类 B 来说,func 不是 B 的友元。
  3. 友元关系的非传递性:如果 class C 是 class B 的友元,class B 是 class A 的友元,不能得出 class C 是 class A 的友元。
    这种特点保证了友元关系的明确性和安全性,防止了不必要的访问权限扩散。

5.继承与静态变量

如果基类中定义了一个静态变量,则整个继承体系里面只有一个这样的成员。无论派生出多少子类,都只有一个static成员实例。

6.多继承

在现实生活中,例如一位学生,他同时具有人的属性,也具有学生的属性。因此这名学生最起码有两个的直接父类。因此祖师爷引入了多继承这个概念来更好的面向对象编程。
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承。
在这里插入图片描述

1.菱形继承

在这里插入图片描述
如上图所示,Assistant的对象中会出现两份Person成员。
这就说明了菱形继承的两个问题:数据冗余和二义性

class Person
{
public :
 	string _name ; // 姓名
};
class Student : public Person
{
	protected :
	 int _num ; //学号
};
class Teacher : public Person
{
	protected :
	 int _id ; // 职工编号
};
class Assistant : public Student, public Teacher
{
	protected :
	 string _majorCourse ; // 主修课程
};
void Test ()
{
 // 这样会有二义性无法明确知道访问的是哪一个
	 Assistant a ;
	 a._name = "peter";
// 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
	 a.Student::_name = "xxx";
	 a.Teacher::_name = "yyy";
}

由此可知,二义性我们可以通过显式指定访问来完成,但是数据冗余我们无法解决。

2.菱形虚拟继承

为了解决数据冗余问题,引入了菱形虚拟继承。
例如类A,B,C,D。B,C继承了A,D继承了B,C。
我们可以通过调试来看出在这里插入图片描述
上图是菱形虚拟继承的内存对象成员模型:这里可以分析出D对象中将A放到的了对象组成的最下面,这个A同时属于B和C。
那么B和C如何去找到公共的A呢?
这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的是偏移量。通过偏移量可以找到下面的A。

7.继承和组合

  1. public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
    组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。
  2. 优先使用对象组合,而不是类继承 。
  3. 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称
    为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的
    内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很
    大的影响。派生类和基类间的依赖关系很强,耦合度高。
  4. 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
    来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复
    用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。
    组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
    封装。
  5. 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
    些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
    继承,可以用组合,就用组合。

三、多态

1.什么是多态?

1.静态多态(绑定)

静态多态即为函数重载。
底层是编译时通过在函数名添加参数类型来识别不同的函数重载。

2.动态多态(继承中的多态,动态绑定)

构成的条件
  1. 必须通过基类的指针或者引用调用虚函数。
  2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

2.虚函数

虚函数:即被virtual关键字修饰的函数即为虚函数。

class Person {
public:
	 virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

1.虚函数的重写

虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了基类的虚函数。

class Person {
public:
	 virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
	 virtual void BuyTicket() { cout << "买票-半价" << endl; }
};

注意:派生类中的虚函数在重写时可以不加virtual关键字。但这种写法不太规范,不建议使用。

2.虚函数重写的意外

a.协变

当两个函数构成虚函数时,并且基类与子类的返回值类型分别对应一个基类和子类的指针或引用时,就构成了协变。

class A{};

class B : public A {};

class Person {
public:
	 virtual A* f() {return new A;}
};

class Student : public Person {
public:
	 virtual B* f() {return new B;}
};
b.析构函数的重写

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同。
虽然看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

class Person {
public:
	 virtual ~Person() {cout << "~Person()" << endl;}
};

class Student : public Person {
public:
	 virtual ~Student() { cout << "~Student()" << endl; }
};

3.final和override

a.final

final修饰虚函数,表示该虚函数不能被重写。

class Car
{
public:
 	virtual void Drive() final {}
};

class Benz :public Car
{
public:
 	virtual void Drive() {cout << "Benz-舒适" << endl;}
};

例如这样就会出现问题。

b.override

override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car{
public:
 	virtual void Drive(){}
};

class Benz :public Car {
public:
 	virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

4.重载,重写(覆盖),隐藏(重定义)

在这里插入图片描述

3.抽象类

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。
派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写,另外纯虚函数更体现出了接口继承。

class Car
{
public:
	virtual void Drive() = 0;
};

class Benz :public Car
{
public:
	 virtual void Drive()
	 {
	 cout << "Benz-舒适" << endl;
	 }
};

class BMW :public Car
{
public:
	 virtual void Drive()
	 {
	 cout << "BMW-操控" << endl;
	 }
};

4.多态的原理

虚函数表
总结:是通过存放虚函数表来实现的。
虚函数表是在编译阶段生成的,一般情况下存放在代码段(常量区)。

5.多态总结

内联函数不能是虚函数。
一个类中的不同对象共用同一张虚表。

总结

以上就是我的C/C++day2总结。
本人小白一枚,有问题还望各位大佬指正。

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

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

相关文章

数据结构:栈(含源码)

目录 一、栈的概念和结构 二、栈的实现 2.1 头文件 2.2 各个功能的实现 初始化栈 入栈 出栈 获取栈顶元素和栈中有效个数 判断栈是否为空 栈的销毁 2.3 测试 完整源码 一、栈的概念和结构 栈&#xff1a;一种特殊的线性表&#xff0c;其只允许在固定的一端进行插入和…

[C++][opencv]基于opencv实现photoshop算法图像剪切

【测试环境】 vs2019 opencv4.8.0 【效果演示】 【核心实现代码】 //图像剪切 //参数&#xff1a;src为源图像&#xff0c; dst为结果图像, rect为剪切区域 //返回值&#xff1a;返回0表示成功&#xff0c;否则返回错误代码 int imageCrop(InputArray src, OutputArray dst,…

遥感影像-语义分割数据集:sar水体数据集详细介绍及训练样本处理流程

原始数据集详情 简介&#xff1a;该数据集由WHU-OPT-SAR数据集整理而来&#xff0c;覆盖面积51448.56公里&#xff0c;分辨率为5米。据我们所知&#xff0c;WHU-OPT-SAR是第一个也是最大的土地利用分类数据集&#xff0c;它融合了高分辨率光学和SAR图像&#xff0c;并进行了充…

Chromium编译指南2024 -Android篇:安装其他常用软件(三)

1.引言 在前面的章节中&#xff0c;我们详细讲解了编译 Chromium for Android 所需的系统和硬件要求&#xff0c;并介绍了如何配置开发环境&#xff0c;包括更改软件源和安装基本依赖。在完成这些基础配置之后&#xff0c;为了进一步提升开发和编译效率&#xff0c;您可能还需…

【Hot100】LeetCode—438. 找到字符串中所有字母异位词

目录 1- 思路哈希表 滑动窗口 2- 实现⭐438. 找到字符串中所有字母异位词——题解思路 3- ACM 实现 原题链接&#xff1a;438. 找到字符串中所有字母异位词 1- 思路 哈希表 滑动窗口 思路 哈希表&#xff1a;通过数组维护一个哈希表滑动窗口&#xff1a;通过控制数组的下标…

为何说本届巴黎奥运会中国金牌榜应排列第一?

为何说本届巴黎奥运会中国金牌榜应排列第一&#xff1f; 在奥运会上&#xff0c;金牌榜的排名一直是各国关注的焦点。然而&#xff0c;在历届奥运会中&#xff0c;关于金牌榜的统计方法和排名标准却存在一定的争议。尤其在中美两国之间&#xff0c;金牌榜的排名往往成为双方媒体…

制作好的excel报表设置打开密码或忘记密码怎么办?

excel工作表经常用来做数据统计、工资、报表等的文件格式&#xff0c;这些类型的文件都是很重要的数据资料&#xff0c;为此做这些数据的朋友们都会给他设置一个打开密码&#xff0c;不让其他人随便打开。但随着时间的流逝&#xff0c;我们做的数据报表越来越多了&#xff0c;做…

transformer(李宏毅老师系列)

自学参考&#xff1a; Transformer:Attention Is All You Need Transformer论文逐段精读 视频课 课件资料 笔记 一、引入 seq2seq&#xff1a;输入一个序列的向量作为input&#xff0c;output的长度由机器自己决定seq2seq model应用: 语音辨识 输入是声音讯号的一串vector 输出…

提高清晰度的全彩LED显示屏的关键要素

全彩LED显示屏作为现代广告宣传和信息传播的主要媒介&#xff0c;其清晰度在很大程度上决定了观众的视觉体验和信息传达的效果。随着人们对高清显示需求的不断提升&#xff0c;全彩LED显示屏也在向更高清、更细腻的显示效果迈进。那么&#xff0c;如何进一步提升全彩LED显示屏的…

6数字基石:掌握计算机语言、多媒体与系统工程

计算机语言 计算机语言是指用于人与计算机之间交流的一种语言&#xff0c;是人与计算机之间传递信息的媒介。计算机语言主要由一套指令组成&#xff0c;而这一种指令一般包括表达式、流程控制和集合三大部分内容。 表达式又包含变量、常量、字面量和运算符。 流程控制有分支…

善用 AI ,优化项目,保姆级简历写作指南第七弹

大家好&#xff0c;我是程序员鱼皮。做知识分享这些年来&#xff0c;我看过太多简历、也帮忙修改过很多的简历&#xff0c;发现很多同学是完全不会写简历的、会犯很多常见的问题&#xff0c;不能把自己的优势充分展示出来&#xff0c;导致错失了很多面试机会&#xff0c;实在是…

如何将TRIZ的“最终理想解”应用到机器人电机控制设计中?

TRIZ理论&#xff0c;作为一套系统的创新方法论&#xff0c;旨在帮助设计师和工程师突破思维惯性&#xff0c;解决复杂的技术难题。其核心思想之一便是“最终理想解”&#xff0c;它如同一盏明灯&#xff0c;指引着我们在技术创新的道路上不断前行。最终理想解追求的是产品或技…

“听到“温度 - 科学家发现人类感知的新层次

雷克曼大学&#xff08;IDC Herzliya&#xff09;伊夫切尔大脑、认知与技术研究所&#xff08;BCT Institute&#xff09;的研究人员发现了一种在很大程度上被忽视的感知能力&#xff0c;他们利用机器学习揭示了跨模态感知–不同感官模态之间的相互作用–的动态。在最近的一项研…

【HarmonyOS NEXT星河版开发学习】小型测试案例06-小红书卡片

个人主页→VON 收录专栏→鸿蒙开发小型案例总结​​​​​ 基础语法部分会发布于github 和 gitee上面&#xff08;暂未发布&#xff09; 前言 在鸿蒙&#xff08;HarmonyOS&#xff09;开发中&#xff0c;自适应伸缩是指应用程序能够根据不同设备的屏幕尺寸、分辨率和形态&…

2-63 基于matlab的GMPHD滤波器算法

基于matlab的GMPHD滤波器算法&#xff08;1&#xff09;本次仿真采用线性CV模型&#xff1b;&#xff08;2&#xff09;观测模型为线性条件下&#xff0c;观测值为X&#xff0c;Y轴坐标&#xff1b;&#xff08;3&#xff09;验证GMPHD算法对多目标跟踪的有效性&#xff1b;输出…

对于产品设计方面来说,3D 技术的应用有哪些优势?

3D技术在产品设计方面提供了许多优势&#xff0c;主要体现在以下几个方面&#xff1a; 1、可视化&#xff1a;设计师利用3D技术创建产品三维模型&#xff0c;使得产品在设计阶段就能被可视化&#xff0c;帮助团队更好地理解产品的外观和功能。 2、精确性&#xff1a;3D模型可…

人人都能搞定的大模型原理 - 神经网络

人工智能的发展起步于1950年&#xff0c;期间经历了各种里程碑和变革&#xff0c;与此相关的神经网络技术也从最初的单层感知到复杂的层级和卷积神经网络一路创新和变革&#xff0c;不断推动人工智能领域的发展&#xff0c;直到 2022 年 ChatGPT 的问世&#xff0c;彻底引爆了…

Leetcode174.地下城游戏

题目 代码&#xff08;首刷看解析 2024年5月6日&#xff09; class Solution { public:// 动态规划int calculateMinimumHP(vector<vector<int>>& dungeon) {// dp[i][j]从(i,j)出发&#xff0c;到达终点所需要的最少血量int m dungeon.size();int n dungeo…

【1.9】动态规划-解单词拆分

一、题目 给定一个非空字符串s和一个包含非空单词的列表wordDict&#xff0c;判定s是否可以被空格拆分为一个或多个在字典中出现的单词。 说明&#xff1a; 1. 拆分时可以重复使用字典中的单词。 2. 你可以假设字典中没有重复的单词。 示例1&#xff1a; 示例2&#xff1a;…

SpringBoot优雅开发REST API最佳实践

目录 RestController注解 接口版本管理 定义版本号注解 编写版本号匹配逻辑处理器 注册处理器 参数校验 Validated注解 使用注解进行参数校验 统一异常捕获 RestControllerAdvice注解 使用RestControllerAdvice注解处理参数异常 统一响应封装 统一状态码 统一返回结…