C++入门知识【超详解】

news2024/11/24 3:46:54

目录

    • 1.认识C++
      • hello world
      • C++关键字
    • 2.命名空间
    • 3.std标准库
    • 4.输入输出
    • 5.缺省参数
    • 6.函数重载
    • 7.引用
      • 7.1引用的概念
      • 7.2引用的场景
        • 1.作参数
        • 2.作返回值
      • 7.3引用的注意点
      • 7.4指针和引用的区别
    • 8.auto关键字
    • 9.基于范围的for循环
    • 10.内联函数
      • 10.1概念
      • 10.2特征
    • 11. C++98中的指针空值

1.认识C++

hello world

#include<iostream>
int main() {
	std::cout << "hello world" << std::endl;
	return 0;
}

C++关键字

C语言32个关键字,而C++总计63个关键字

image-20221203165237334

2.命名空间

对于上述问题,即是C语言常见的命名冲突问题,C++中用命名空间来解决

命名空间是

变量查找规则:先在局部找,再全局找

在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称将都存 在于全局作用域中,可能会导致很多冲突。使用命名空间的目的是对标识符的名称进行本地化, 以避免命名冲突或名字污染 , namespace关键字的出现就是针对这种问题的。

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可, {} 中即为命名空间的成员

默认查找规则:先在局部找,再全局找

#include<stdio.h>
#include<stdlib.h>
namespace A {
	int rand = 10;
	int x = 1;
}

int main() {
	printf("%p\n", rand);
	printf("%d\n", A::rand);
	printf("%d\n", A::x);

	return 0;
}

image-20221202105417071

namespace域不会影响生命周期,相当于全局变量,放在静态域中;在预处理完后,头文件展开,rand未找到局部变量的定义,因此在头文件声明展开后的全局找到了它;而我们用到的**::即为作用域限定符**,相当于在查找变量时,在指定空间查找

变量定义在全局和定义在命名空间的区别?防止冲突

1.命名空间中可以定义变量/函数/类型,可以嵌套

namespace N1
{
	int a;
	int b;
	int Add(int left, int right)
	{
		return left + right;
	}

	namespace N2
	{
		int c;
		int d;
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}

对于嵌套的命名空间,访问时应:

N1::a=1;
N1::N2::c=2;

2.同一个工程中允许存在多个相同名称的命名空间 ,编译器最后会自动合并到同一个命名空间中

3.std标准库

std是C++标准库的命名空间,如何展开std使用更合理呢?

1.在日常练习中,建议直接using namespace std即可,这样就很方便

image-20221202111119573

2.using namespace std展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型/对象/函数,就存在冲突问题,因此我们不展开std,而是使用时加上std作用域限定符

image-20221202111051162

3.指定展开:常用的展开,自己定义的时候避免跟常用重名即可

image-20221202111539755

总结:命名空间std尽量避免全部展开,防止命名冲突

4.输入输出

1.使用cout标准输出对象(控制台:Console)和cin标准输入对象**(键盘)时,必须包含< iostream>头文件 以及按命名空间使用方法使用std**

2.cout和cin是全局的流对象, endl是特殊的C++符号,表示换行输出,他们都包含在包含< iostream >头文件中。

3.**<<**是流插入运算符, **>>**是流提取运算符。

4.使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式;C++的输入输出可以自动识别变量类型

#include <iostream>
using namespace std;

int main()
{
	int a;
	double b;
	char c;

	// 可以自动识别变量的类型
	cin >> a;
	cin >> b >> c;

	cout << a << endl;
	cout << b << "  " << c << endl;
	return 0;
}

C++的输入输出虽然方便了,却不好精确控制输入输出格式(例如保留小数点后几位),但是C++兼容C语言,因此在处理这些问题时可以配合着C语言使用;对于值、字符串、空格等多种混输出时,printf同样的比cout更方便

5.缺省参数

using namespace std;
void Func(int a = 10, int b = 20, int c = 30) {
	cout << "a= " << a << endl;
	cout << "b= " << b << endl;
	cout << "c= " << c << endl;
}

1.全缺省调用

int main() {
	Func(); 
}

image-20221202132533334

2.半缺省调用(从左向右依次传)

int main() {
	Func(1); 
}

image-20221202132643751

缺省只能从右往左连续缺省

此时b和c缺省:

void Func(int a, int b = 20, int c = 30) {
	cout << "a= " << a << endl;
	cout << "b= " << b << endl;
	cout << "c= " << c << endl;
}

例如缺省的使用:

namespace S {
	typedef struct Stack
	{
		int* a;
		int top;
		int capacity;
	}ST;
	void StackInit(ST* ps, int defaultCapacity = 4) {
		ps->a = (int*)malloc(sizeof(int)*defaultCapacity);
		assert(ps->a);
		ps->top = 0;
		ps->capacity = defaultCapacity;
	}
}

int main() {
	//不知道要插入多少数据
	S::ST st1;
	S::StackInit(&st1);

	//知道要插入100个数据
	S::ST st2;
	S::StackInit(&st2, 100);
}

注意:缺省参数不能在函数声明和定义中同时出现,规定只能在声明时(.h)去定义缺省参数

//a.h
void Func(int a = 10);

// a.cpp
void Func(int a = 20){
    
}
// 注意:如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

6.函数重载

函数重载是函数的一种特殊情况, C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(①参数个数或②类型或③类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。

int add(int x, int y) {
	return x + y;
}

double add(double x, double y) {
	return x + y;
}

int main() {
	cout << add(1, 2) << endl;
	cout << add(1.2, 1.5) << endl;

	return 0;
}

①C++能自动识别参数类型,本质也是函数重载支持的

②函数重载调用时应该明确调用哪个函数,重载+缺省调用会报错:二义性

③为什么C语言不支持重载,C++是怎么支持重载呢?——函数名修饰(name Mangling)

④返回值不同不能构成重载!原因是调用时的二义性,无法区分调用时不指定返回值类型

7.引用

7.1引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空 间,它和它引用的变量共用同一块内存空间

类型& 引用变量名(对象名) = 引用实体;

引用的存在,就能间接替代C语言当中的指针的部分功能了(无法完全替代指针)

例如我们常用的交换函数:

void Swap(int& m, int& n) {
	int tmp = m;
	m = n;
	n = tmp;
}

int main() {
	int a = 10;
	int b = 20;
	Swap(a, b);
	std::cout << "a=" << a << " " << "b=" << " " << b << std::endl;
}

image-20221202164913483

同理的,我们不仅能给一般变量取别名,也可以给指针变量取别名,因此可以代替二级指针

*&pa=**p;    //相当于我们用pa来代替*p,那么对于**p来说即为*pa

注意:

①引用在定义时必须初始化

② 一个变量可以有多个引用

③引用一旦引用一个实体,再不能引用其他实体(引用的指向不可改变)

7.2引用的场景

1.作参数

一般用作输出型参数,C++中可用引用来替代指针;减少拷贝提高效率

void Swap1(int& left, int& right)
{
    int temp = left;
    left = right;
    right = temp;
}

2.作返回值

①引用返回

int& Count()
{	
    //只有静态才行:减少了一次拷贝
    static int n = 0;
    n++;
    // ...
    return n;
}

②传值返回

int Count()
{
    static int n = 0;
    n++;
    // ...
    return n;
}

那么引用返回和传值返回有什么区别呢?

在传值返回时,会将返回值拷贝到一个临时变量(寄存器或上一层栈帧中);而利用引用返回时,也可以理解为存在一个临时变量,它的类型仍是引用类型,相当于返回了一个返回值n的别名,但是由于返回时原返回值n的栈已经销毁了,而返回的它的别名仍然指向的这一片空间;

内存空间销毁意味着什么?

①空间还在吗?在,只是使用权不是我们的,我们存的数据不被保护

②我们还能访问吗?能,只是我们读写的数据都是不确定的

为什么会出现100?main函数调用了Func函数,恰好和调用Count位于同一块空间,ret是这块空间的别名,因此它的值变成了100;本质即是非法空间的访问!这也说明了空间是可以重复使用的

【结论】:出了函数作用域,返回变量不存在了,不能用引用返回,因为引用返回的结果是未定义的;正确使用引用返回值是对于静态声明的变量,返回变量还存在,这种情况就可以使用,使用它的意义即是在返回值传递时不用再开辟临时拷贝变量,提高效率

通过设计一个简单的程序,可以发现确实能提高效率:

image-20221203153311159

7.3引用的注意点

1.指针和引用赋值中,权限可以缩小,但是不能放大

这里b是只读的,a是可读可写的

对于权限缩小是允许的:

需要注意的是,权限的放大缩小只对于指针或引用来说的,正常的赋值是无影响的

int a=1;
const int b=0;
a=b;    //right

这里是将b的值传递给a,a值的改变并不会影响b,其本质即是它们不是指针/引用变量,值的改变对互相无影响,这也是指针/引用赋值中有权限放大缩小的概念;

那么对于以后,一般用引用参数都是用const引用,但是如果你要修改这个参数,不用const就行了


2.引用不能使用缺省参数,因为缺省参数一般是常量,但是加上const即可使用

void func(const int& N=10){       //right
    
}

3.引用时涉及强制类型转换需要用到常引用

强制类型转换会产生临时变量:例如我们将double类型的a转换为int类型,我们输出10,但是我们修改的不是并不是a,输出的是产生的临时变量

临时变量具有常性,无法被修改,因此在引用时也要定义常引用

int& ra1 = a;           //wrong
const int& ra2 = a;     //right

这里的ra2不是a的别名了,而是这个临时变量的别名,打印ra2的值即可验证:


4.传值返回不能用引用变量接收

同理,传值返回涉及到临时变量的拷贝,临时变量具有常性,因此只能用const引用接收


5.语法层面上,ra是a的别名,不开空间;底层实现上,引用是使用指针实现的

7.4指针和引用的区别

①引用概念上定义一个变量的别名,指针存储一个变量地址。

②引用在定义时必须初始化,指针没有要求

③引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何 一个同类型实体

④没有NULL引用,但有NULL指针

⑤在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节,64位平台下占8个字节)

⑥引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小

⑦有多级指针,没有多级引用

⑧访问实体方式不同, 指针需要显式解引用,引用编译器自己处理

⑨引用比指针使用起来相对更安全

8.auto关键字

引入:随着程序越来越复杂,程序中用到的类型也越来越复杂,经常体现在

①类型难于拼写

②含义不明确导致容易出错

因此有了auto:一个类型指示符来指示编译器, auto声明的变量类型由编译器在编译时期推导而得

int a=10;
auto b=a;

定义变量时,auto声明的变量b,根据a的类型推导b的类型

9.基于范围的for循环

范围for循环的作用是方便遍历数组

int arr[]={1,2,3,4,5};
//依次取arr中数据赋值给e,自动判断结束,自动迭代
for(auto e:arr)
{
    cout<<e<<" ";
}
cout<<endl;

遍历结果为:

image-20221203163841585

10.内联函数

10.1概念

inline修饰的函数叫做内联函数, 编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,内联函数提升程序运行的效率

image-20221203210008096

例如对于冒泡排序、交换排序等,我们涉及到Swap交换函数,当数据量很大的时候就要多次调用Swap(频繁调用的小函数),即多次调用栈帧;C语言中通过宏函数可以避免栈帧消耗(预处理替换,优化)

而对于C++来说,可以通过inline函数(内联函数)来解决

>为什么不继续使用C语言的宏?①不能调试②没有类型安全检查③容易出错

>例如ADD的宏函数

#define ADD(int x,int y)  ((x)+(y))

10.2特征

1.inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。

2.inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现),不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性

是否展开取决于编译器,函数过长强行展开会引起代码膨胀

代码量决定了可执行程序的大小,在开发中,编译出的程序即可以被理解为安装包

3.在debug模式下,内联不会默认展开,通过修改设置可以展开

>使用时直接在函数前加关键字inline即可

inline ADD(int a,int b){
    return a+b;
}

4.inline不在声明和定义分离,在.h文件中声明后,预处理后会在.cpp文件展开;inline被展开,没有函数地址,链接(地址)就会找不到

11. C++98中的指针空值

在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针;如果指针没有合法的指向,我们基本都是按照如下 方式对其进行初始化:

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

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL
#else
#define NULL
#endif
#endif

可以看到, NULL可能被定义为字面常量0,或者被定义为无类型指针(void)的常量

由于语言具有向前兼容的特点,因此在C++98中的指针空值问题延续到了现在

解决方案为:关键字nullptr,表示指针空值;因此在以后,我们均使用nullptr来表示空指针

return a+b;
}


4.inline不在声明和定义分离,在.h文件中声明后,预处理后会在.cpp文件展开;inline被展开,没有函数地址,链接(地址)就会找不到

## 11. C++98中的指针空值

> 在良好的C/C++编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针;如果指针没有合法的指向,我们基本都是按照如下 方式对其进行初始化:

```cpp
int* p1 = NULL;
int* p2 = 0;

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL
#else
#define NULL
#endif
#endif

可以看到, NULL可能被定义为字面常量0,或者被定义为无类型指针(void)的常量

由于语言具有向前兼容的特点,因此在C++98中的指针空值问题延续到了现在

解决方案为:关键字nullptr,表示指针空值;因此在以后,我们均使用nullptr来表示空指针

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

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

相关文章

数据结构——单链表(上)

&#x1f307;个人主页&#xff1a;_麦麦_ &#x1f4da;今日名言&#xff1a;“生活总是让我们遍体鳞伤&#xff0c;但到后来&#xff0c;那些受伤的地方一定会变成我们最强壮的地方。” ——海明威《永别了武器》 目录 ​编辑 一、前言 二、正言 3.1链表的概念及结构…

HMM(隐马尔科夫模型)-理论补充2

目录 一.大数定理 二.监督学习方法 1.初始概率 2.转移概率 3.观测概率 三.Baum-Welch算法 1.EM算法整体框架 2. Baum-Welch算法 3.EM过程 4.极大化 5.初始状态概率 6.转移概率和观测概率 四.预测算法 1.预测的近似算法 2.Viterbi算法 1.定义 2. 递推&#xff1…

倒计时2天:中国工程院院士谭建荣等嘉宾确认出席,“警务+”时代来临...

近日伴随公安部、科技部联合印发通知&#xff0c;部署推进科技兴警三年行动计划&#xff08;2023-2025年&#xff09;&#xff0c;现代科技手段与警务工作相结合的方式&#xff0c;正式被定义为未来警务发展的新趋势。 21世纪以来&#xff0c;随着科技的不断发展和创新&#xf…

硬间隔支持向量机算法、软间隔支持向量机算法、非线性支持向量机算法详细介绍及其原理详解

相关文章 K近邻算法和KD树详细介绍及其原理详解朴素贝叶斯算法和拉普拉斯平滑详细介绍及其原理详解决策树算法和CART决策树算法详细介绍及其原理详解线性回归算法和逻辑斯谛回归算法详细介绍及其原理详解硬间隔支持向量机算法、软间隔支持向量机算法、非线性支持向量机算法详细…

JavaScript Date 日期对象实例合集

文章目录JavaScript Date 日期对象实例合集一&#xff0c;使用 Date() 方法获得当日的日期二&#xff0c;使用 getFullYear() 获取年份三&#xff0c;使用getTime() 返回从 1970 年 1 月 1 日至今的毫秒数四&#xff0c;如何使用 setFullYear() 设置具体的日期五&#xff0c;使…

小兔鲜注册页面验证、阶段案例(登录、首页页面)(重点)、小兔鲜放大镜效果——DOM

目录 1. 小兔鲜注册页面验证 2. 阶段案例&#xff08;登录、首页页面&#xff09;&#xff08;重点&#xff09; 3. 小兔鲜放大镜效果 1. 小兔鲜注册页面验证 验证码模块有个小问题&#xff1a; 连续点击获取验证码会导致触发多次计时器&#xff0c;会导致计时出现问题&…

【Stata】从入门到精通.零基础小白必学的教程,一学就fei

视频教程移步&#xff1a;https://www.bilibili.com/video/BV1hK4y1d714/?p4&spm_id_frompageDriver&vd_sourcecc8074e9c81a225f214226065db53d32P3 第二讲 Stata处理数据全流程&#xff08;上&#xff09; P3 - 01:37&#xfeff;内置数据 file example datasets使用…

FastDFS - 分布式文件存储系统

目录一、分布式文件存储1.分布式文件存储的由来2.常见的分布式存储框架二、FastDFS介绍三、FastDFS安装1.拉取镜像文件2.构建Tracker服务3.构建Storage服务4.测试图片上传四、客户端操作1.Fastdfs-java-client1.1 文件上传1.2 文件下载2.SpringBoot整合一、分布式文件存储 1.分…

【MySQL】什么是意向锁 IS IX 及值得学习的思想

文章目录前言行锁和表锁使用意向锁意向锁的算法意向锁的思想JDK 中相似的思想前言 之前看 MySQL 都刻意忽略掉了 IS 和 IX 锁&#xff0c;今天看 《MySQL 是怎样运行的》&#xff0c;把意向锁讲的很通透&#xff0c;本篇博文提炼一下思想。 I: Intention Lock&#xff08;意向…

自建服务器系列-0元搭建linux服务器(windows笔记本)

0元搭建linux服务器一.windows装Centos71.1 centos7 iso镜像1.2 准备U盘1.3 UltraISO 启动盘制作工具安装1.4 准备一台windows 机器1.5 安装过程二 、连接无线wifi三、固定wifi ip3.1 查看网络状态3.2 查看DNS3.3 查看GATEWAY3.4 设置静态IP四、一键快速安装单机版k8s五、申请域…

游戏高度可配置化:通用数据引擎(data-e)及其在模块化游戏开发中的应用构想图解

游戏高度可配置化&#xff1a;通数据引擎在模块化游戏开发中的应用构想图解 ygluu 码客 卢益贵 目录 一、前言 二、模块化与插件 1、常规模块化 2、插件式模块化&#xff08;插件开发&#xff09; 三、通用数据引擎理论与构成 1、名字系统&#xff08;数据类型&#xf…

数据结构与算法之树结构基础

目录为什么要使用树结构树结构基本概念树的种类树的存储与表示常见的一些树的应用场景为什么要使用树结构 线性结构中不论是数组还是链表&#xff0c;他们都存在着诟病&#xff1b;比如查找某个数必须从头开始查&#xff0c;消耗较多的时间。使用树结构&#xff0c;在插入和查…

58-59-60 - 动态内存分配的实现

---- 整理自狄泰软件唐佐林老师课程 文章目录1. 讨论2. 动态内存管理2.1 动态内存管理的关键2.2 动态内存管理的分类3. 定长内存管理的设计与实现3.1 空间划分3.2 内存申请和归还3.3 关键数据类型3.4 思考4. 变长内存管理的设计与实现4.1 空间划分4.2 内存申请和归还4.3 关键数…

802.11 MCS 的最低SNR分析

常常看到这样的表格: 那么这个SNR如何而来? 看看RSSI和SNR的关系,它们之间隔了一个noise floor。从表格看得出,这个底噪在-80~-90之间。 而SNR的核心,也有类似的原因,它和BER有关。

tkinter界面的TCP通信/开启线程等待接收数据

前言 用简洁的语言写一个可以与TCP客户端实时通信的界面。之前做了一个项目是要与PLC进行信息交互的界面&#xff0c;在测试的时候就利用TCP客户端来实验&#xff0c;文末会附上TCP客户端。本文分为三部分&#xff0c;第一部分是在界面向TCP发送数据&#xff0c;第二部分是接收…

【Python从入门到进阶】9、流程控制语句-条件语句(if-else)

接上篇《8、Python的输入输出》 上一篇我们学习了Python的输入和输出相关内容。本篇我们来学习Python的控制流语句。 一、流程控制语句的含义 之前我们分别学习过“变量及数据类型”、“运算符”&#xff0c;其中“变量及数据类型”相当于我们学习自然语言中的“字”&#xf…

【数据库系统概论】基础知识总结

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

Linux基础命令和工具使用详解

Linux基础命令和工具使用详解一、grep搜索字符二、find查找文件三、ls 显示文件四、wc命令计算字数五、uptime机器启动时间负载六、ulimit用户资源七、curl http八、scp远程拷贝九、dos2unix和unix2dos十、sed 行处理10.1、简单模式10.2、替换模式十一、awk 列处理11.1、打印某…

【C++从入门到放弃】类和对象(上)

&#x1f9d1;‍&#x1f4bb;作者&#xff1a; 情话0.0 &#x1f4dd;专栏&#xff1a;《C从入门到放弃》 &#x1f466;个人简介&#xff1a;一名双非编程菜鸟&#xff0c;在这里分享自己的编程学习笔记&#xff0c;欢迎大家的指正与点赞&#xff0c;谢谢&#xff01; 类和对…

JavaScript 高级1 :面向对象

JavaScript 高级1 &#xff1a;面向对象 Date: January 16, 2023 Text: 面向对象、ES6中类和对象、类的继承、面向对象案例 目标&#xff1a; 能够说出什么是面向对象 能够说出类和对象的关系 能够使用 class 创建自定义类型 能够说出什么是继承 面向对象编程介绍 面向过…