当前位置: 首页 > news >正文

C++基础(一)

目录

1.不同版本的hello word!

2.namespace和::域作用限定符以及using

2.1 namespace

 2.2::

2.3using用于展开域

3.C++输入和输出

4.缺省参数

 5.重载

6.引用

6.1引用介绍

6.2 引用的特性

注意:

 6.4 const 的引用

 6.5指针和引用的关系

 7.inline

 8.nullptr



1.不同版本的hello word!

还记得第一次写C语言的hello word吗

//text.c
#include<stdio.h>int main()
{printf("hello word!\n");return 0;
}

 这是C++版的

//text.cpp
#include<iostream>
using namespace std;
int main()
{cout << "hello word! \n" << endl;return 0;
}

C++ 诞生于c之后,c++兼容了c,我们重点了解c++中不同于c的部分

2.namespace和::域作用限定符以及using

 定义命名空间,需要用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{}中即为命名空间的成员。命名空间可以定义变量/函数/类型等。

2.1 namespace

namespace bit

{

//定义变量
int rand=10;

//定义函数

int Add ( int a , int b )

{

return a +b ;
}

//定义类型

 struct Node

{

struct Node* next;

int val;
};

}

 2.2::

调用域下面的变量/函数等

bit  ::rand

  • namespace本质是定义了一个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以下面的rand不存在冲突了
namespace text1 {int rand = 0;
}
namespace text2 {int rand = 10;
}

 C++中域有函数局部域,全局域,类域;域影响的是编译时语法查找一个变量/函数/类型出处(声明或定义)的逻辑,所以有了域隔离,名字冲突就解决了。局部域和全局域除了会影响编译查找逻辑,还会影响变量的声明周期,命名空间域和类域不影响变量声明周期。

//text.cpp
#include<iostream>
using namespace std;namespace bit 
{int rand;int a = 2;int Fun(float c, float d){return c * d;}
}
int Fun(int c ,int d )
{return c + d;
}
int a = 1;
int main()
{int a = 0;printf("%d\n", a);// a没有被域限定 代码从上往下走,main 函数下被重新赋值 a=0,所以输出0printf("%d\n", ::a);//a被::域限定符限定,但左边没有元素,默认为全局域,a=1所以输出1printf("%d\n", bit::a);//a被::域限定符限定,左边有域名,限定在自定义的域,在bit域中a=2,所以输出2
printf("%d\n",Fun(1,2));
// a没有被域限定 代码从上往下走,找到自定义函数Fun调用Fun实现相加,输出3
printf("%d\n", ::Fun(1, 2));
//a虽然有域限定符,但是并没有限定范围,还是全局域找到Fun实现相加,输出3printf("%d\n", bit::Fun(1,2));//a被::域限定符限定,左边有域名,限定在自定义的域,在bit域中找到Fun实现相乘,输出2}

 验证答案

namespace只能定义在全局,当然他还可以嵌套定义

//text.cpp
#include<iostream>
using namespace std;namespace bit 
{int rand;namespace aa{int rand=1;}namespace bb {int rand=0;}
}int a = 1;
int main()
{printf("%d\n", bit::aa::rand);printf("%d\n", bit::bb::rand);return 0;
}

项目工程中多个文件中定义的同名namespace会认为是一个namespace,不会冲突

C++标准库都放在一个叫std(stdndard)的命名空间中

2.3using用于展开域

using namespace std;
//展开std头文件
using namespace bit::rand;
//展开bit中的rand

 一般日常练习中我们可以using namespace std,世纪项目开发中不建议using namespace

3.C++输入和输出

  1.  <iostream>是Input Output Stream 的缩写,是标准的输入,输出流库,定义了标准的输入,输出对象
  2. std::cin是ostream类的对象,他主要面向窄字符(narrow character (of type char))的标准输入流
  3. std::cout是ostream类的对象,它主要面向窄字符的标准输出流
  4. std::endl 是一个函数,流插入输出流,相当于插入一个换行字符加刷新缓冲区
  5. <<是流插入运算符,>>是流提取运算符(c语言中的左移右移操作符)
  6. 使用C++输入输出更方便,不要像printf/scanf输入输出那样,需要手动指定格式,C++的输入输出可以自动识别变量类型(本质是痛过重载实现的),其实最重要的是C++的流能更好的支持只定义类型对象的输入和输出。
//text.cpp
#include<iostream>
using namespace std;int main()
{int i = 123;int j = 234;int x;int y;cin >> x >> y;//控制台手动输入x,ycout << i << j << endl;//输出i和j并且最后换行return 0;
}

但是一对<<  <<内只能有一个变量,否则编译器报错

在c++中我们没有包含<stdio.h>,也可以使用printf和scanf,在包含<iostream>间接包含了。vs编译器是这样的,但其他编译器会报错

4.缺省参数

  • 缺省参数是声明或定义函数时为函数指定一个确实值,在调用该函数时,如果没有指定实参则采用改形式参数的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数                                                                                                                                                                   全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省必须从右往左依次连续缺省,不能间隔条约给缺省值
  • 函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值
//text.cpp
#include<iostream>
using namespace std;
//全缺省
void Fun1(int a=10,int b=20,int c=30)
{cout << "全缺省" << endl;cout << "a=" << a << endl;cout << "b=" << b << endl;cout << "c=" << c << endl;}
//半缺省
void Fun2( int a ,int b=10 ,int c=20)
{cout << "半缺省" << endl;cout << "a=" << a << endl;cout << "b=" << b << endl;cout << "c=" << c << endl;
}void Fun3(int a, int b, int c)
{cout << "普通" << endl;cout << "a=" << a << endl;cout << "b=" << b << endl;cout << "c=" << c << endl;
}
int main()
{Fun1();//没有传参时,使用参数的默认值Fun2(10);//传参时,使用的指定值Fun3(10,20,30);//传入指定值return 0;
}

 5.重载

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

//text.cpp
#include<iostream>
using namespace std;
//1.参数类型不同
int Add(int a, int b)
{cout << "int Add(int a, int b)" << endl;return a + b;
}
double Add(double a, double b)
{cout << "double Add(double a, double b)" << endl;return a + b;
}
//2.参数个数不同
int Add(int a)
{cout << "int Add(int a)" << endl;return a;
}
double Add(double a, double b)
{cout << "double Add(double a, double b)" << endl;return a + b;
}
//3.参数类型顺序不同
void  fun(int a, char b)
{cout << "void fun(int a, char b)" << endl;}
void  fun(char b, int  a)
{cout << "void fun(char a, int b)" << endl;}
int main()
{cout << "参数类型不同" << endl;cout << Add(3,5)<< endl;cout << Add(3.1,5.0) << endl;cout << "参数个数不同" << endl;cout << Add(3) << endl;cout << Add(3.1, 5.0) << endl;cout << "参数类型顺序不同" << endl;fun(3, 'x');fun('x', 5);return 0;
}

 注意:返回值不能作为判断是否重载的依据因为容易产生歧义,如下面的代码f()调用

//text.cpp
#include<iostream>
using namespace std;
void f()
{
cout<<"f()<<endl;
}
void f(int a=10)
{
cout<<"f(int a=20)"<<endl;
}int main()
{f();
return 0;
}

 

6.引用

6.1引用介绍

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

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

//text.cpp
#include<iostream>
using namespace std;int main()
{int a = 0;//引用:b和c是a的别名cout << "a=" << a <<" " << &a << endl;int &b = a;cout << "b=" << b <<" " << &b << endl;int& c = a;cout << "c=" << c << " " << &c << endl;//也可以给b取别名,d还是相当于a的别名int& d = a;cout << "d=" << d<< " " << &d << endl;return 0;
}

 值一样,地址也一样

 只是取得“外号”罢了

 传统的交换函数

void Swap(int* x,int* y)
{
int tem=*x;
*y=*x;
*x=tem;
}

现在只要传引用就能解决

void Swap(int& rx,int&ry)
{
int tem=rx;
rx=ry;
ry=tem;}

6.2 引用的特性

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体
  4. 可以给引用取引用

//text.cpp
#include<iostream>
using namespace std;int main()
{int a = 0;int ra&;//编译器报错,未给初始化引用//一个变量可以有多个别名int &b = a;//引用:b和c是a的别名int& c = a;//也可以给b取别名,d还是相当于a的别名int& d = a;//d是a的别名//给别名取别名int& f = b;return 0;
}

 

注意:

  1. 引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时改变被引用对象
  2. 引用传参跟指针传参功能类似,引用传参相对更加方便
  3. 引用返回值的场景相对比较复杂
  4. 引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引用跟其他语言的引用有很大差别,除了用法,最大的点,C++引用定义后不能改变指向,JAVA的引用可以改变指向

 6.4 const 的引用

  • 可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的访问权限在引用过程中可以缩小,但是不能放大

不需要注意的是类似于int& rb=a*3; double d=12.34 ; int & rd=d;这样一些场景下a*3的值结果保存在一个临时对象中,int& rd=d 也是类似,在类型转换中会产生临时对象存储中间值,也就是说,rb和rd引用的都是临时对象,而C++规定的临时对象具有常性,所以在这里就触发了权限放大,必须要用常引用才可以

所谓临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,C++把这个未命名对象叫做临时对象

//text.cpp
#include<iostream>
using namespace std;int main()
{int a = 0; //定义整型a=0const int b = 10;//定义整型常量b=10int& ra = a;//给ra引用a,权限一样,没问题const int& raa = a;//给raa引用a ,权限缩小,也没问题int& rb = b;//给rb引用b,有const修饰到没有const修饰,权限放大,编译器报错const int& rbb = b;//给rbb引用b,有const修饰到const修饰,权限一致,没问题int& ab = (a + b);//编译器报错,表达式的结果存在临时对象里,临时对象具有常性const int& ab = (a + b);//编译通过int rd = b;int abb = a + b;//编译通过,表达式的结果虽然存在临时变量里,这里是将表达式的值拷贝一份给abb没有涉及权限的问题a++;ra++;//可以执行,没有const修饰rb++;//不存在rbb++;raa++;b++;//有const修饰,编译器报错double d = 12.34;int i = d;//隐式类型转换会产生临时变量,但是值得拷贝不影响int& ri = d; //编译不通过,double转换为int类似,C++规定会产生临时对象,临时对象具有常性,所以这里不加const,权限缩小const int& rii = d;//编译通过return 0;
}

注意:临时对象只有短暂的生命周期,但是被引用后,生命周期会延长直到引用对象销毁

 6.5指针和引用的关系

C++中指针和引用就像两个性格迥异的亲兄弟,指针是哥哥,引用是弟弟,在实践中他们相辅相成,功能具有重叠性,但是各有自己的特点,互相不可替代

  • 语法概念上引用是一个变量的取别名不开空间,指针是存储一个变量的地址,要开空间
  • 引用在定义时引用一个对象后,就不能再引用其他对象;而指针可以不断得改变指向对象
  • 引用可以直接访问指向对象,指针需要解引用才能访问指向对象
  • sizeof中含义不同,引用结果为引用类型大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节,64位平台下是8字节)
  • 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全一些

 

从汇编语言来看,引用本质就是指针 

 7.inline

  • 用inline修饰的函数叫做内敛函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内敛函数就需要建立栈帧了,提高效率
//text.cpp
#include<iostream>
using namespace std;inline int Add(int x, int y)
{int ret = x + y;ret += 1;ret += 1;ret += 1;ret += 1;ret += 1;return ret;
}
int main()
{int ret = Add(1, 2);cout << ret << endl;return 0;
}

看一下汇编语言 ,实现内敛了

  • inline对于编译器而言只是一个建议,也就是说,你加了inline编译器也可以选择在调用的地方不展示,不同的编译器关于inline什么情况展开各不相同,因为C++标准没有这个。inline适合用于频繁调用的短小函数,对于递归函数,代码相对较对一些的函数,加上inline也会被编译器忽略2

  • C语言实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错且不方便调试,C++设计了inline目的就是替代C的宏函数
  • VS编译器debug版本下面默认是不展开inline的,这样方便调试,debug版本想展开需要设置一下这两个地方

 

  • inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,连接时会出现报错


 8.nullptr

NULL实际是一个宏,在传统的C头文件(stddef.h)中。C++中NULL可能被定义为字面常量0,或者C中被定义为无类型指针(void*)的常量,不论采取哪种定义。在使用指针时,都不可避免会遇到一些麻烦

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 鹈鹕优化算法(POA)及其Python和MATLAB实现
  • 解决vue3中el-input在form表单按下回车刷新页面
  • ctfshow 信息收集(1-20)
  • 什么样的服务器是合乎直销网站标准
  • 计算机网络入门 --网络模型
  • [短笔记] Ubuntu配置环境变量的最佳实践
  • Golang中读写锁的底层实现
  • Docker容器——初识Docker,安装以及了解操作命令
  • js把文本转成数字的几种方式
  • 前端 css3 媒体查询实现 响应式布局
  • 负载均衡-轮询-两种简易实现
  • 网络安全-网络安全及其防护措施6
  • 智能合约中授权与转账的分离可行性分析
  • 岭回归(Ridge Regression)
  • redis其他类型和配置文件
  • angular2 简述
  • css的样式优先级
  • ES6系统学习----从Apollo Client看解构赋值
  • Otto开发初探——微服务依赖管理新利器
  • Redis 懒删除(lazy free)简史
  • Travix是如何部署应用程序到Kubernetes上的
  • vuex 笔记整理
  • 大主子表关联的性能优化方法
  • 精益 React 学习指南 (Lean React)- 1.5 React 与 DOM
  • 七牛云 DV OV EV SSL 证书上线,限时折扣低至 6.75 折!
  • 前端每日实战:70# 视频演示如何用纯 CSS 创作一只徘徊的果冻怪兽
  • 如何在 Tornado 中实现 Middleware
  • 使用SAX解析XML
  • 微信小程序设置上一页数据
  • #AngularJS#$sce.trustAsResourceUrl
  • (2024最新)CentOS 7上在线安装MySQL 5.7|喂饭级教程
  • (3)选择元素——(17)练习(Exercises)
  • (Charles)如何抓取手机http的报文
  • (vue)el-cascader级联选择器按勾选的顺序传值,摆脱层级约束
  • (安全基本功)磁盘MBR,分区表,活动分区,引导扇区。。。详解与区别
  • (八)Spring源码解析:Spring MVC
  • (笔试题)合法字符串
  • (二)十分简易快速 自己训练样本 opencv级联lbp分类器 车牌识别
  • (接上一篇)前端弄一个变量实现点击次数在前端页面实时更新
  • (三维重建学习)已有位姿放入colmap和3D Gaussian Splatting训练
  • (已更新)关于Visual Studio 2019安装时VS installer无法下载文件,进度条为0,显示网络有问题的解决办法
  • (转)es进行聚合操作时提示Fielddata is disabled on text fields by default
  • (转)Unity3DUnity3D在android下调试
  • (转载)(官方)UE4--图像编程----着色器开发
  • .NET Reactor简单使用教程
  • .NET 通过系统影子账户实现权限维持
  • .Net 执行Linux下多行shell命令方法
  • .NET/C# 推荐一个我设计的缓存类型(适合缓存反射等耗性能的操作,附用法)
  • .NetCore Flurl.Http 升级到4.0后 https 无法建立SSL连接
  • .NET分布式缓存Memcached从入门到实战
  • .net经典笔试题
  • .NET开发不可不知、不可不用的辅助类(三)(报表导出---终结版)
  • @param注解什么意思_9000字,通俗易懂的讲解下Java注解
  • [acwing周赛复盘] 第 94 场周赛20230311
  • [AIGC] Java List接口详解