C++面向对象程序设计-北京大学-郭炜【课程笔记(七)】

C++面向对象程序设计-北京大学-郭炜【课程笔记(七)】

  • 1、类型转换运算符
  • 2、自增、自减运算符的重载
  • 3、继承和派生的基本概念
    • 3.1、基本概念
    • 3.2、派生类对象的内存空间
  • 4、继承关系和复合关系
    • 4.1、继承关系的使用
    • 4.2、复合关系的使用
  • 5、派生类覆盖基类成员
  • 6、存储权限说明符:protected
  • 7、派生类的构造函数
  • 8、public继承的赋值兼容规则
  • 9、直接基类与间接基类(套娃)

毕业中:学习速度较慢
开始课程:P21 6_6. 类型转换运算符的重载
课程链接:程序设计与算法(三)C++面向对象程序设计 北京大学 郭炜
课程PPT:github提供的对应课程PPT

1、类型转换运算符

重载强制类型转换符 double 形式:operator double () {return real};
类型转换符重载是不需要写返回值类型的,默认其double自身。

案例:解释包含在代码中

#include <iostream>
using namespace std;class Complex
{double real, imag;public:Complex(double r=0, double i=0):real(r), imag(i) {};operator double () {return real;}// 重载强制类型转换运算符 double,返回类型就是double,不用单独写出
};int main()
{Complex c(1.2, 3.4);cout << (double)c << endl;   // 输出1.2  //等价于c.operator double()double n = 2 + c;  // 等价于double n = 2+c.operator double()// 本来2和c是不能相加的,因为2为一个整形数字,c确实一个对象,// 所以这里调用了重载类型转换运算符cout << n; // 输出 3.2
}

2、自增、自减运算符的重载

  • 自增运算符++、自检运算符–有前置/后置之分,为了区分所有重载的是前置运算符还是后置运算符,C++规定:

  • 前置运算符作为一元运算符重载

  • 重载为成员函数:
    T & operator++();
    T & operator–();
    重载为全局函数:
    T & operator++(T &);
    T & operator—(T &);
    使用样例:++obj, obj.operator++(), operator++(obj) 都调用上述函数

  • 后置运算符作为二元运算符重载,多写一个没用的参数即可,这参数不具备任何意义,也不会被使用;

  • 重载为成员函数:
    T operator++(int);
    T operator–(int);
    重载为全局函数:
    T operator++(T &, int);
    T operator–(T &, int);
    使用样例:obj++, obj.operator++(0), operator++(obj,0) 都调用上函数

注意事项:在vs中,obj++也调用前置重载,而dev则令obj++编译出错。

案例:课程中的所有解释均在代码中呈现,请配合课程看代码;

#include<iostream>
using namespace std;class CDemo
{private:int n;public:CDemo(int i=0):n(i) {}CDemo & operator++();   // 用于前置形式,返回值为引用CDemo operator++(int);  // 用于后置形式,返回值为对象// 后置类型的++为什么没使用&引用呢!// 答:在C++中 ++a;a的返回值就是a的引用;所以在重载前置++运算符时,我们需要尽量维持它原本的属性。   operator int() {return n;}  // 类型强制转换运算符,直接可以cout输出对象friend CDemo & operator--(CDemo &);     // 全局前置friend CDemo operator--(CDemo &, int);  // 全局后置
};CDemo & CDemo::operator++()  // 返回值为CDemo对象的引用
{// 前置 ++++n;return * this;   // 返回值是一个对象,即对* this的引用;
}  // ++s即为:s.operator++(),即返回值就是s的引用(& s)CDemo CDemo::operator++(int k)  // 这里的k是无用的参数; 返回值为CDemo对象
{// 后置 ++CDemo tmp(*this);   // 记录修改前的对象n++;return tmp;         // 返回修改前的对象
}  // s++即为:s.operator(0);CDemo & operator--(CDemo & d)  // 这里的引用可不是只为了节省空间,而是要通过该引用更改成员变量
{// 前置--d.n--;return d;   // 返回对操作数的引用,即d的引用
}  // --s即为:operator--(s)CDemo operator--(CDemo & d, int)
{// 后置--CDemo tmp(d);  // 生成一个临时的对象,因为后置本身输出的值大小不变,但其运行完后,该对象中的--或++才生效,所以返回的对象是其自身。d.n--;return tmp; // 返回操作数的对象,即d
}  // s--即为:operator--(s, 0)int main()
{CDemo d(5);cout << (d++) << ","; //等价于 d.operator++(0);   //重载为成员函数的形式cout << d << ",";   // 可以重载左移运算符或者重载一个强制类型转换运算符即可输出d;cout << (++d) << ","; //等价于 d.operator++();    //重载为成员函数的形式cout << d << endl;cout << (d--) << ","; //等价于 operator--(d,0);   //重载为全局函数的形式cout << d << ",";cout << (--d) << ","; //等价于 operator--(d);     //重载为全局函数的形式cout << d << endl;return 0;
}// OUT
MacBook-Air beida_lesson % g++ 21.cpp -o 21
MacBook-Air beida_lesson % ./21
5,6,7,7
7,6,5,5

注意事项:一共有以下一些观点:

由上述代码可知,后置运算符的重载比前置运算符的重载多了一个临时函数的构建,所以前置运算符在时间上的开销要小于后置运算符重载的开销,特别是遇到递归时,变得就比较明显了。所以提倡写前置运算符。

请添加图片描述

3、继承和派生的基本概念

3.1、基本概念

  • 继承:在定义一个新的类B时,如果该类与某个已有的类A相似(指的是B拥有A的全部特点),那么就可以把A作为一个基类,而把B作为基类的一个派生类(也称子类)
  • 派生类是通过对基类进行修改和扩充得到的。在派生类中,可以扩充新的成员变量和成员函数。
  • 派生类一经定义后,可以独立使用,不依赖于基类。
  • 派生类拥有基类的全部成员函数和成员变量,不论是private、protected、public。
    • 在派生类的各个成员函数中,不能访问基类中的private成员。

案例

  • 所有学生都有的共同属性:
    • 姓名
    • 学号
    • 性别
    • 成绩
  • 所有学生都有的共同方法:(成员函数)
    • 是否该留级
    • 是否该奖励
  • 不同学生又有个字==各自不同的属性和方法
    • 研究生
      • 导师
    • 大学生
    • 中学生
      • 竞赛特长加分
class CStudent
{private:string sName;int nAge;public:bool IsThreeGood() {};void SetName(const string & name){sName = name;}
};// 要毕业的学生
class CUndergraduateStudent: public Cstudent
{private:int nDepartment;  // 添加自己的新的成员变量public:bool IsThreeGood() {.....};  // 覆盖(与基类的成员函数名一样,但内容不一样)bool CanBao Yan() {.....};
};   // 派生类的写法是:类名:public基类名// 研究生
class CGraduatedStudent:public CStudent
{private:int nDepartment;char szMentorName[20];public:int CountSalary() {.....};
}

3.2、派生类对象的内存空间

派生类对象的体积,等于基类对象的体积,再加上派生类对象自己的成员变量的体积。在派生类对象中,包含着基类对象,而且基类对象的春初位置位于派生类对象新增的成员变量之前
在这里插入图片描述

举例一个完整的例子:两个类的简单学生管理程序

// File name:22.cpp
#include<iostream>
#include<string>
using namespace std;class CStudent
{private:string name;string id;  // 学号char gender; // 性别, “F”代表女, “M”代表男int age;public:void PrintInfo();void SetInfo(const string & name_, const string & id_, int age_, char gender_);string GetName() {return name;}
};void CStudent::PrintInfo()
{cout << "Name = " << name << endl;cout << "ID = " << id << endl;cout << "Age = " << age << endl;cout << "Gender = " << gender << endl;
}void CStudent::SetInfo(const string & name_, const string & id_,int age_, char gender_)
{name = name_;id = id_;age = age_;gender = gender_;
}class CUndergraduateStudent:public CStudent
{// 本科生类,继承了CStudent类private:string department;  // 学生所属的系的名称public:void QualifiedForBaoyan(){// 输出给予保研资格cout << "qualified for baoyan" << endl;}void Printfo()   // 覆盖:CUndergraduateStudent自己的Printfo{CStudent::PrintInfo();   // 调用基类PrintInfocout << "Department:" << department << endl;}void SetInfo(const string & name_, const string & id_, int age_, char gender_, const string & department_){CStudent::SetInfo(name_, id_, age_, gender_);  // 调用基类的Setinfodepartment = department_;}
};int main()
{CUndergraduateStudent s2;s2.SetInfo("Harry Potter", "118829212", 19, 'M', "Computer Science");cout << s2.GetName() << "" ;s2.QualifiedForBaoyan();s2.PrintInfo();return 0;
}// OUT
MacBook-Air beida_lesson % g++ 22.cpp -o 22
MacBook-Air beida_lesson % ./22
Harry Potterqualified for baoyan
Name = Harry Potter
ID = 118829212
Age = 19
Gender = M

4、继承关系和复合关系

继承关系:“是” 关系

  • 基类A,B是基类A的派生类。
  • 逻辑上要求:“一个B对象也是一个A对象”。(例:从学生类派生出中学生类,因为中学生也是学生。)

复合关系:“有”关系。

  • 类C中“有”成员变量k,k是类D的对象,则C和D是复合关系
  • 一般逻辑上要求:“D对象是C对象的固有属性或组成部分”。

4.1、继承关系的使用

在这里插入图片描述
请添加图片描述

4.2、复合关系的使用

请添加图片描述在这里插入图片描述在这里插入图片描述

5、派生类覆盖基类成员

派生类可以定义一个和基类成员同名的成员,这叫覆盖。在派生类中访问这类成员时,缺省的情况是访问派生类中定义的成员。要在派生类中访问有基类定义的同名成员时,要使用作用域符号::


class base
{int j;public:int i;void func();
};class derived : public base
{public:int i;void access();void func();
};void derived::access()
{// j = 5; // error,这是base的私有成员变量,派生类derived不能访问i = 5; // 引用的是派生类的ibase::i = 5; // 引用的基类的ifunc();  // 派生类的base::func(); // 基类的}int main()
{derived obj;obj.i = 1;   // 派生类的iobj.base::i = 1;  // 引用的基类的i
}// 在派生类和基类中不要写相同的成员变量,如上代码中的i

注意事项:在派生类和基类中不要写相同的成员变量,如上代码中的i

6、存储权限说明符:protected

在这里插入图片描述
特点:基类的protected成员:可以被以下函数访问:

  • 基类的成员函数
  • 基类的有缘函数
  • 派生类的成员函数可以访问当前对象的基类的保护成员
class People
{private: int nPrivate;  // 私有成员public: int nPublic;    // 公有成员protected: int nProtected;  // 保护成员
};class Son:public People
{void Accesspeople(){nPublic = 1;     // OknPrivate = 1;    // wrongnProtected = 1;  // Ok, 访问从基类继承的protected成员Son f;  // 非当前对象 f.nProtected = 1; // wrong, f 不是当前对象}
};int main()
{People f; Son s;f.nPublic = 1;s.nPublic = 1;f.nProtected = 1; f.nPrivate = 1; s.nProtected =1; s.nPrivate = 1; 
}

7、派生类的构造函数

在这里插入图片描述


class Bug
{private:int nLegs;int nColor;public:int nType;Bug(int nLegs_, int nColor_);void PrintBug(){};
};class FlyBug:public Bug  // FlyBug是Bug的派生类
{int nWings;public:FlyBug(int nLegs_, int nColor_, int nWings_);
};Bug::Bug(int legs, int color)
{nLegs = legs;nColor = color;
}// 错误的FlyBug构造函数
FlyBug::FlyBug(int legs, int color, int wings)
{nLegs = legs;   // 不能访问nColor = color; // 不能访问nType = 1;      // OK,基类的公有成员,没有问题的nWings = wings;
}// 正确的FlyBug构造函数
// 直接初始化派生类所包含的基类的Bug对象(即Bug构造函数)初始化列表
FlyBug::FlyBug(int legs, int color, int wings):Bug(legs, color) // 基类的构造函数
{nWings = wings;
}int main()
{FlyBug fb(2, 3, 4);  // 调用构造函数初始化fb.PrintBug();fb.nType = 1;fb.nLegs = 2;   // error , nLegs is privatereturn 0; 
}

在这里插入图片描述
例:

#include <iostream>class Base
{public:int n;Base(int i):n(i){std::cout << "Base" << n << " constructed " << std::endl;}~Base(){std::cout << "Base " << n << "destructed" << std::endl;}
};class Derived:public Base
{public:Derived(int i):Base(i) // 1、先进入Base构造函数{ std::cout << "Derived constructed " << std::endl;} //2、进入Drivate构造函数~Derived(){std::cout << "Derived destructed" << std::endl;}
};int main()
{Derived Obj(3);return 0;// 3、执行 Derived析构函数// 4、执行 Base析构函数
}// OUT
MacBook-Air beida_lesson % ./23
Base3 constructed 
Derived constructed 
Derived destructed
Base 3destructed

例题2:

#include <iostream>class Bug
{private:int nLefs; int nColor;public:int nType;Bug(int legs, int color);void PrintBug() {};
};class Skill
{public:Skill(int n) {};
};class nWings
{public:nWings(int nWings_) {};
};class FlyBug:public Bug
{nWings w1;Skill sk1, sk2;public:FlyBug(int legs, int color, int wings);
};FlyBug::FlyBug(int legs, int color, int wings):Bug(legs, color), sk1(5), sk2(2), w1(wings) {}

老师版本:

#include <iostream>class Bug
{private:int nLefs; int nColor;public:int nType;Bug(int legs, int color);void PrintBug() {};
};class Skill
{public:Skill(int n) {};
};class FlyBug:public Bug
{int nWings;Skill sk1, sk2;public:FlyBug(int legs, int color, int wings);// nWings(int nWings_) { nWings = nWings_};
};FlyBug::FlyBug(int legs, int color, int wings):Bug(legs, color), sk1(5), sk2(2), nWings(wings) {}

请添加图片描述
请添加图片描述

8、public继承的赋值兼容规则

以下等号“=”并没有采用运算符重载。

请添加图片描述

9、直接基类与间接基类(套娃)

请添加图片描述
请添加图片描述
例:

#include <iostream>class Base
{public:int n;Base(int i):n(i){std::cout << "Base" << n << " constructed " << std::endl;}~Base(){std::cout << "Base " << n << "destructed" << std::endl;}
};class Derived:public Base
{public:Derived(int i):Base(i){ std::cout << "Derived constructed " << std::endl;}~Derived(){std::cout << "Derived destructed" << std::endl;}
};class MoreDerived:public Derived
{public:MoreDerived():Derived(4){std::cout << "More Derived constructed" << std::endl;}~MoreDerived(){std::cout << "More Derived destructed " << std::endl;}
};int main()
{MoreDerived Obj;return 0;
}// OUT~MoreDerived()^
2 errors generated.
MacBook-Air beida_lesson % g++ 23.cpp -o 23
MacBook-Air beida_lesson % ./23            
Base4 constructed 
Derived constructed 
More Derived construct

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

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

相关文章

《论文阅读》基于情感原因感知的共情对话生成模型 2023 AAAI

《论文阅读》基于情感原因感知的共情对话生成模型 2023 AAAI 前言简介模型构架情绪推理器回复生成器实验结果前言 亲身阅读感受分享,细节画图解释,再也不用担心看不懂论文啦~ 无抄袭,无复制,纯手工敲击键盘~ 今天为大家带来的是《The Empathic Dialogue Generation Model…

npm ERR! code CERT_HAS_EXPIRED (创建vue过程)

npm ERR! code CERT_HAS_EXPIRED &#xff08;创建vue过程&#xff09; 起因&#xff1a;卸载 npm uninstall -g vue-cli时候发现报这个错误。 当我们创建vue之前&#xff0c;使用npm更新或者安装啥的时&#xff0c;出现此类提示&#xff0c;则表明&#xff0c;用来验证和网络加…

java读取Excel表格数据

java读取Excel表格数据 环境说明项目结构1.controller层2.service层实现层StudentModel.java类 使用的Maven依赖效果示例一效果示例二文档截图第一页第二页 postman请求说明其他说明 环境说明 jdk1.8&#xff0c;springboot2.5.3 项目结构 1.controller层 package com.exam…

Taro-vue微信小程序用户隐私保护

Taro-vue微信小程序用户隐私保护 一、在 微信公众平台的【设置】- 【服务内容与声明】 &#xff0c;设置用户隐私保护指引&#xff0c;添加项目需要的接口权限。 【用户隐私保护指引】提交之后&#xff0c;官方会进行审核。审核通过之后&#xff0c;对应的接口权限才会生效。 …

区块链安全应用----压力测试

通过Caliper进行压力测试程序 1.环境配置 第一步. 配置基本环境 部署Caliper的计算机需要有外网权限&#xff1b;操作系统版本需要满足以下要求&#xff1a;Ubuntu > 16.04、CentOS > 7或MacOS > 10.14&#xff1b;部署Caliper的计算机需要安装有以下软件&#xff…

【C语言回顾】函数

前言1. 函数的概念和分类2.库函数3. 自定义函数3.1 自定义函数的简单介绍3.2 自定义函数举例 4. 形参和实参4.1 形参4.2 实参4.3 形参和实参的关系4.3.1 理解4.3.2 举例代码和调试 5. 嵌套函数和链式访问5.1 嵌套函数5.2 链式访问 6. 函数的声明和定义6.1 单个文件6.2 多个文件…

ChatGPT在遥感领域中的应用

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本课程重点介绍ChatGPT在遥感中的应用&#xff0c;人工智…

中文编程入门(Lua5.4.6中文版)第十三章 Lua 文件操作

在《Lua世界》的冒险旅途中&#xff0c;勇士们时常需要与神秘的文本卷轴打交道。为了更好地掌握这些知识宝藏&#xff0c;Lua I/O库提供了两种强大的探索模式&#xff1a;简单模式和完全模式&#xff0c;助你轻松应对各类文献挑战。 简单模式&#xff1a;初识卷轴 简单模式如…

C# aspose word实现模板方式打印及打印速度慢解决方法

1.引用dll nuget或者网上都有下载的方式。不过都要收费。下载地址&#xff1a;https://files.cnblogs.com/files/rolayblog/Tool.zip?t1713322422&downloadtrue 2.打印模板设计 新建一个doc文档&#xff0c;根据自己的需求画页面。 A、普通文本 在word中需要替换值的地方添…

《2024最新Java面试题及答案(带完整目录)》

获取链接&#xff1a;《2024最新Java面试题及答案&#xff08;带完整目录&#xff09;》 更多技术书籍&#xff1a;技术书籍分享&#xff0c;前端、后端、大数据、AI、人工智能... ​ ​ ​ 4.1.9.8. 可重入锁&#xff08;递归锁&#xff09; ...........................…

Oracle11.2.0.1,(CVE-2012-1675)漏洞解决方案

1.进入容器停止监听 docker exec -it -u 0 oracle11g bash su - oracle lsnrctl stop listener2.找到监听配置文件位置&#xff0c;修改监听文件 echo $ORACLE_HOMEvi network/admin/listener.ora #在文件底部添加 SECURE_REGISTER_LISTENER (IPC) #启动监听 lsnrctl start …

基于springboot+vue+Mysql的汽车租赁系统

开发语言&#xff1a;Java框架&#xff1a;springbootJDK版本&#xff1a;JDK1.8服务器&#xff1a;tomcat7数据库&#xff1a;mysql 5.7&#xff08;一定要5.7版本&#xff09;数据库工具&#xff1a;Navicat11开发软件&#xff1a;eclipse/myeclipse/ideaMaven包&#xff1a;…

idea运行报错:启动命令过长

JAVA项目&#xff0c;运行的时候报错 Command line is too long. Shorten the command line via JAR manifest or via a classpath file and rerun老问题了&#xff0c;记录一下 解决办法&#xff1a; 1、Edit Configurations 2、点击Modify options设置&#xff0c;勾选S…

PlistEdit Pro for Mac激活版:强大的Plist文件编辑工具

PlistEdit Pro for Mac是一款专为Mac用户设计的强大Plist文件编辑工具。Plist文件是苹果公司开发的一种XML文件格式&#xff0c;用于存储应用程序的配置信息和数据。这款软件为用户提供了直观、易用的界面&#xff0c;使编辑和管理Plist文件变得轻松简单。 PlistEdit Pro for M…

C++设计模式|创建型 3.抽象工厂模式

在上一篇文章中介绍了工厂模式&#xff0c;每个具体工厂负责生产一个专门的产品&#xff0c;其代码扩展性很好&#xff0c;这篇文章将介绍抽象工厂模式。 1.为什么要使用抽象工厂模式&#xff1f; 既然已经有了“工厂模式”&#xff0c;那为什么还会有抽象工厂模式呢&#xf…

生成人工智能体:人类行为的交互式模拟论文与源码架构解析(1)——场景故事介绍

生成NPC为交互应用程序创建逼真的人类行为模拟。在这项工作中&#xff0c;我们通过将二十五个NPC放置在一个沙盒环境中&#xff08;类似于The Sims&#xff0c;模拟人生&#xff09;&#xff0c;展示了生成NPC的能力。用户可以观察和干预NPC的日常计划、分享新闻、建立关系以及…

就业班 第三阶段(ansible) 2401--4.16 day2 ansible2 剧本+角色

六、Ansible playbook 简介 playbook 是 ansible 用于配置&#xff0c;部署&#xff0c;和管理被控节点的剧本。   通过 playbook 的详细描述&#xff0c;执行其中的一系列 tasks &#xff0c;可以让远端主机达到预期的状态。playbook 就像 Ansible 控制器给被控节点列出的的…

广东海洋大学成功部署(泰迪智能科技)大数据人工智能实验室建设

广东海洋大学简称广东海大&#xff0c;坐落于广东省湛江市&#xff0c;是国家海洋局与广东省人民政府共建的省属重点建设大学、广东省高水平大学重点学科建设高校、粤港澳高校联盟成员 &#xff0c;入选卓越农林人才教育培养计划&#xff0c;是教育部本科教学水平评估优秀院校。…

odoo添加自定义网页---添加模块图标

1.新建一个模块 python odoo-bin scaffold test_web <路径> 2.我们需要修改一下几个文件 3.web.xml <?xml version"1.0" encoding"UTF-8"?> <templates id"template" xml:space"preserve"><t t-name"T…

详解汽车交流充电桩主板的四大版本

近年来&#xff0c;在电动汽车行业快速发展背景下&#xff0c;充电桩的建设变得愈发重要&#xff0c;特别是兼容性较高、适用性较广的交流充电桩。 交流充电桩的心脏——主板的设计与功能&#xff0c;对于充电桩的性能和用户体验起着至关重要的作用。目前&#xff0c;市面上的…