【C++ 初阶】命名空间 / 输入输出 / 缺省参数 / 函数重载

【C++ 初阶】命名空间 / 输入输出 / 缺省参数 / 函数重载

在这里插入图片描述

🎬 博主名称键盘敲碎了雾霭
🔥 个人专栏: 《C语言》《数据结构》《C++》

⛺️指尖敲代码,雾霭皆可破


在这里插入图片描述

文章目录

一、namespace

1.1 namespace的引入

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染,
C语言错误示范:

#include<stdio.h>#include<stdlib.h>int rand =6;intmain(){printf("%d", rand);}
在这里插入图片描述


c语言项目类似下面程序这样的命名冲突是普遍存在的问题,C++引入namespace就是为了更好的解决
C++优化

#include<stdio.h>namespace m {int rand =6;}intmain(){printf("%d", m::rand);}

1.2 namespace的定义

  • 定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对即可,中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
namespace m {int a =6;intAdd(int a,int b){return a + b;}structNode{structNode* next;int val;};}
  • namespace本质是定义出⼀个域,这个域跟全局域各自独立,不同的域可以定义同名变量
  • C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
  • namespace只能定义在全局,当然他还可以嵌套定义。
namespace m {namespace n {int a =0;int b =9;}int c =6;}
  • 项目工程中多文件中定义的同名namespace会认为是一个namespace,不会冲突。
  • C++标准库都放在一个叫std(standard)的命名空间中。

1.3 namespace使用

编译查找一个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找
我们要使用命名空间中定义的变量/函数,有三种方式:

  • 指定命名空间访问,项目中推荐这种方式。
std::cout 
  • using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。
using m::a;
  • 展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。
usingnamespace std;

二、输入输出

#include<iostream>intmain(){int a =0;double b =0; std::cin >> a >> b; std::cout << a <<" "<< b << std::endl;}
  • 是Input Output Stream 的缩写,是标准的输入、输出流库,定义了标准的输入、输出对象
  • std::cin是istream类的对象,它主要面向窄字符(narrow characters(of type char))的标准输入流
  • td::cout是ostream类的对象,它主要面向窄字符的标准输出流
  • std::endl是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。
  • <<是流插入运算符,>>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)
  • C++的输入输出可以自动识别变量类型,不需要像printf/scanf输入输出时那样,需要手动指定格式
  • cout/cin/endl等都属于C++标准库,C++标准库都放在一个叫std(standard)的命名空间中,所以要通过命名空间的使用方式去用他们。
  • 一般日常练习中我们可以using namespace std,实际项目开发中不建议using namespace std
  • VS上<iostream>间接包含<stdio.h>,也可以使用printf和scanf
    在io需求比较高的地方,如部分大量输入的竞赛题中,加上以下3行代码:
#include<iostream>usingnamespace std;intmain(){ ios_base::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);}

三、缺省参数

  • 缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。
#include<iostream>usingnamespace std;intAdd(int a =1,int b =2){return a + b;}intmain(){ cout <<Add(4,3)<< endl; cout <<Add()<< endl;return0;}
  • 全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。并且带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参
#include<iostream>usingnamespace std;intAdd(int a ,int b =2){return a + b;}intmain(){ cout <<Add(4,3)<< endl; cout <<Add(2)<< endl;return0;}

函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值

usingnamespace std;intAdd(int a,int b=2);intAdd(int a ,int b){return a + b;}intmain(){ cout <<Add(4,3)<< endl; cout <<Add(2)<< endl;return0;}

四、函数重载

C++支持在同一作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调用就表现出了多态行为,使用更灵活。C语言是不支持同一作用域中出现同名函数的

4.1 参数类型不同

#include<iostream>usingnamespace std;voidAdd(int a ,int b){ cout << a + b << endl;}voidAdd(double a ,double b){ cout << a + b << endl;}intmain(){Add(3,5);Add(4.0,7.0);}

4.2 参数个数不同

#include<iostream> using namespace std;voidfuc(){ cout <<"haha"<< endl;}voidfuc(int a){ cout <<"hehe"<< endl;}intmain(){fuc();fuc(4);}

4.3 参数类型顺序不同

#include<iostream>usingnamespace std;voidfuc(int a,char b){ cout <<"haha"<< endl;}voidfuc(char b,int a){ cout <<"hehe"<< endl;}intmain(){fuc(1,'0');fuc('0',4);}

4.4 错误点

  • 返回值不同不能作为重载条件,因为调用时也无法区分
  • 有缺省参数时,会报错,存在歧义,编译器不知道调用谁
usingnamespace std;voidf1(){ cout <<"f()"<< endl;}voidf1(int a =10){ cout <<"f(int a)"<< endl;}

解决方法

#include<iostream>usingnamespace std;voidf1(){ cout <<"f()"<< endl;}namespace m {voidf1(int a =10){ cout <<"f(int a)"<< endl;}}intmain(){f1(); m::f1();}

五、引用

5.1 引用的概念和定义

类型&引用别名=引用对象;

usingnamespace std;intmain(){int a =0;int& b = a;int& c = b;int& d = a; c++; cout << a <<" "<< b <<" "<< c<<" "<< d << endl;return0;}
在这里插入图片描述

5.2 引用的特性

  • 引用在定义时必须初始化
int& b;//错误写法
  • 一个变量可以有多个引用
  • 引用一旦引用一个实体,再不能引用其他实体(不能改变指向)
#include<iostream>usingnamespace std;intmain(){int a =10;int& b = a;int c =20; b = c;//赋值操作,不能引用其他实体 cout << a <<" "<< b<<" "<< c << endl;}

5.3 引用的使用

  • 引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被引用对象。
#include<iostream>usingnamespace std;typedefint STDataType;typedefstructStack{ STDataType* a;int top;int capacity;}ST;voidSTInit(ST& rs,int n =4){ rs.a =(STDataType*)malloc(n *sizeof(STDataType)); rs.top =0; rs.capacity = n;}voidSTPush(ST& rs, STDataType x){if(rs.capacity == rs.top){int newcapacity =(rs.capacity ==0?4: rs.capacity *2); STDataType*ptr=(STDataType*)realloc(rs.a,sizeof(STDataType)* newcapacity);if(ptr ==NULL){perror("realloc");return;} rs.capacity = newcapacity;} rs.a[rs.top++]= x;}int&STTop(ST& rs){return rs.a[rs.top-1];}voidSTDestroy(ST& rs){if(rs.a ==NULL){return;}free(rs.a); rs.a =nullptr; rs.capacity = rs.top =0;}intmain(){ ST rs;STInit(rs);STPush(rs,1);STPush(rs,2);STPush(rs,3); cout <<STTop(rs)<< endl;STTop(rs)=10; cout <<STTop(rs)<< endl;return0;}
  • 引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引用跟其他语言的引l用(如Java)是有很大的区别的,除了用法,最大的点,C++引用定义后不能改变指向,Java的引用可以改变指向。

5.4 const引用

  • 可以引l用一个const对象,但是必须用const引l用。const引l用也可以引l用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大。
#include<iostream>intmain(){int a =1;constint& b = a;constint c =1;//只读不写constint& d = c;}
  • 在类型转换中会产生临时对象存储中间值,而C++规定临时对象具有常性,所以这里就触发了权限放大,必须要用常引用才可以。
double a =1.23;constint& b = a;
  • 所谓临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,C++中把这个未命名对象叫做临时对象。(函数返回值也有临时对象,具有常性)
int a =0;int b =2;constint& c = a + b;

六、指针和引用

  • 用是一个变量的取别名不开空间,指针是存储一个变量地址,要开空间
  • 用在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
  • 用在初始化时引用一个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。(不能代替指针)
  • 可以直接访问指向对象,指针需要解引用才是访问指向对象
  • sizeof中含义不同,引用结果为引l用类型的大小,但指针始终是地址空间所占字节个数
  • 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全一些。(相对安全,当变量在函数里面定义,却把引用返回来,类似于空指针)

七、inline

  • 用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈顿了,就可以提高效率
  • inline适用于频繁调用的短小函数,对于递归函数,代码相对多一些的函数,加上inline也会被编译器忽略。
inlinevoidSwap(int& a,int& b){int tmp = a; a = b; b = tmp;}
  • C语言实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不方便调试,C++设计了inline目的就是替代C的宏函数
  • inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。并且编译器在展开时找不到函数体。

vs编译器debug版本下面默认是不展开inline的,这样方便调试,debug版本想展开需要设置一下以下两个地方。

在这里插入图片描述


在这里插入图片描述


更改前:

在这里插入图片描述


更改后:

在这里插入图片描述

八、nullptr

8.1 问题引入

本想通过fuc(NULL)调用指针版本的fuc(int*)函数,但是由于NULL被定义成0,调用了fuc()因此与程序的初衷相悖。

#include<iostream>usingnamespace std;voidfuc(int a){ cout <<"haha"<< endl;}voidfux(int* a){ cout <<"haha"<< endl;}intmain(){fuc(0);fuc(NULL);fuc((void*)0);}

8.2 原因分析

C++中NULL可能被定义为字面常量0,或者C中被定义为无类型指针(void*)的常量。

#ifndefNULL#ifdef-_cplusplus#defineNULL0#else#defineNULL((void*)0)#endif#endif

8.3 解决方法

C++11中引入nullptrnullptr是一个特殊的关键字,nullptr是一种特殊类型的字面量,它可以转换成任意其他类型的指针类型(void*)在C++中不能隐式转换,在C语言中可以。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,而不能被转换为整数类型。

fuc(nullptr);

文章结语

感谢你读到这里~我是「键盘敲碎了雾霭」,愿这篇文字帮你敲开了技术里的小迷雾 💻

如果内容对你有一点点帮助,不妨给个暖心三连吧👇
👍 点赞 | ❤️ 收藏 | ⭐ 关注
(听说三连的小伙伴,代码一次编译过,bug绕着走~)

你的支持,就是我继续敲碎技术雾霭的最大动力 🚀

🐶 小彩蛋:

 /^ ^\ / 0 0 \ V\ Y /V / - \ / | V__) || 

摸一摸毛茸茸的小狗,赶走所有疲惫和bug~我们下篇见 ✨

Read more

Flutter 三方库 fsrs 突破鸿蒙端智能认知交互模型的高频动态复习算法引擎适配:搭建复杂离线记忆曲线追踪体系全息掌握大脑突触留存衰退参数助力超效在线学习-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 fsrs 突破鸿蒙端智能认知交互模型的高频动态复习算法引擎适配:搭建复杂离线记忆曲线追踪体系全息掌握大脑突触留存衰退参数助力超效在线学习-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 fsrs 突破鸿蒙端智能认知交互模型的高频动态复习算法引擎适配:搭建复杂离线记忆曲线追踪体系全息掌握大脑突触留存衰退参数助力超效在线学习 前言 在 OpenHarmony 智慧教育与个人效能类应用开发中,如何帮助用户高效记忆海量知识点(如单词、医学条目、法律条文)?如果仅仅采用简单的均匀复习,学习效率会由于大量重复已知内容而极其低下。fsrs(Free Spaced Repetition Scheduler)算法库为开发者提供了一套比传统的 Anki (SM-2) 更先进、基于 DSR 模型(Difficulty, Stability, Retrievability)的现代间隔重复调度算法。本文将实战介绍如何在鸿蒙端利用该算法构建一个顶级水平的学习大脑。 一、原直线性 / 概念介绍 1.1 基础原理/概念介绍 fsrs 的核心逻辑是基于 基于三个动态指标的三阶模型:难度 (Difficulty)、稳定性

By Ne0inhk
python基于逻辑回归的信用卡评分模型研究

python基于逻辑回归的信用卡评分模型研究

文章目录 * 前言 * 一、项目介绍 * 二、功能介绍 * 三、核心代码 * 四、效果图 * 源码获取 前言 在金融行业中,信用卡评分模型是评估客户信用风险、辅助信贷决策的重要工具。逻辑回归作为一种经典的分类算法,因其实现简单、解释性强、预测性能稳定等优点,在信用卡评分领域得到了广泛应用。Python作为一种功能强大且易于上手的编程语言,结合其丰富的数据处理和机器学习库(如Pandas、Scikit-learn等),为构建基于逻辑回归的信用卡评分模型提供了有力支持。 一、项目介绍 开发语言:Python python框架:Django 软件版本:python3.7/python3.8 数据库:mysql 5.7或更高版本 数据库工具:Navicat11 开发软件:PyCharm/vs code 二、功能介绍 Python基于逻辑回归的信用卡评分模型研究,是利用Python编程语言结合逻辑回归算法,

By Ne0inhk
告别 Python 多版本环境冲突!Anaconda 虚拟环境实操指南

告别 Python 多版本环境冲突!Anaconda 虚拟环境实操指南

日常 Python 开发中,不少开发者都会遇到一个头疼的问题:不同项目依赖不同版本的 Python 解释器或第三方包,直接全局安装会导致版本覆盖、依赖不兼容,轻则命令执行失败,重则项目直接报错。本文就来拆解 Python 多版本环境冲突的根源,并给出新手也能轻松上手的解决方案 —— 用 Anaconda 打造隔离的虚拟环境。 一、为什么会出现多版本环境冲突? Python 环境冲突的核心根源很简单: * 全局环境只有一个,不同项目对 Python 版本(如 3.8、3.10)、第三方包版本(如 numpy 1.21、numpy 1.24)的需求不同; * 直接在全局环境安装 / 升级包,会覆盖原有版本,导致依赖旧版本的项目运行异常; * 手动管理多个 Python 安装包,不仅路径易混乱,环境变量配置也容易相互干扰。 举个常见场景:

By Ne0inhk