17- C++ const和异常-5 (C++)

news2024/11/18 5:36:30

第六章 C++对C的拓展2

6.1 const详解

6.1.1 const 修饰普通变量

被修饰的对象是只读的

  1. const int a;   //a的值是只读的 int const a;

  2. const int * p;   该语句表示指向整形常量的 指针它指向的值不能修改

  3. int const * p;   该语句与b的含义相同,表示指向整形常量 的指针,它指向的值不能修改。

  4. int * const p;   该语句表示指向整形的常量指针,它不能再指向别的变量,但指向(变量)的值可以修改。

  5. const int *const p;  该语句表示指向整形常量 的常量指针 。它既不能再指向别的常量,指向的 值也不能修改

  6. int const *const p;   表示指向整形常量 的常量指针 。它既不能再指向别的常量,指向的值也不能修改

6.1.2 const修饰成员变量

const修饰类的成员变量,表示成员常量,不能被修改,同时它只能在初始化列表中赋值。

class A
{
    const int nValue;
    A(int x):nValue(x){};
};

6.1.3 const 修饰类的成员函数

使用const修饰类的成员函数,该函数为类的常成员函数, 它不改变对象的成员变量

class B
{
public:
    int x;
public:
    void f()  //void f() const
    {
        this->x++; // 允许在非常量成员函数中修改 x
    }
};

注意:普通的全局函数不能用const修饰

6.1.4 const 修饰对象

1、const 修饰的对象为常量对象,其中的任何成员都不能被修改

2、const 修饰的对象只能访问类中的const函数。

3、const 修饰的对象可以访问public成员变量,但是不能够修改。

#include <iostream>
using namespace std;

class B
{
public:
    int x;
public:
    void f() const
    {cout << "f(): " << endl;}
    void f1() {};
};

int main(int argc, char **argv) {
    B b;
    const B p = b;
    p.f();
    return 0;
}

6.1.5 const 修饰引用

void function(const TYPE& Var); //引用参数在函数内不可以改变
void function(const int& t)
{
    t++; //[Error] increment of read-only reference 't'
}

//引用指向某个常量时,需要用const修饰
//invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int'
int &a = 19;
const int &b = 19;

6.1.6 const 修饰函数返回值

const修饰函数返回值其实用的并不很多,它的含义和const修饰普通变量以及指针的含义基本相同。

  • 函数返回值为普通变量和对象时使用const修饰没有意义,接收函数的返回值可以是const也可以不是,因为不论怎样都不可能通过函数返回值的接收者改变函数的返回值

  • 函数返回值是被const修饰的指针,注意返回的地址不要为函数栈上的地址,接收函数的返回值必须用const修饰

  • 函数返回值是被const修饰的引用时,接收函数的返回值可以是const也可以不是,如果接收函数的返回值是引用时必须用const修饰

#include <iostream>
#include <Cstring>
using namespace std;

//const 修饰成员变量
class A
{
public:
    const int x; //成员常量 只能在初始化成员列表中被赋值

    A():x(100)
    {}
    /*  A a;
     *  a.x = 1000; //错!!
     * */
};

//const 修饰成员函数
class B
{
public:
    int x;
    B():x(100){}

    int getX() const
    {
        return x;
    }
};

#if 0
//const 不能修饰全局函数
void func() const //error: non-member function 'void func()' cannot have cv-qualifier
{}
#endif

class C
{
public:
    int x;
    C():x(100)
    {}

    int getX() const
    {return x;}

    void setX(int x)
    {this->x  = x;}
};

//const修饰引用:目的是不希望通过引用改变被应用对象的内容
void func(const B &t)
{
   // t.x++;
}

//const修饰函数的返回值
const int func2()
{
    int x = 100;
    return 100;
}

const B func3()
{
    B b;
    return b;
}

//file10000
const char *func4() //之所以用const修饰 是希望调用函数的用户不能够修改这个函数体内在堆上申请的空间中的内容
{
    char *p = new char[10];
    strcpy(p, "hello");
    return p;
}

const int *func5()
{
    int a[4];
    return a; //返回栈上的地址是没有意义的!因为这块栈空间在函数调用结束后会被系统自动回收,所以我们使用一个指针变量来接收这个函数的返回值是没有意义的q
}

const B &func6(B &t) //返回值是引用时,等价于返回值是:const B *func6()
{
   // B b;
    //return b; //如果返回值是引用,return b 编译器在处理的时return &b,返回的是栈上的一个地址是有问题的!
    return t;
}

int main() {
    //const修饰函数的返回值
    int x = func2();   //a = b  (int = const int) 不能通过a将b的值修改,所以b是不是const的无所谓
    cout << x << endl;

    B b = func3();
    cout << b.x << endl;

    const char *p = func4(); // error: invalid conversion from 'const char*' to 'char*'
    cout << p << endl;
   // *p = 'H';

    int a = 100;
    const int *p2 = &a;
    const int *p3 = p2;
//    *p3 = 1000; //*p2 = 1000   a = 1000

    B t = func6(b); //t = b 不可能通过t修改b
    const B &t2 = func6(b); // B &t2 = b;  能够通过应用t2修改b
#if 0
    //const修饰普通变量
    const int a = 10; //变量a是只读的
    //a = 100; //error: assignment of read-only variable 'a'

    int x = 100;
    const int *p = &x; //*p是只读的,指针p不能够改变他所指向的内存空间的内容
   // *p = 10;

    int const *p2 = &x; //等效于 const int *p2 = &x;

    const  int *const p3 = &x; //p3是只读的:p3这个指针一旦初始化后就不能够再指向别的内存空间了   *p3是只读的
    //p3 = &a; //error: assignment of read-only variable 'p3'
    //*p3 = 1000; //error: assignment of read-only location '*(const int*)p3'

    int const *const p4 = &x; //等价于  const  int *const p3 = &x;

    const C c;
    //c.x = 1000; // error: assignment of member 'C::x' in read-only object
    //c.setX(); //error: passing 'const C' as 'this' argument discards qualifiers [-fpermissive]
    cout << c.x << endl;
#endif
    return 0;
}

6.2 extern "C"

6.2.1 extern "C"的含义

1、extern是C/C++语言中表明函数和全局变量作用范围(可见性)的关键字,该关键字告诉  编译器其声明的函数和变量可以在本模块或其它模块中使用

2、通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错;它会在 链接阶段 中从模块A编译生成的目标代码中找到此函数。

3、extern "C" 包含双重含义,从字面上即可得到:首先,被它修饰的目标是“extern”的;其次,被它修饰的目标是“C”的。

6.2.2 extern "C"的作用

1、我们来回顾一下c语言编译器gcc和c++编译器g++在编译一个函数的时候的处理方法

我们新建三个文件,分别命名为:example.h example.c main.cpp

“example.c” 的内容如下:

#include "example.h"
int add( int x, int y )
{
	return x + y;
}

"example.h" 的内容如下:

#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H
extern int add(int x,int y);
#endif

“main.cpp" 的内容如下:

#include <iostream>
using namespace std;

#include "example.h"
int main(int argc, char* argv[])
{
	add(2,3); 
	return 0;
}

接下来我们对这几个文件进行编译,分别执行如下命令:

gcc -c example.c

g++ -c main.cpp

g++ main.o example.o

我们会发现 编译报错

2、在C++编写的源文件中使用extern "C"告诉编译器该文件中哪些函数是来外来的,并且告诉编译器这些函数需要使用”C语言“的规则进行编译。

main.cpp 修改成这样既可:

#include <iostream>

using namespace std;
extern "C"
{
    #include "example.h"
}
int main(int argc, char* argv[])
{
	add(2,3); 
	return 0;
}

或者将example.h修改成这样:

#ifndef C_EXAMPLE_H
#define C_EXAMPLE_H

#ifdef __cplusplus
extern "C" {
#endif

int add(int, int);
#ifdef __cplusplus
}

#endif
#endif

这样,编译器在编译”add(2,3);“这条代码的时候,会用C语言的规则进行编译,这样生成的符号也叫做"add",因为在example.o中存在"add"函数,所以编译能够通过。

6.3 nullptr

C++中为了 避免“野指针”(即指针在首次使用之前没有进行初始化)的出现,我们声明一个指针后最好马上对其进行初始化操作。如果暂时不明确该指针指向哪个变量,则需要赋予NULL值。除了NULL之外,C++11新标准中又引入了nullptr来声明一个“空指针”,这样,我们就有下面三种方法来获取一个“空指针”:

int *p1 = NULL; 
int *p2 = 0;
int *p3 = nullptr;

为什么C++11要引入nullptr?它与NULL相比又有什么不同呢?

C/C++中的NULL到底是什么呢?

1、NULL在C++中的定义,NULL在C++中被明确定义为整数0:

/* Define NULL pointer value */
#ifndef NULL
    #ifdef __cplusplus
        #define NULL    0
    #else  /* __cplusplus */
        #define NULL    ((void *)0)
    #endif  /* __cplusplus */
#endif  /* NULL */

2、NULL在C中的定义在C中,NULL通常被定义为如下:

#define NULL ((void *)0)

也就是说NULL实质上是一个void *指针。

那么问题又来了,我们从一开始学习C++的时候就被告诫C++是兼容C的,为什么对于NULL C++却不完全兼容C呢?C++之所以做出这样的选择,根本原因和C++的函数重载机制有关。考虑下面这段代码:

void Func(char *);
void Func(int);

int main()
{
    Func(NULL);
}

如果C++让NULL也支持void *的隐式类型转换,这样编译器就不知道应该调用哪一个函数。

为什么要引入nullptr

为了让c++传递空指针调用void Func(char *);传参时传 nullptr

void Func(char *);
void Func(int);

int main()
{
    Func(nullptr);  
}

由于我们经常使用 NULL表示空指针,所以从程序员的角度来看,Func(NULL)应该调用的是Func(char *)但实际上NULL的值是0,所以调用了Func(int)。nullptr关键字真是为了解决这个问题而引入的。

第七章 异常

7.1 异常的基本概念

异常是程序在执行期间产生的问题。C++ 异常是指在程序运行时发生的特殊情况,比如操作容器时下标越界等问题。

C++ 异常处理涉及到三个关键字:trycatchthrow

  • throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。

  • catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常

  • try: 尝试执行语句块,它后面通常跟着一个或多个 catch 块

如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。使用 try/catch 语句的语法如下所示:

#include <iostream>
using namespace std;

class MyException1 {}; // 自定义异常类1
class MyException2 {}; // 自定义异常类2

int main()
{
    try
    {
        // 保护代码,可能会抛出异常
        // 例如:throw MyException1(); // 抛出自定义异常1
        // 或:throw MyException2(); // 抛出自定义异常2
    }
    catch (MyException1 e1)
    {
        // 捕获 MyException1 类型的异常,并进行处理
    }
    catch (MyException2 e2)
    {
        // 捕获 MyException2 类型的异常,并进行处理
    }
    catch (...)
    {
        // 捕获其他类型的异常(不是 MyException1 或 MyException2)
    }

    return 0;
}

如果 try 块在不同的情境下会抛出不同的异常,这个时候可以尝试罗列多个 catch 语句,用于捕获不同类型的异常

7.2 抛出异常

您可以使用 throw 语句在代码块中的任何地方抛出异常。throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。

以下是尝试除以零时抛出异常的实例:

double division(int a, int b)
{
   if( b == 0 )
   {
      throw "Division by zero condition!";
   }
   return (a/b);
}  

7.3 捕获异常

catch 块跟在 try 块后面,用于捕获异常。您可以指定想要捕捉的异常类型,这是由 catch 关键字后的括号内的异常声明决定的。

try
{
   // 保护代码
}catch( ExceptionName e )
{
  // 处理 ExceptionName 异常的代码
}

上面的代码会捕获一个类型为 ExceptionName 的异常。如果您想让 catch 块能够处理 try 块抛出的任何类型的异常,则必须在异常声明的括号内使用省略号 ...,如下所示

try
{
   // 保护代码
}catch(...)
{
  // 能处理任何异常的代码
}

哦?还有这么高级的!意思是如果在try代码块中产生了除0的异常,catch语句块也能捕获到洛?

Let me try try ...

try
	{
	   // 保护代码
	   int x = 10/0;
	}catch(...)
	{
	  // 能处理任何异常的代码
	  cout << "10/0" << endl;
	}

运行结果我们发现,并不是我们所想象的,程序还是因为10/0操作而异常终止了,所以这种方式并不能处理系统产生的异常,而只能获取我们使用throw抛出的异常,例如:

try
	{
	    // 保护代码
	    // int x = 10/0;
	 	throw "Division by zero condition!";
	 	cout << "hello" << endl;
	}catch(...)
	{
	  // 能处理任何异常的代码
	  cout << "10/0" << endl;
	}

在程序开发过程中我们常常会抛出很多异常,我们该如何精准得捕获到底是哪个异常发生了呢?由于我们抛出的异常为字符串,所以,当捕获该异常时,我们必须在 catch 块中使用 const char*

#include <iostream>
using namespace std;

double division(int a, int b)
{
    if(b == 0)
    {throw "Division by zero condition!";}
    return (a / b);
}

double division1(int a, int b)
{
    if(b == 0)
    {throw "hahaha++++++++++++++++++ ";}
    return (a/b);
}

int main()
{
    int x = 50;
    int y = 0;
    double z = 0;

    try{
        z = division(x, y);
        z = division1(x, y);
        cout << "z:" << endl;
    } catch (const char *msg) {
        cerr << msg << endl;
    }
    return 0;
}

7.4 C++ 标准的异常

如果程序运行时抛出的是系统异常,我们又该如果捕获呢?

C++ 提供了一系列标准的异常,定义在 &#x3c;exception> 中,我们可以在程序中使用这些标准的异常。它们是以父子类层次结构组织起来的。

下表是对上面层次结构中出现的每个异常的说明:

7.5 定义新的异常

您可以通过继承和重载 exception 类来定义新的异常。下面的实例演示了如何使用 std::exception 类来实现自己的异常。

#include <iostream>
#include <exception>
using namespace std;
 
class MyException : public exception
{
public:
  const char * what () const throw ()
  {
    return "C++ Exception";
  }
};
 
int main()
{
  try
  {
    throw MyException();
  }
  catch(MyException& e)
  {
    cout << "MyException caught" << endl;
    cout << e.what() << endl;
  }
  catch(exception& e)
  {
    //其他的错误
  }
}

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

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

相关文章

adobe ps beta的使用方法

1、人物换发型。 1&#xff09;套索套选出来相关的头发。 2&#xff09;点击生成&#xff0c;输入“red hair” 按“生成”键。 2、人物换眼睛。 1&#xff09;套索套选出来相关的眼睛区域&#xff0c;大一点范围。 2&#xff09;点击生成&#xff0c;输入“blue eyes"…

【Golang 接口自动化03】 解析接口返回XML

目录 解析接口返回数据 定义结构体 解析函数&#xff1a; 测试 优化 资料获取方法 上一篇我们学习了怎么发送各种数据类型的http请求&#xff0c;这一篇我们来介绍怎么来解析接口返回的XML的数据。 解析接口返回数据 定义结构体 假设我们现在有一个接口返回的数据resp如…

分布式软件架构——内容分发网络

内容分发网络&#xff08;CDN&#xff0c;Content Distribution Network或Content Delivery Network&#xff09; 其基本思路是尽可能避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节&#xff0c;使内容传输得更快、更稳定。通过在网络各处放置节点服务器所构成的在现…

【数据结构与算法】归并排序

归并排序 归并排序&#xff08;MERGE-SORT&#xff09;是利用归并的思想实现的排序方法&#xff0c;该算法采用经典的分治&#xff08;divide-and-conquer&#xff09;策略&#xff08;分治法将问题分&#xff08;divide&#xff09;成一些小的问题然后递归求解&#xff0c;而…

LaTex语法(常用数学符号的语法和注意事项)

说明:[]括号表示把语法括起来&#xff0c;并不表示LaTex语法。 1. 求和符号(Σ) 这个符号的基本语法为&#xff1a;[\sum_{}^{}]。 符号有两种模式&#xff1a;内联数学模式(inside math mode)和显示数学模式(displayed math mode)。 内联数学模式&#xff1a;排版时使用各…

CAN通信的位定时与同步

位定时与同步 1.位时间 1.1相关基本概念 1&#xff09;系统时钟&#xff1a;记为 t c l k t_{clk} tclk​&#xff1b; 2&#xff09;CAN时钟周期&#xff1a;CAN时钟是由系统时钟分频而来的一个时间长度值&#xff0c;表示CAN控制器的工作时钟&#xff0c;实际上就是一个时…

【前端知识】React 基础巩固(四十二)——React Hooks的介绍

React 基础巩固(四十二)——React Hooks的介绍 一、为什么需要Hook? Hook 是 React 16.8 的新增特性&#xff0c;它可以让我们在不编写class的情况下使用state以及其他的React特性&#xff08;比如生命周期&#xff09;。 class组件 VS 函数式组件&#xff1a; class的优势…

C语言每日一题:9.《数据结构》链表的中间节点+链表的倒数第k个节点。

第一题&#xff1a; 题目链接&#xff1a; >思路一&#xff1a; 1.第一遍遍历链表&#xff0c;直到找到尾结束第一次遍历&#xff0c;遍历的过程中记录链表长度。定义长度为k。 2.确定中间是第几个节点&#xff0c;计算是k/21根据题目要求。 3.再一次去遍历我们的数组&…

iOS开发-格式化时间显示刚刚几分钟前几小时前等

iOS开发-格式化时间显示刚刚几分钟前几小时前等 在开发中经常遇到从服务端获取的时间戳&#xff0c;需要转换显示刚刚、几分钟前、几小时前、几天前、年月日等格式。 主要用到了NSCalendar、NSDateComponents这两个类 NSString *result nil;NSCalendarUnit components (NSC…

minGPT 代码详解(训练 GPT 模型执行两位数加法)

文章目录 1. MinGPT 项目简介2. 相关论文2.1 GPT-12.2 GPT-22.3 GPT-3 3. 代码详解3.1 项目结构3.2 GPT 模型代码详解3.2.1 Transformer block3.2.2 GPT 3.3 两位数加法实验3.3.1 数据集构造3.3.2 训练器3.3.3 模型参数设置3.3.4 训练过程 1. MinGPT 项目简介 MinGPT 是 GPT 模…

【Linux】关于Bad magic number in super-block 当尝试打开/dev/sda1 时找不到有效的文件系统超级块

每个区段与 superblock 的信息都可以使用 dumpe2fs 这个指令来查询的&#xff01; 不过可惜的是&#xff0c;我们的 CentOS 7 现在是以 xfs 为默认文件系统&#xff0c; 所以目前你的系统应该无法使用 dumpe2fs 去查询任何文件系统的。 因为目前两个版本系统的根目录使用的文…

Servlet文件的下载

第一种方法直接在前端使用超链接&#xff0c;也就是a标签 浏览器不能识别会直接下载&#xff08;像压缩文件不能直接下载&#xff09;&#xff0c;浏览器能识别&#xff0c;想要下载加一个download属性。download可以不写任何信息。 首先在web下建一个文件&#xff0c;放需要…

Vue 3:玩一下web前端技术(七)

前言 本章内容为VUE生命周期与相关技术讨论。 上一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;六&#xff09;_Lion King的博客-CSDN博客 下一篇文章地址&#xff1a; Vue 3&#xff1a;玩一下web前端技术&#xff08;八&#xff09;_Lion Ki…

9、测试Service组件和使用模拟组件辅助测试

测试Service组件和使用模拟组件辅助测试 测试Service组件 测试Service组件无需启动Web服务器&#xff0c;所以使用SpringBootTest(webEnvironment WebEnvironment.NONE)修饰测试用例类即可 &#xff08;用NONE表示不启动Web服务器&#xff09;。 Service组件其实就是一个普…

【002 操作系统】进程的状态及状态转换图?

一、进程的状态 1. 创建状态 2. 就绪状态 3. 运行状态 4. 阻塞状态 5. 终止状态 图源&#xff1a;进程、线程基础知识全家桶&#xff0c;30 张图一套带走_Linux_小林coding_InfoQ写作社区 NULL -> 创建状态&#xff1a;一个新进程被创建时的第一个状态&#xff1b; 创建状态…

python+django+mysql项目实践一(环境准备)

python项目实践 环境说明: Pycharm 开发环境 Django 前端 MySQL 数据库 Navicat 数据库管理 创建Pycharm项目 安装Django 在pycharm文件—设置进行安装 新建Django项目 注意项目创建目录 项目默认目录文件说明: __init__.py asgi.py 【异步接受网络…

Qt 5. QSerialPort串口收发

1. 代码 //ex2.cpp #include "ex2.h" #include "ui_ex2.h" #include <QtSerialPort/QSerialPort> #include <QtSerialPort/QSerialPortInfo>int static cnt 0;Ex2::Ex2(QWidget *parent): QDialog(parent), ui(new Ui::Ex2) {ui->setupUi…

Win11的dev通道更新Build23493版本后启用Windows Copilot的解决办法

博客嘛&#xff0c;多偷懒少打字&#xff0c;先上图&#xff1a; 首先是微软宣布了对dev通道版本推送了Windows Copilot for Windows 11&#xff0c;但是相信像我这样的小白想体验又对win一窍不通的人应该也有不少&#xff0c;经历了一次重装&#xff0c;五次版本的回退再更新后…

opencv rtsp 硬件解码

讨论使用opencv的reader 硬件解码的方案有太多种&#xff0c;如果使用ffmpeg硬件解码是最方便的&#xff0c;不方便的是把解码过后的GPU 拉到 CPU 上&#xff0c;再使用opencv的Mat 从cpu 上上载到gpu上&#xff0c;是不是多了两个过程&#xff0c;应该是直接从GPU mat 直接去…

从Bean的生命周期分析Dubbo的源码

写作目的 Dubbo作为RPC中的经典落地实践&#xff0c;作为阿里内部目前还是大规模使用的基础框架&#xff0c;作为CRUD的底层。无论从什么角度来看简单的阅读一下Dubbo的源码还是有必要的。 前提&#xff1a;要了解Bean的生命周期 源码下载 gitee源码下载 源码分析 开启Dub…