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

news2024/10/6 4:08:39

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

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异常

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

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

相关文章

SpringBootWeb(接收请求数据,返回响应结果,分层解耦,Spring的IOCDI)【详解】

目录 一、接收请求数据 1. 接收表单参数 1.原始方式【了解】 2.SpringBoot方式 3.参数名不一致RequestParam 2.实体参数 1.简单实体对象 2.复杂实体对象 3.数组集合参数 4.日期参数 3. JSON参数 1.Postman发送JSON数据 2.服务端接收JSON数据 4. 路径参数(rest风格…

httprunner结合pytest的关键字

1. 通用关键字 可参考官方文档&#xff1a; Write Testcase - HttpRunner V3.x Docs 2. 特别关键字 2.1. 步骤step前置 2.1.1. setup_hook 关键源码 def setup_hook(self, hook: Text, assign_var_name: Text None) -> "RunRequest":if assign_var_name:sel…

【Python】新手入门:全局变量和局部变量的概念、区别以及用法

【Python】新手入门&#xff1a;全局变量和局部变量的概念、区别以及用法 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyTorch零基础入门教程&#x1f448;…

基于卷积神经网络的野外可食用植物分类系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 本文详细探讨了一基于深度学习的可食用植物图像识别系统。采用TensorFlow和Keras框架&#xff0c;利用卷积神经网络&#xff08;CNN&#xff09;进行模型训练和预测&#xff0c;并引入迁移学习模型…

联立方程模型的可识别性的通俗解释

联立方程模型的可识别性&#xff0c;主要的解法是阶条件算法和秩条件算法&#xff0c;数学公式角度的解释就不讲了&#xff0c;参考下面的前人文献。 【计量经济学】联立方程模型-CSDN博客 说一下公式算法背后的通俗原理。 在计量经济模型中&#xff0c;比如 Y23*Xu中&#x…

springboot251基于springboot-vue的毕业论文管理系统

毕业论文管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本毕业论文管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短…

实战|环信 Vue2 uniapp Demo重构焕新!经典再升级!

项目背景 当前环信 uni-app vue2 Demo 地址升级版本 Github 地址&#xff08;临时&#xff09; 原版本功能实现方式较混乱&#xff0c;代码逻辑晦涩难懂&#xff0c;不利于开发者参考或复用。此实战项目在确保原项目功能保留的情况下进行完全重写并新增大量功能&#xff0c;以…

鸡肋的Git

1.前言 对于大多数开发人员来说&#xff0c;我们大多数在学习或者工作过程中只关注核心部分&#xff0c;比如说学习Java&#xff0c;可能对于大多数人而言一开始都是从Java基础学起&#xff0c;然后408&#xff0c;Spring&#xff0c;中间件等&#xff0c;当你发现很多高深的技…

ARM中汇编语言的学习(加法、乘法、除法、左移、右移、按位与等多种命令操作实例以及ARM的 N、Z、C、V 标志位的解释)

汇编概述 汇编需要学习的大致框架如下&#xff1a; 汇编中的符号 1.指令&#xff1b;能够北嘁肷梢惶?2bit机器码&#xff0c;并且能够被cpui识别和执行 2.伪指令&#xff1a;本身不是指令&#xff0c;编译器可以将其替换成若干条指令 3.伪操作&#xff1a;不会生成指令…

STL之set容器代码详解

1 基础概念 所有元素都会在插入时自动被排序 本质&#xff1a; set/multiset属于关联式容器&#xff0c;底层结构是用二叉树实现。 set和multiset区别&#xff1a; set不允许容器中有重复的元素&#xff1b; multiset允许容器中有重复的元素 。 2 代码示例 Talk is chea…

GO语言接入支付宝

GO语言接入支付宝 今天就go语言接入支付宝写一个教程 使用如下库&#xff0c;各种接口较为齐全 "github.com/smartwalle/alipay/v3"先简单介绍下加密&#xff1a; 试想&#xff0c;当用户向支付宝付款时&#xff0c;若不进行任何加密&#xff0c;那么黑客就可以任…

【牛客】VL76 任意奇数倍时钟分频

描述 编写一个模块&#xff0c;对输入的时钟信号clk_in&#xff0c;实现任意奇数分频&#xff0c;要求分频之后的时钟信号占空比为50%。模块应包含一个参数&#xff0c;用于指定分频的倍数。 模块的接口信号图如下&#xff1a; 要求&#xff1a;使用Verilog HDL语言实现&#…

2024最新版CleanMyMac X 4.15.1 Crack+激活码下载

CleanMyMac X 为您喜爱的事物腾出空间。 CleanMyMac 具有一系列巧妙的新功能&#xff0c;可让您安全、智能地扫描和清理整个系统、删除大型未使用的文件、减小 iPhoto 图库的大小、卸载不需要的应用程序或修复开始工作不正常的应用程序、管理所有应用程序您可以从一个地方进行扩…

html css 导航栏 2

鼠标划过会向上移动改变颜色 html文件 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title>导航栏</title><link rel"stylesheet" href"css/dhl1.css" /></head><body><div …

分销商城微信小程序:用户粘性增强,促进复购率提升

在数字化浪潮的推动下&#xff0c;微信小程序作为一种轻便、高效的移动应用形式&#xff0c;正成为越来越多企业开展电商业务的重要平台。而分销商城微信小程序的出现&#xff0c;更是为企业带来了前所未有的机遇。通过分销商城微信小程序&#xff0c;企业不仅能够拓宽销售渠道…

揭秘税务信息接口:解读企业税务登记与纳税情况

导语&#xff1a; 随着社会经济的发展&#xff0c;税收对于国家财政收入的重要性不言而喻。税务登记信息和纳税情况对于企业和个人来说至关重要。在这个背景下&#xff0c;税务信息接口应运而生&#xff0c;为我们提供了便捷的查询途径。本文将以挖数据平台提供的税务信息接口…

【IPC】管道通信【命名管道】

文章目录 1.管道小总结2.命名管道2.1认识命名管道2.2命名管道的应用小场景2.3模拟命名管道1.Lod.hpp2.common.hpp3.server.cxx4.client.cxx 3.管道代码总结 1.管道小总结 linux-manualshouce 在Linux中&#xff0c;manual手册的编号用于区分手册的不同部分。这些编号通常用于ma…

个人商城系统开源(发送手机验证码!)

原文地址&#xff1a;个人商城系统开源&#xff08;发送手机验证码&#xff01;&#xff09; - Pleasure的博客 下面是正文内容&#xff1a; 前言 由于近期实在没有什么话题可写和一些有趣的项目教程可以分享。所以我只能决定将我自己亲手编写的一个迷你迷你商城系统进行开源…

3 模型评估

3 模型评估 在测试AI系统中的模型训练和评估阶段,需要使用准备好的数据集对AI模型进行训练和评估。在训练过程中,应该对模型进行监控和调整,以确保模型的准确性和效果。在评估过程中,需要使用测试数据集对模型进行测试,以验证模型的准确性和效果。模型的评估也分为离线评…

通过一篇文章带你玩转git和GitHub

Git和Github的基本用法 前言一、Git和Github的基本用法背景下载安装安装 git for windows安装 tortoise gitgit安装过程中的一些选项 tortoise git汉化教程下载tortoise git汉化安装包安装tortoise git汉化安装包 三、使用 Github 创建项目注册账号创建项目下载项目到本地 四、…