c++ primer plus 第十五章笔记 友元,异常和其他

友元类:
        两个类不存在继承和包含的关系,但是我想通过一个类的成员函数来修改另一个类的私有成员和保护成员的时候,可以使用友元类。

class A
{
    private:
        int num;//私有成员
    //...
    public: 
    //...
        friend class B;//声明一个友元类
}

class B
{
    private:
    //...
    public:
    void set(A &t, int c){t.num = c;}//友元类的成员函数可以直接访问其他类的私有成员和保护成员

}

如何将一个类中的成员函数设置为另一个类的友元函数?

class B
{
    private:
    //...
    public:
    //...
    void set();
}

class A
{
    private:
        //...
        int num;
    public:
        friend void B::set();//声明另一个类中的方法是友元函数,B中对应的友元函数直接可以访问A中私有和 保护成员,而不用通过A对象的方法

}

如何解决循环引用,循环依赖的问题?

什么是循环引用和循环依赖?比方说类A中的函数对类B的对象进行了操作,类B中又对类A的对象进行了操作,如果类A声明在类B前面,则会导致编译器不认识类A中的B,如果类B在类A前面,则会导致编译器不认识类B中的A,这就是循环引用。

如:

class A
{
    public:
        int num1;
    private:
        void Output() {cout << num1};
        void Show(const B & b){ b.Output();}
};

class B
{
    public:
        int num2;
    private:
        void Output() {cout << num2};
        void Show(const A &a){ a.Test();}
};
//显然A中的Show()不认识其参数类型const B &
//如果AB交换位置,又会导致B的Show不认识类A
//这就是循环引用,循环依赖

此时,编译器既不认识B也不认识b.Output()

解决办法:
使用"向前声明"

class B;//声明一下B是一个类,这种方法叫做向前声明
class A
{
    public:
        int num1;
    private:
        void Output();
        void Show(const B & b);
};

class B
{
    public:
        int num2;
    private:
        void Output() {cout << num2};
        void Show(const A &a){ a.Test();}
};

但是还有一个问题,那就是编译器不认识Show中b的方法Output,因为现在编译器只是知道B是个类,并不知道B中的成员函数是什么

即此时知道B是个类,但是不知道B的成员函数Output()

解决方法:

将A中的内联函数写在类B的声明之下

class B;//声明一下B是一个类,这种方法叫做向前声明
class A
{
    public:
        int num1;
    private:
        void Output();
        void Show(const B & b);
}

class B
{
    public:
        int num2;
    private:
        void Output() {cout << num2};
        void Show(const A &a){ a.Test();}
}
//在类外面定义内联函数,要用inline,并声明属于哪个类
inline void A::Show(const B &b)
{
     b.Output();
}

注意:在头文件下定义的成员函数都是内联函数,因此要在函数前面加上inline

模板类里面的成员函数一点都是放在.h文件下进行定义的

嵌套类:

即在一个类里面再声明一个了类作为成员变量:

template<class T>
class Queue
{
	private:
		class Node
		{
			T data;
			Node *next;
		} 
}

15.3 异常

1.调用#include<cstdlib>里面的abort()函数,遇到异常时,直接终止程序,并输出核心已转存(core dumped)的错误提示

#include<cstdlib>

//计算调和平均值,调和平均值 = 2.0 * x * y / x + y,注意x和y不能是相反数
/*
在一些新式编译器中,当除数为0时,并不会出现错误信息,程序仍然可以正常运行,此时算出来的调和平均值
是INF,表示一个无穷大的特殊浮点数,而有的编译器可能会运行到生成除数为0就崩溃的程序
*/

double hmean(double a, double b)
{
    if(a == -b)
    {
        cout << "invalid argument to hmean()" << endl;
        abort();//程序直接终止运行; 
    }
    else
        return 2.0 * x * y / x + y;

}

2.

返回错误码

bool hmean(double a, double b, double *c)//传入指针,指针直接操作内存
{
    if(a == -b)
    {
        cout << "invalid argument to hmean()" << endl;
        return false;
    }
    else
    {
        *c = 2.0 * x * y / x + y;
        return true;//此处的true和false可作为错误码
    }
}

3.

使用c++的异常处理机制

1)使用try块

2)异常发生时抛出异常(throw)

3)程序捕抓异常(catch)

double hmean(double a, double b, double *c)//传入指针,指针直接操作内存
{
    if(a == -b)
    {
        throw "bad hmean() argument, a = -b not allowed. ";//抛出异常,throw后面可以跟字符串,对象或其他c++类型,此处的异常类型是const char *类型
        //throw抛出异常后hmean函数立即终止,返回main函数中调用hmean的下一行,程序找到能接收抛出异常类的异常处理程序catch(const char *s)
    }
    else
        return 2.0 * a * b / a + b;
}

int main(void)

{
    int a, b;
   while( cin >> a >> b && a != 0 && b != 0)
    {
        try//try块,用于注明可能发生异常的代码,在try块外调用的hmean函数,发生时异常不会处理该异常,而是会终止程序的运行
        {
            hmean(a, b);
        }
        catch(const char *s)//捕抓异常,异常处理程序
        {
            cout << s << endl;
            cout << "Enter a new pair of arguments." << endl;
            continue;
        }
    }
    hmean(10, -10);//在try块外发生异常,异常处理程序不会捕获,而是直接终止程序运行
    return 0;
}

通常是类作为异常类型来抛出,因为类可以携带更多的信息,抛出异常捕抓异常try块标注可能发生异常的代码的方式和字符串类型相同,此处不在过多赘述。

栈解退

两种抛出异常

1.直接抛出异常

try块中的函数中,直接有抛出异常的语句,此时抛出异常后函数结束,抛出的异常类型返回main函数找到匹配的异常处理函数捕捉异常并处理,如上面的例子,都是直接抛出异常

2.间接抛出异常

比方说函数a调用函数b,函数b调用函数c,函数c中抛出异常

我们知道函数的调用是一个压栈的过程,此时函数c抛出异常后,将会直接结束函数c运行,退回到上一个函数的调用,即出栈,到函数b,在函数b中寻找能够处理该异常类型的catch异常处理函数,找不到直接退栈不会执行该函数的内容,再往前找,直到找到能够处理该异常类型的catch异常处理函数为止,如果退到最后main函数都没有能处理这个异常类型的catch异常处理函数,则结束程序运行,这个就是栈解退,即异常抛出后就会往回倒,找到能满足条件的函数,找不到就程序运行结束。

一些需要注意的点:

1)如果抛出的异常类型是某个了的对象的话,将会调用该类的构造函数,生成一个该对象的副本,然后找到对应的catch来处理该异常

注意点:

为什么要创建副本?

原因是抛出对象后,函数结束,该对象被销毁,不能抛出对象本身,应该抛出该对象的副本

catch接收的异常类型是什么类型?

是该对象类型的引用

为什么是引用?而不是按值传参?

因为基类的引用既可以指向基类,还可以指向派生类,使用引用的话,该异常类型的派生类也可以被这个catch捕获

{
    Class test;
    throw test;//将会生成test的副本,p指向test的副本而不是test本身是一件好事
}
//...

catch(Class & t)//对应的异常类型是Class类型的引用
{

}

2)在catch中,如果throw后面没有跟异常类型,那么抛出来的异常类型是什么?

是,和catch处理的异常类型是同一异常类型

catch<const char *>
{
    throw;//此时抛出的异常是const char *的异常
}

3)如果a派生出b,b派生出c,c派生出d,这种叫做层次化派生

//假设a是Base1类型的对象, c是Base2, c是Base3, d是Base4,这种叫做层次化派生
{
 throw a;
 throw b;
 throw c;
 throw d;
}

//那么catch接收应该是
catch(Base4 &d)
{
}

catch(Base3 &c)
{
}

catch(Base2 &b)
{
}

catch(Base1 &a)
{
}
//catch的顺序应该和throw的顺序相反,原因是基类的引用可以指向基类的对象也可以指向派生类的对象
//如果把Base1 &a放在最前面,那么Base1的这一个catch就能接收Base2, Base3, Base4的异常了,导致
//其他catch无法执行

exception类

1.exception类是c++的异常类,这个异常类里面有一个重要的虚函数what,它返回一个字符串,我们可以使用exception这个异常类来派生出其他的异常类供我们使用。

//exception里面虚函数what的声明
virtual const char* what() const noexcept;
//返回一个c风格的指针, noexcept的意思是说该函数不会抛出任何异常

2.c++库还定义了很多基于exception的异常类,比如在stdexcep头文件下的logic_error和runtime_error.

3.bad_alloc和new

bad_alloc是一个异常类,在头文件new中,其作用是当你用new开辟内存的时候,开辟失败了,库里面的某个函数就会抛出异常(不需要自己抛出),就会返回一个bad_alloc类的对象(在包含头文件new的情况下)

可以使用

catch(bad_alloc &ba)//捕获该异常 
{

}

对开辟内存失败后做出一些操作。

我们熟悉的new开辟内存空间一般是返回NULL指针,如果包含了new头文件,就可以有两种选择

int *pi = new (std::nothrow) int;//关闭bad_alloc,返回NULL
int *pa = new (std::nowthrow) int[500];//开辟bad_alloc,抛出bad_alloc异常

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

ChatGPT Plus 自动扣费失败,如何续订

ChatGPT Plus 自动扣费失败&#xff0c;如何续订 如果您的 ChatGPT Plus 订阅过期或扣费失败&#xff0c;本教程将指导您如何重新订阅。 本周更新 ChatGPT Plus 是一种每月20美元的订阅服务。扣费会自动进行&#xff0c;如果您的账户余额不足&#xff0c;OpenAI 将在一次扣费…

css 背景图片居中显示

background 简写 background: #ffffff url(https://profile-avatar.csdnimg.cn/b9abdd57de464582860bf8ade52373b6_misnice.jpg) center center / 100% no-repeat;效果如图&#xff1a;

git - 笔记

为什么要学习Git 为什么要学习Git软件 为什么学习 因为在主流开发中&#xff0c;基于互联网软件开发的项目都会使用Git软件来进行项目开发过程中的资源管理 比如人力资源 代码资源 比如前端资源 .html .java等代码资源 文档资源 像项目开发中涉及到的需求文档等 这种项目中管理…

CRM术语速览:掌握这十个专业名词,成为CRM专家

无论您是销售人员还是采购经理&#xff0c;熟悉CRM管理系统专业术语都是一门必修课。擅于运用CRM专业术语帮助您理解CRM管理系统的功能、更好的开展业务。本文与您分享不得不知道的十大CRM专业术语&#xff0c;CRM常用术语合集。常见的CRM术语包括MQL、SQL、SDR、销售漏斗等等。…

带摄像头的 AirPods,苹果会怎么做出来?

苹果对智能产品的设计&#xff0c;正在放飞自我。 根爆料&#xff0c;苹果在「未来设备」的规划里&#xff0c;有两个大胆的想法&#xff1a; 一是带有屏幕的 HomePod 正在研发中&#xff0c;当中将集成 Apple TV、FaceTime 等重多功能&#xff1b;二是配备摄像头的 AirPod…

【ARM Trace32(劳特巴赫) 高级篇 21 -- SystemTrace ITM 使用介绍】

文章目录 SystemTrace ITMSystemTrace ITM 常用命令Trace Data AnalysisSystemTrace ITM CoreSight ITM (Instrumentation Trace Macrocell) provides the following information: Address, data value and instruction address for selected data cyclesInterrupt event info…

201909青少年软件编程(Scratch)等级考试试卷(三级)

青少年软件编程&#xff08;Scratch&#xff09;等级考试试卷&#xff08;三级&#xff09;2019年9月 第1题&#xff1a;【 单选题】 执行下面的脚本后&#xff0c;变量“分数”的值是多少&#xff1f;&#xff08;&#xff09; A:5 B:6 C:10 D:25 【正确答案】: C 【试题…

【Java网络编程】TCP核心特性(下)

1. 拥塞控制 拥塞控制&#xff1a;是基于滑动窗口机制下的一大特性&#xff0c;与流量控制类似都是用来限制发送方的传送速率的 区别就在于&#xff1a;"流量控制"是从接收方的角度出发&#xff0c;根据接收方剩余接收缓冲区大小来动态调整发送窗口的&#xff1b;而…

文章解读与仿真程序复现思路——电网技术EI\CSCD\北大核心《考虑多能互补灵活性和用户低碳意愿的区域综合能源系统鲁棒优化调度》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

STM32 SDRAM知识点

1.SDRAM和SRAM的区别 SRAM不需要刷新电路即能保存它内部存储的数据。而SDRAM&#xff08;Dynamic Random Access Memory&#xff09;每隔一段时间&#xff0c;要刷新充电一次&#xff0c;否则内部的数据即会消失&#xff0c;因此SRAM具有较高的性能&#xff0c;但是SRAM也有它…

npm 操作报错记录1- uninstall 卸载失效

npm 操作报错记录1- uninstall 卸载失效 1、问题描述 安装了包 vue/cli-plugin-eslint4.5.0 vue/eslint-config-prettier9.0.0 但是没有使用 -d &#xff0c;所以想重新安装&#xff0c;就使用 uninstall 命令卸载&#xff0c;结果卸载了没反应&#xff0c;也没有报错&#xf…

一文帮助快速入门Django

文章目录 创建django项目应用app配置pycharm虚拟环境打包依赖 路由传统路由include路由分发namenamespace 视图中间件orm关系对象映射操作表数据库配置model常见字段及参数orm基本操作 cookie和sessiondemo类视图 创建django项目 指定版本安装django&#xff1a;pip install dj…

【linux】04 :linix实用操作

1.常用快捷键 ctrlc表示强制停止。linux某些程序的运行&#xff0c;如果想强制停止&#xff0c;可以使用&#xff1b;命令输入错误&#xff0c;也可以通过ctrlc,退出当前输入&#xff0c;重新输入。 ctrld表示退出登录&#xff0c;比如退出root以回到普通用户&#xff0c;或者…

C++ Qt开发:QHostInfo主机地址查询组件

Qt 是一个跨平台C图形界面开发库&#xff0c;利用Qt可以快速开发跨平台窗体应用程序&#xff0c;在Qt中我们可以通过拖拽的方式将不同组件放到指定的位置&#xff0c;实现图形化开发极大的方便了开发效率&#xff0c;本章将重点介绍如何运用QHostInfo组件实现对主机地址查询功能…

spring-cloud-openfeign 3.0.0(对应spring boot 2.4.x之前版本)之前版本feign整合ribbon请求流程

在之前写的文章配置基础上 https://blog.csdn.net/zlpzlpzyd/article/details/136060312 下图为自己整理的

UE5 UE4 开发常用工具AssetDeveTool

AssetDeveTool工具&#xff0c;支持UE5 5.0-.5.3 UE4 4.26/4.27 下载链接&#xff1a; 面包多 https://mbd.pub/o/bread/ZZubkphu 工坊&#xff1a; https://gf.bilibili.com/item/detail/1104960041 包含功能&#xff1a; 自动化批量展UV功能 快速选择功能 自动化批量减面功能…

SoraAI视频工具优先体验资格申请步骤

SoraAI视频工具优先体验资格申请 申请网址&#xff1a;OpenAI Red Teaming Network application 申请步骤&#xff1a; 填写基础信息 请使用英文根据内容填写以下内容&#xff0c;名、姓、电子邮件、居住国家、组织隶属关系(如果有)、教育水平 、学位&#xff08;哪个领域的…

02-gitlab的数据备份和恢复

一、gitlab的数据备份 关于数据备份&#xff0c;咱们就不需要多说什么了&#xff0c;主要就是方式数据意外丢失&#xff0c;导致代码上线流程及数据的损坏崩溃&#xff1b; 为了避免严重生产事故&#xff0c;进而gitlab有了数据备份与恢复的功能。 1&#xff0c;修改gitlab的配…

python淘宝网页爬虫数据保存到 csv和mysql(selenium)

数据库连接设置&#xff08;表和字段要提前在数据库中建好&#xff09; # 数据库中要插入的表 MYSQL_TABLE goods# MySQL 数据库连接配置,根据自己的本地数据库修改 db_config {host: localhost,port: 3306,user: root,password: ma*****6,database: may2024,charset: utf8mb…

MySQL 篇-快速了解事务、索引

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 事务概述 1.1 事务四大特性(ACID) 2.0 索引概述 2.1 关于 “索引一定要创建在主键上&#xff1f;” 的问题 2.2 索引操作语法 2.3 索引结构 1.0 事务概述 事务是…