从C语言过渡到C++,这些知识点应该是比较重要的。

目录

  • 第一个C++程序
  • 名称空间特性
    • cout
    • cin
  • 缺省参数
  • 重载
    • 提问:为什么C语言不支持重载而C++支持
  • 引用
    • 常引用
  • 引用与函数返回值
  • 对于指针和引用的区别
    • 引用的底层实现
    • 两者在语法上
    • 两者在物理上
    • 两者的不同之处
  • 内联函数
  • auto
    • 范围循环

第一个C++程序

#include <iostream>
void print(void)
{std::cout << "hello world" << std::endl;
}
int main(void)
{using namespace std;cout << "hello world" << endl;print();return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

对于C语言而言,直接printf("hello world");
但是对于C++而言,需要一个using namespace std;
这个叫使用命名空间(名称空间)。

名称空间特性

名称空间支持是C++的一个特性,方便在大型项目由多个部门共同编写时,解决多个变量名相同的情况。对于不同部门,封装不同的域作为名称空间。可以解决命名冲突的问题

同一项目中,如果在不同文件中有同一个名字的命名空间,在最后会讲这一样的命名空间合并成一个命名空间。

像这种using namespace std;是其std名称空间中的所有组件都能使用,当然仅限域其作用域中。

std::cout是只能使用限定的组件。

还有一种使用方法

#include <iostream>
using std::cout;
using std::endl;
int main(void)
{cout << "hello world" << endl;return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

提前在全局域声明。后面函数就能使用了。

::符号为:域作用限定符
在C语言中也存在,但我见的比较少。

#include <stdio.h>
int a = 10;
int main(void)
{int a = 20;printf("%d\n", a);printf("%d", ::a);//这样可以调用全局变量return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

对于局部变量和全局变量有一模一样的变量,在编译时,会先查找局部域,如果没找到才去查找全局域
::a可以这样调用全局域的变量,因为,空白也就是相当于全局域

对于C++也同样理解,去std这个名称空间的域中查找使用其中的函数或其他组件。

cout

COUT<<"HELLO WORLD";
  • 1

<<将字符串发送给cout
从C++概念上看,输出是一个流,这个操作就是将字符串插入到流中。

cin

C++的输入形式cin>>num;
理解成,cin从输入流中读取信息放入变量中。

<<>>都是表示信息的流向。

缺省参数

是C++支持的一种特性。从某种角度来看,是备胎,是备用数据

概念:缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,
如果没有指定实参则采用该默认值,
否则使用指定的实参。

就是在调用函数的时候,如果没有传实参,那么函数就会使用在定义或声明的时候提前指定的参数,防止函数出错。

全缺省参数

#include <iostream>
int add(int a , int b = 200,int c=10)
{return a + b + c;
}
int main(void)
{std::cout << add()<<std::endl;std::cout<<add(100,100,500);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

输出结果:>

半缺省参数

#include <iostream>
int add(int a , int b = 0,int c=0)
{return a + b + c;
}
int main(void)
{std::cout << add(1)<<std::endl; std::cout<<add(100,100,500);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

但是,如果有参数未指定,那么就一定要传递实参。
同时,对于半缺省参数,

1,未指定的参数一定要从左向右排列,指定的参数从右向左排列,
不能中间既有指定参数又有未指定参数,不能跳跃分布。
2,对于缺省参数,不能在函数声明和定义中同时出现,万一给的参数不一样,会导致编译器出错。
3,缺省参数必须是全局变量或者常量

重载

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

也就是在不同的环境下,其函数因为已经提前定义,一样的函数名但可以运行且满足需求。

看例子

#include <iostream>
int add(int a, int b)
{return a + b;
}
double add(double a, double b)
{return a + b;
}
double add(double a, double b,int c)
{return a + b+c;
}
int main(void)
{std::cout << add(1, 3)<<std::endl;std::cout << add(1.2, 2.5)<<std::endl;std::cout<<add(1.2, 1.3, 6);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

输出结果

对于函数重载的要求:
形参列表(参数个数 或 类型 或 顺序)必须不同,只有返回类型不同不是重载

提问:为什么C语言不支持重载而C++支持

这一切都与C++的函数命名修饰规则有关

我们都知道无论是C/C++运行起来都需要经历这几个阶段
参考C语言编译过程

  1. 预处理->>头文件展开,宏替换,删除注释,条件编译
  2. 编译->>检查语法问题,语义分析,词法分析,符号汇总(函数,变量)
  3. 汇编->>将汇编翻译成机械码,各个文件都要汇总成符号表
  4. 链接->>所有文件中的符号表合并,链接在一起生成exe文件

大致过程

C++程序大致也是这个过程。
但是,C++针对函数,与C语言有着明显的不同。
C++要支持重载这个性质,那么对于函数就有自己的函数命名修饰规则

C/C++在编译的时候,将所有的符号汇总,在汇编的时候,要翻译成汇编语言,同时形成符号表。也就是对每个函数都会根据声明形成同一个符号表。

汇编:转成机械码后,
对每行函数调用都会转变成一行机械指令。

 add(1, 3);//call _Z3addii(地址)add(1.2, 2.5);//call _Z3adddd(地址)add(1.2, 1.3, 6);//call _Z3addddi(地址)
  • 1
  • 2
  • 3

当然,如果是分文件写的,函数声明和定义分开在不同文件。

那就找不到函数的地址,因为,各个文件分别形成符号表,在链接前是不会相互联系。只有在链接时,声明才会去其他文件找定义,才能知道函数的地址。

如果定义声明都在一个文件中,直接就知道函数地址了,不用等到链接时。

再来看看C语言的

add(1, 3);//call <add>
  • 1

指令直接函数名。
根本不存在重载的情况。

C++的机械码指令还会附带显示参数类型,而C语言只是显示函数名。

C++的函数名修饰规则直接提供了重载的可能。

对于重载的判断

1,在查找符号表的同时,会判断是否有相同函数名的调用。
2,如果有,就会去匹配与传参类型数目匹配的函数
3,如果有匹配就执行,无匹配就报错。
如果出现二义性也会报错

总结
C++会根据参数列表去与函数参数列表匹配,去执行响应的函数。

引用

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

这也是C++的特性之一,与之相对应的是C语言中的指针。
引用:就相当于取了个别名int& b=a;

标识符a代表那个空间的所储存的值,而标识符b也代表a空间存储的值。b就是a,不是形参。

但指针容易出错,而引用不容易出错,且,更好理解。

这是C++实现的

#include <iostream>
void swap(int& a, int& b)
{int tmp = a;a = b;b = tmp;
}
int main(void)
{int a = 10, b = 30;std::cout << a << " " << b<<std::endl;swap(a, b);std::cout << a << " " << b;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

这是C语言的

#include <stdio.h>
void swap(int* a, int* b)
{int tmp = *a;*a = *b;*b = tmp;
}
int main(void)
{int a = 10, b = 30;printf("%d %d", a, b);swap(&a, &b);printf("%d %d", a, b);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15


再来看看这个

#include <iostream>
int main(void)
{int a = 10;int& b = a;int& c = b;c = 100;return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

对代码进行调试

1,

2,

3,

4,

由此可见,当c改变时,a,与,b都一起改变了。
这只能说明一件事

a/b/c这2个标识符都代表这个空间。
都可以通过这任意一个标识符访问到该空间。只是有不同的名字罢了。

引用:就是为该对象取了另外一个名字,无论是什么名字,真实作用的还是该对象。

注意

  1. 引用在定义时必须初始化(不然无意义)
  2. 一个变量可以有多个引用
  3. 引用一旦引用了一个实体,就不能再引用另一个实体,否则不能运行。
#include <iostream>
int main(void)
{int a = 1;int m = 10;int& b = a;int& c = b;int& d = m;c = d;return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

b/c都是a的别名
d是m的别名

c=d;
并不是让c是d的别名,而是单纯的操作
将d的值赋给c。
也就是将m的值赋给a.

引用一旦定义后,就不会轻易改变,比指针稳定,安全。

常引用

例->>

#include <iostream>
int main(void)
{int a = 10;//a可读可写const int& a1 = a;//a1操作只能都不能写//对权限缩小//a1 = 12;//->对与a1的操作权限放大a = 12;const int b = 10;//b的操作只能都不能写const int& b2 = b;//b2只能读不能写//int& b3 = b2;//int& b1 = b;//->对b的权限放大
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

有const,对与引用而言

权限可以缩小,不能放大

 int a=10;double d = 1.11;//d = a;//隐式类型转换//a = d;double& tmp = a;const double& tmp1 = a;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

对于a=d;d=a;
这种叫隐式类型转换
这并不是直接的作用,而是创建了一个要转换类型的临时变量,在赋值。

引用与函数返回值

#include <iostream>
int add(int a, int b)
{return a + b;
}
int main(void)
{int ret = add(10, 20);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这个int ret = add(10, 20);实际上,其ret接收的是一个临时空间里的值。

如果,使用引用。

#include <iostream>
int& add(int a, int b)
{int c = a + b;return c;
}
int main(void)
{int ret = add(10, 20);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11


返回的就是对这个临时空间的引用。
而c变量一旦函数结束,该空间就销毁了,没有了访问权限。
所以只能开辟一个临时空间,用于返回值。
而引用也就是该空间的引用。

但是对于随时会销毁的空间而言,引用并不适合用于返回值。

为什么?
来看看这个

#include <iostream>
int& add(int a, int b)
{int c = a + b;return c;
}
int main(void)
{int& ret = add(10, 20);add(20, 50);std::cout<<ret;return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

这个ret打印是70

有点意想不到。
因为,当一个函数结束后,那个空间就会被销毁。而返回的引用却还是引用那块空间。当下一个函数调用时,又会建立栈帧,在main函数下的那块空间。
也就是上一个函数建立栈帧的空间。而返回的临时空间还是原来的地址,那引用的空间不会变,但是其临时空间中的值被改变了。

std::cout<<ret;时,是打印ret所引用的空间的值。虽然越界了,但是编译器却没有检查出来。

对于引用而言,如果所引用的那块空间不稳定,容易被销毁后出现创建。那所引用的值也是不确定的。
对于此返回值并不推荐用引用。

如果是

#include <iostream>
int& add(int a, int b)
{static int c = a + b;return c;
}
int main(void)
{int& ret = add(10, 20);std::cout << ret<<std::endl;add(20, 50);std::cout << ret;return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15


因为静态变量存放在静态区,是不会轻易被销毁的,可以使用引用。

对于指针和引用的区别

引用的底层实现

在汇编实现的时候,引用和指针实现的底层都是一样的,引用是靠指针的底层实现,只不过编译器会对其进行打包。

都是依靠传地址进行操作。

两者在语法上

引用
只是为变量空间取了一个别名,并未开辟新的空间,增加了一个新符号,还是原来的空间。

指针:
指针开辟了4个字节的空间,存储目标空间的首字节的地址。

两者在物理上

两者底层都是一样的,都是使用指针进行操作。

对于两者在效率上,其实都差不多,因为,底层都是靠指针进行操作,只不过引用被打包了一下。

两者的不同之处

  1. 引用在概念(语法)上是定义一个变量的别名,而指针是存储一个变量的地址。
  2. 引用在定义时必须要初始化,而指针没有要求
  3. 引用在初始化引用一个实体后不能再引用其他实体(变量),而指针可以在任何时刻都可以随便改变指向同类型的实体。
  4. 没有NULL引用,但有空指针。
  5. sizeof():对于引用实际上是计算引用类型的字节大小,而对于指针,却是地址所占空间的字节大小。
  6. 引用自增,就是其引用的实体加1,而指针自增则是其指向的地址向后移动一个类型大小。
  7. 有多级指针,但没有多级引用,有多次引用。
  8. 引用不需要解引用,编译器已经处理好了,而指针需要显示解引用。
  9. 引用比指针更安全。

内联函数

在C语言中,为了减少小一点的多次调用的函数的开销,会使用宏函数来减少时间消耗。

例如:

#include <stdio.h>
#define Add(x,y) ((x)+(y))
int main(void)
{printf("%d",Add(1, 2)*4);return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

直接替换,还减少了调用函数,为函数开辟栈帧,跳转到函数位置的时间。

但是C语言的宏函数也存在一些不足:

  1. 不支持调试
  2. 没有安全检查
  3. 语法复杂,容易出错。

而C++提供了另一种方法来解决C语言的一些不足,也就是内联函数
效果与宏差不多。

这张图很清晰的描述了内联函数的过程。
要使用内联函数的特性,必须要

在函数声明前加上关键字inline
在函数定义前加上关键字inline

但是,当使用内联函数时,编译器并不一定会执行。当编译器认为函数过大或者函数使用了递归,就不会处理为内联函数。

注意,分文件函数的声明和定义时,是不可行的,因为内联函数就是直接插入main文件中,
是不会建立栈帧的,即函数是不会有地址,不会形成符号表。所以不要将内联函数的声明和定义分文件。

对下面代码进行汇编查看

#include <iostream>
inline int Add(int a, int b)
{return a + b;
}
int main(void)
{int c = Add(1, 2);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

如图

这就是形成汇编代码时,内联函数和常规函数的区别。

内联函数在汇编时,没有call指令是不会调用Add函数的。
而常规函数是由call指令,会去一个地方调用函数。

这就是内联函数对于常规函数的优势。时间更快

使用内联函数可以减少程序的在调用函数上的时间,但是却会增加可执行文件的大小。可以说是以空间换时间的操作了。

对于C++而言,并不建议使用宏,而是推荐使用const,因为宏不会进行检查。

auto

#include <iostream>
int main(void)
{int a = 10;auto b = a;return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

声明auto类型的变量会根据赋值的变量的类型自动推导相应的类型。

b 会根据 a 的 int 类型自动推出 b 的类型也是 int 。

我觉得挺方便的。
还有

#include <iostream>
int main(void)
{int a = 10;auto b = a;int& c = a;auto d = c;//b是int类型//相当于int d=c;auto& e = c;//e是int& //相当于int& e=c;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

引用并不是一个类型,c 虽然是引用 a ,但其还是 int 类型

范围循环

#include <iostream>
int main(void)
{int arr[] = { 1,2,3,4,5 };for (auto a : arr)std::cout << a << " ";std::cout << std::endl;for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)std::cout << arr[i]<<" ";return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11


可以达到同样的效果。

再来看看这个

#include <iostream>
int main(void)
{int arr[] = { 1,2,3,4,5 };for (auto a : arr)a *= 2;for (auto a : arr)std::cout << a << " ";std::cout << std::endl;for (auto& a : arr)a *= 2;for (auto a : arr)std::cout << a << " ";std::cout << std::endl;return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

打印效果



这就是,将数组中每个元素赋值给变量 a,int a=arr[i],再对 a自乘一个2,却不会影响数组中的元素



这个用的是引用,相当于int& a=arr[i],直接可以作用于数组元素。会改变数组元素

这些都是从C过度到C++的基础知识点,比较零碎,但都需要了解。

谢谢观看
如有问题,烦请大佬指点一二。

由C过渡到C++-入门知识点相关推荐

  1. HTML DOM 入门知识点总结

    HTML DOM 入门知识点总结 HTML DOM 节点概念 获取节点 节点的属性 样式 事件 HTML DOM 节点关系 基本概念 创建节点 删除节点 替换节点 插入节点 使用场景 教程来源:how ...

  2. Double计算精度丢失(金融入门知识点)

    Double计算精度丢失(金融入门知识点) 一.double精度丢失 二.为什么double会精度丢失 三.BigDecimal错误的用法 四.BigDecimal正确的用法 Double计算精度丢失 ...

  3. (金融入门知识点)Double类型丢失精度

    Double计算精度丢失原因:Double计算精度丢失(金融入门知识点)__yosemite的博客-CSDN博客_double精度丢失 Double计算精度丢失解决办法:Double类型丢失精度的两种 ...

  4. C# 零基础入门知识点汇总

    C# 零基础入门 知识点汇总 前言 一,基础语法(1~10) 二,流程控制(11~20) 三,数组相关(21~30) 四,函数介绍(31~40) 五,类和对象(41~50) 六,面向对象(51~60) ...

  5. redis原理快速入门知识点总结

    redis原理快速入门知识点总结 1. 项目中缓存是如何使用的?为什么要用缓存?缓存使用不当会造成什么后果? 为什么用缓存? 1.高性能: 一些需要复杂操作耗时查出来的结果,且确定后面不怎么变化,但是 ...

  6. python入门知识点-1

    文章目录 python入门知识点-1 字符串的常见操作 字符串的编码 字符串的编码集 格式化输出字符 字符串format方法的使用 列表的基本使用 基操 列表增加元素 列表的修改查询和删除 列表的遍历 ...

  7. C++57个入门知识点_17 类的访问权限及C语言模拟类的封装(类的私有权限突破方法:编译期进行权限检查,运行期通过指针修改类的私有成员变量;利用函数指针对结构体中成员变量进行修改;CPU大小尾排列)

    接上篇:C++57个入门知识点_16 类的标准写法(类名.成员变量.成员函数及对象命名规则:成员变量一般为私有,成员函数为公有并暴露给外部使用成员变量:防止类过大,声明写在.h,实现写在.cpp,调用 ...

  8. Java小结|Java入门知识点

    Java入门知识点 Java入门知识点 Java的起源与演变 Java的起源 Java的演变 Java 体系与特点 Java体系 Java能做什么 Java的特性 Java 跨平台原理 Java 技术 ...

  9. C++57个入门知识点_50 菱形继承与虚继承(C++中语法允许多重继承造成菱形继承;会造成近亲结婚的问题;可以通过虚继承的方式解决;实际项目中不多用多重继承)

    上篇C++57个入门知识点_49 多重继承与组合(一个类同时具有多个类的属性的方法:多重继承或者组合:多重继承:一个类同时继承多个类:多重继承构造和析构的顺序与普通继承类似:组合:类中包含多个成员对象 ...

最新文章

  1. 如何使用工具包 (NLTK) 开发NLP 项目?(附教程)
  2. error: device not found - waiting for device -
  3. UC伯克利博士尤洋回国创业,求学期间破ImageNet纪录!已获超千万种子轮融资
  4. 监督学习——随机梯度下降算法(sgd)和批梯度下降算法(bgd)
  5. 回归素材(part8)--python机器学习算法
  6. Eclipse中的集成Git插件删除线上远程分支
  7. 再见Postman,这款API神器更好用!
  8. halcon——在图形窗口中画坐标系
  9. 第六章 线程的基础知识
  10. Java 自动装箱与拆箱(Autoboxing and unboxing)
  11. RadioButton 与 XML 之间的协调使用
  12. 30. 人类将如何变革--走出金字塔模型(下)
  13. navicat连接数据库出错,但是SQLyog可以正常连接
  14. Flume从入门实战到精通再到面试一文搞定
  15. OSChina 周一乱弹 ——强行把她拖到家里洗了个澡
  16. java自动转换与强制转换
  17. [仅个人学习用] 知识图谱 刘知远老师 TransE
  18. Java 读取resources下的文件
  19. ISO27701个人隐私信息安全管理体系 认证经验
  20. win10 Windows无法访问指定设备、路径或文件。你可能没有适当的权限访问该项目。

热门文章

  1. python 阿狸的进阶之路(4)
  2. Android Wifi简单的梳理【转】
  3. RPM方式安装MySQL5.6
  4. 如何手动给Docker容器设置静态IP
  5. 块分割,维特比算法小结
  6. webservice系统学习笔记9-使用契约优先的方式的一个服务端demo(隐式传Header信息)...
  7. mysql配置文件注解
  8. XSL学习笔记6 XSLT内置模板规则
  9. 五轴加工的RTCP技术
  10. 【文件处理】——字典写入json文件或TXT文件,读取文件中的字典TypeError: Object of type ‘ndarray‘ is not JSON serializable错误解决方法