目录

命名空间

C++的输入与输出

缺省参数

函数重载

引用

内联函数

auto关键字


命名空间

在C++的学习过程中,比起C语言,C++有着更多的函数,关键字等等,但是他们都存在于全局作用域中,就可能会导致很多冲突,有了命名空间,就可以避免命名冲突或名字污染。namespace就是它的关键字。

#include <stdio.h>
#include <stdlib.h>int rand = 2;int main()
{printf("%d\n", rand);
}
//会报错rand重定义,在C语言中,全局变量rand与stdlib.h库中的rand函数重命

1.命名空间的定义 

//1.正常的定义
namespace a
{   //在命名空间中可以定义变量、函数、结构体int rand;int Add(int x, int y){return x + y;}struct Node{struct Node* next;int val;};
}
//2.嵌套定义
namespace n1
{int a;int b;int Add(int x, int y){return x + y;}struct Node{struct Node* next;int val;};namespace n2{int c;int Sub(int x, int y){return x - y;}}
}
//3.在同一个工程中,如果有多个相同名称的命名空间,最后编译器会将他们合并
//test.h
namespace m1
{int a;int Add(int x, int y){return x + y;}
}
//test.cpp
namespace m1
{int b;int Sub(int x, int y){return x - y;}
}
//合并后
namespace m1
{int a;int b;int Add(int x, int y){return x + y;}int Sub(int x, int y){return x - y;}
}

一个命名空间就是一个作用域,定义的变量只在此作用域内才能使用。

 2.命名空间的使用

namespace x
{   int a;int b;int Add(int x, int y){return x + y;}struct Node{struct Node* next;int val;};
}
//如果想要访问命名空间x中的变量、函数、结构体就要用到作用域限定符::
int main()
{printf("%d\n", a);//错误printf("%d\n", X::a);//正确
}
//若使用using namespace可以将命名空间的所有变量直接引入
using namespace x;
int main()
{printf("%d\n", a);//正确printf("%d\n", X::a);//正确
}
//若使用using可以将命名空间的某个成员引入
using x::a;
int main()
{printf("%d\n", a);//正确printf("%d\n", X::a);//正确printf("%d\n", b);//错误printf("%d\n", X::b);//正确
}

在日常训练中,我们可以直接将命名空间的变量用using namespace全部引入,在工作时,使用using将常用的几个成员,如C++库中的cout可以引入。

C++的输入与输出

在引入了命名空间这个概念,在C++ 中的输入与输出就与C语言就有不同了。

#include <iostream>
//在C++中标准库被放在了 std 这个命名空间中了
using namespace std;int main()
{cout << "hello world!" << endl;//cout是标准输出对象(控制台),endl类似于C语言的换行符//<<与>>是流插入运算符return 0;
}
#include <iostream>
using namespace std;int main()
{int a;double b;char c;//cin是标准输入对象(键盘)cin >> a;//输入1cin >> b >> c;//输入3.14 acout << a << endl;//输出的结果为1cout << b << " " << c << endl;输出结果为3.14 a//cin与cout都能自动识别变量类型return 0;
}

缺省参数

缺省参数是在定义或声明函数时为函数的参数提供一个缺省值,调用函数时若没有指定实参贼则采用缺省值,否则采用实参。

#include <iostream>using namespace std;void func(int a = 10)
{cout << "a = " << a << endl;
}
int main()
{func();//在不给定实参输出10func(2);//给定实参输出实参2return 0;
}
//全缺省参数
void func1(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;}
//半缺省参数
void func(int a, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl;}
int main()
{func1();//结果//a = 10//b = 20//c = 30func2(5);//结果//a = 5//b = 10//c = 20
}

半缺省参数必须从右往左依次给出,不能间隔着给。同时缺省参数不能在函数的声明和定义中同时出现。

//test.h

void func(int a = 10);

//test.cpp

void func(int a =20)

{}

//若声明与定义的缺省值给的不同就导致编译器无法确定缺省值

缺省值必须是全局变量或者是常量。

函数重载

函数重载是一种特殊的情况,在C++中允许在同一个域中声明有着相似功能的但是名字相同的函数,这些同名函数的参数列表(参数类型,参数个数,类型顺序)是不同的,这样就构成了函数重载。

#include <iostream>
using namespace std;
//参数类型不同
int Add(int x, int y)
{return x + y;
}
double Add(double x, double y)
{return x + y;
}
//参数个数不同
void func()
{cout << "func()" << endl;
}
void func(int a)
{cout << "func(int a)" << endl;
}
//参数类型顺序不同
void a(int x, char y)
{cout << "a(int x, char y)" << endl;
}
void a(char y, int x)
{cout << "a(char a, int x)" << endl;
}

C++支持函数重载是因为相对于C语言,C++某个程序在调用函数时,是根据一个函数的命名修饰来找的,C语言对于函数的命名修饰仅仅只是记录了函数的函数名,而C++对于函数的修饰有函数名加上参数的修饰。

这是gcc采用C语言编译器编译后的结果,对于函数的命名修饰仅仅只是函数名。

而在Linux环境下利用g++编译完成后,对于函数的命名修饰大概就是 函数名+参数类型。

引用

引用不是定义一个新的变量,而是给一个已经存在的变量取“别名”,它与它引用的变量共用同一块空间。

在定义引用时,应该遵循 引用类型& 引用变量名 = 引用实体;

void test()
{int a = 10;int& ra = a;printf("%p\n", &a);printf("%p\n", &ra);
}
//可以利用这段代码去测试他们的储存空间的地址

注意:引用类型必须和引用实体的类型时同一种类型。

引用的特性: 

1.引用必须在定义时初始化。

2.一个变量可以有多个引用。

3.引用一旦引用了一个实体,就不能引用其他实体。

void test2()
{int a = 10;int& ra = a;int& rra = ra;printf("%p\n", &a);printf("%p\n", &ra);printf("%p\n", &rra);
}//多重引用都是一个变量实体

常引用的一些问题: 

void test3()
{const int a = 10;//int& ra = a; // 该语句编译时会出错,a为常量const int& ra = a;// int& b = 10; // 该语句编译时会出错,b为常量const int& b = 10;double d = 12.34;//int& rd = d; // 该语句编译时会出错,类型不同const int& rd = d;
}

如果引用const修饰的常变量时,被const修饰的常变量的权限被进行了放大,这样就会报错。同理在引用一个常量时,也发生了权限放大,也会报错。如果引用的引用类型与被引用变量类型不同则也会报错。

引用的使用场景: 

1.做参数

void swap(int& ra, int& rb)
{int tmp = ra;ra = rb;rb = tmp;
}
//使用引用作为参数
void swap(int* pa, int* pb)
{int tmp = *pa;*pa = *pb;*pb = tmp;
}
//使用指针作为参数

在C语言中,我们要在一个函数体内可以改变函数外的变量的值,只能使用指针来改变,但是有了引用,引用就相当于变量的别名,传过去的参数,也就是变量本身,可以简化指针作为参数的麻烦。

2.做返回值(重点)

int& count()
{static int n = 0;n++;return n;
}
int main()
{int& ret = count();printf("%d\n", ret);return 0;
}//答案是1
//这是一个正确使用引用返回的代码

大家看下面代码

在vs2022下,如果我们的Add函数中c是int出来的而没有带static修饰,main函数中两次输出ret的值就会不一样,为什么呢?其实由于c是int出来的,是存在于Add函数栈帧里的,在函数结束时会随着函数栈帧的销毁一同销毁掉,它的返回值时int&类型的,返回值就是原本在函数栈帧中int出来的c,但是由于销毁后,我们利用ret接受返回值,ret就是原本函数栈帧里的c,第一次输出,操作系统并没有回收,则打印出的值就是30,第二次的操作系统回收后打印的就是随机值。

结论:如果函数返回时,出来函数作用域,如果返回对象没有随着函数栈帧一起销毁,就可以使用引用返回,如果返回对象随着函数栈帧一起销毁,则使用传值返回。

传值与传引用的效率比较

#include <time.h>
struct A { int a[10000]; };
void TestFunc1(A a) {}
void TestFunc2(A& a) {}
void TestRefAndValue()
{A a;// 以值作为函数参数size_t begin1 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc1(a);size_t end1 = clock();// 以引用作为函数参数size_t begin2 = clock();for (size_t i = 0; i < 10000; ++i)TestFunc2(a);size_t end2 = clock();// 分别计算两个函数运行结束后的时间cout << "TestFunc1(A)-time:" << end1 - begin1 << endl;cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl;
}

上面代码段,可以用来测试传值与传引用的效率。由于传值时,函数会创建一个临时变量来保存传过去的值,而传引用时,传过去的是一个变量的”别名“也就是变量本身,故不用创建临时变量,所以传引用要比传值运行更快。

值返回与引用返回的性能比较: 

#include <time.h>
struct A { int a[10000]; };
A a;
// 值返回
A TestFunc1() { return a; }
// 引用返回
A& TestFunc2() { return a; }
void TestReturnByRefOrValue()
{// 以值作为函数的返回值类型size_t begin1 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc1();size_t end1 = clock();// 以引用作为函数的返回值类型size_t begin2 = clock();for (size_t i = 0; i < 100000; ++i)TestFunc2();size_t end2 = clock();// 计算两个函数运算完成之后的时间cout << "TestFunc1 time:" << end1 - begin1 << endl;cout << "TestFunc2 time:" << end2 - begin2 << endl;
}

上面代码是用来测试它们的性能的。对于值返回,在函数栈帧销毁时,会创建一个临时变量来保存返回值,如果外面有变量接收再进行拷贝。对于引用返回,在函数栈帧销毁时,若正确使用引用返回,返回值就是不随着函数栈帧销毁而销毁的变量本身,所以不需要创建临时变量来保存返回值,如果在外面有变量接收也就拷贝一次。总结:值返回一次拷贝一次, 引用返回一次不需要拷贝,若外面有变量接收返回值它们都需要拷贝。对于返回的变量占很大空间时,引用返回效率远远高于值返回。

 引用与指针的区别:

在语法层面上,引用就是一个变量的”别名“,没有独立空间,与引用实体共用同一块空间。但是实际上在底层上,引用是利用指针的方式来实现的,要占用空间。

不同点:

1.引用在概念上只是一个变量的”别名“不占储存空间,指针储存变量的地址,占用储存空间。

2.引用在定义时必须初始化,指针不用。

3.引用在引用一个实体后就不能改变引用对象,指针可以任意改变。

4.引用没有NULL这个概念,指针有NULL。

5.在利用sizeof计算大小时,引用就是引用实体的大小,指针始终是地址的所占的字节数(32位平台为4字节,64位为8字节)

6.引用自增一就是引用实体自增一,指针自增一就是向后偏移一个类型的大小。

7.指针有多级指针,而引用没有,引用就是引用实体的别名。

8.指针要解引用,而引用编译器自己处理。

9.引用比起指针更加安全,可以避免野指针。

内联函数

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

上图中,普通函数调用在底层反汇编时会call函数(也就是调用函数),有了函数栈帧的开销。

在vs2022环境下,想要看到内联函数是否被展开要将编译器属性中设置C/C++下常规调试信息格式设置为程序数据库,优化设置为只适用于inline才能查看是否展开,上图是底层反汇编时函数直接在调用位置展开,而不会call函数,没有建立函数栈帧,无开销,程序运行会比call快。但是由于是在调用位置展开,空间就会增加,所以内联函数是以空间换时间的一个手段。

特性:

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

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

3. inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址
了,链接就会找不到。(程序运行要经过预处理、编译、汇编、链接,在链接前会形成符号表,如果是内敛函数的话,由于是在调用处展开,并不会被符号表记录,链接的时候就会出错)如下面代码:

// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
f(10);
return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl
//f(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用

auto关键字

auto简介: 

在早期C/C++中auto的含义是:使用auto修饰的变量,是具有自动存储器的局部变量,但遗憾的
是一直没有人去使用它,大家可思考下为什么?
C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一
个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

//auto自动推导类型
int main()
{int x = 10;auto a = &x;auto* b = &x;auto& c = x;cout << typeid(a).name() << endl;cout << typeid(b).name() << endl;cout << typeid(c).name() << endl;*a = 20;*b = 30;c = 40;return 0;
}//typeid().name()可以打印出对于变量的类型

使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto
的实际类型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编
译期会将auto替换为变量实际的类型。

注意:

1.利用auto声明指针类型的时候auto与auto*无区别,但是声明引用的时候要加上&。

2.如果在同一行auto声明多个变量,这些变量必须是同类型的否则会报错。

void TestAuto()
{
auto a = 1, b = 2;
auto c = 3, d = 4.0; // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

3.auto不能作为函数参数,因为编译器无法推导出类型。

4.auto也不能用来声明数组。

利用auto的范围for(一颗语法“糖”)

//一般遍历,将数组元素每个值乘以2再遍历输出
void Testfor()
{int array[] = { 1,2,3,4,5,6 };for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++){array[i] *= 2;}for (int i = 0; i < sizeof(array) / sizeof(array[0]); i++){cout << array[i] << ' ';}
}
//范围for遍历,将数组元素每个值乘以2再遍历输出
void Testfor()
{int array[] = { 1,2,3,4,5,6 };for (auto& e : array){e *= 2;}for (auto e:array){cout << e << ' ';}
}

for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。注意:范围for能够使用必须要知道遍历的范围。

谢谢各位的观看,创作不易,一键三连。如有错误欢迎指正!

【C++入门】函数重载,引用,内联函数......相关推荐

  1. C++ C++基础语法入门总结(二)引用-内联函数-C++11新特性

    C++基础语法入门总结 C++引用 再谈引用和指针 C++内联函数 附加C++11新特性 auto关键字 基于范围的for循环 指针空值nullptr C++引用 引用:就是某一变量(目标)的一个别名 ...

  2. C++学习笔记之——引用 内联函数

     本文为原创作品,转载请注明出处 欢迎关注我的博客:http://blog.csdn.net/hit2015spring和http://www.cnblogs.com/xujianqing/ 作者 ...

  3. 函数模板、 内联函数

       函数重载就是有相同的函数名但参数的个数或类型不同从而根据不同的参数个数和参数类型来调用相应的方法.    我们发现函数重载只是解决了函数命名的问题,但函数体虽然相同我们还是要重复的写,为了解决这 ...

  4. C++中虚函数可以是内联函数吗?

    1.需要注意的几点: 虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联. 内联是在编译器建议编译器内联,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因 ...

  5. Kotlin学习笔记 第三章 函数 高阶函数 lambda表达式 内联函数

    参考链接 Kotlin官方文档 https://kotlinlang.org/docs/home.html 中文网站 https://www.kotlincn.net/docs/reference/p ...

  6. C++的内联函数和非内联函数的区别

    一.内联函数和非内联函数的定义 1.内联函数 inline void test(); void test() { cout<<"test"<<endl; } ...

  7. [课程视频]指针、引用、函数参数、内联函数等

    今天气温低.风大,我穿一个略正式的T恤衫.一条大短裤和人字拖在办公室里录制最新一期课程视频.反正拍不到下半身,所以下半身衣着随意我面向的录制环境如下图,我背后是一个绿幕.设备由广东省教学改革项目资金提 ...

  8. inline 内联函数详解 内联函数与宏定义的区别

    一.在C&C++中 一.inline 关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义. 表达式形式的宏定义一例: #define ExpressionName ...

  9. C++ 基础 : 函数重载、引用、内联函数、auto、范围for循环

    函数重载 引用 内联函数 auto 范围for循环 函数重载 C++中引入了一个新特性,函数重载. 在同一个作用域下,对于相同的函数名,函数的参数不同,不同类型的参数顺序不同,参数的个数不同,都可以形 ...

最新文章

  1. 比特币再度遭遇资金“大逃离” 后市前景愈发摇摇欲坠
  2. Java中JDBC连接数据库(MySQL)
  3. Spring之代理模式实例
  4. 高度等于动态宽度(CSS流体布局)
  5. java正则表达式版本_java 正则表达式 版本号_java正则表达式?=.*_密码的正则表达式java...
  6. 魔方:公式记忆(三字诀)
  7. springboot+爱心捐赠小程序 毕业设计-附源码211711
  8. 【编译原理】 根据语法树 写出对应的短语 直接短语 句柄 构造产生式
  9. JavaScript系列(一):浏览器及内核介绍
  10. 什么是内存屏障?具有什么作用?
  11. 屏的像素与传输速率_HDMI线的传输速率是如何定义的
  12. hloj#168“倒牛奶”解题讨论
  13. 梯度下降法的简单理解
  14. 【第25章】移动应用安全需求分析与安全保护工程(软考:信息安全工程师) -- 学习笔记
  15. pyinstaller打包遇到MatplotlibDeprecationWarning问题
  16. win7单机计算机就可打开,月影传说单机版电脑版
  17. 腾讯王巨宏:“未来+教育”,以智能技术助力人才培养新模式
  18. ORACLE基本语句
  19. Latex如何写出一个增广矩阵中的长坚虚线
  20. python读取数据库数据格式_python 连接数据库读取及写入

热门文章

  1. pandas数据处理基础——筛选指定行或者指定列的数据
  2. 普元 AppServer 6.5 支持哪些日志级别?
  3. 夯实Java基础(一)
  4. BUG解决记录--iOS13中iPhoneX系统右箭头出现黑框
  5. 中科大辅修计算机,中科大新生入学第二考来了——校规考试!(一不小心就挂)...
  6. 【Python】随机漫步
  7. 年收入80亿、三年扩张四倍:1700人的Playrix已成休闲游戏新巨头
  8. 聚智云算,向新而生| 有孚网络“专有云”开启新纪元
  9. 这所211大学计算机学院全面改考408!中国地质大学(武汉)
  10. linux定时任务输出时间日志,linux 定时任务 日志记录