还是唐老师的视频,还是老样子,既然看了学了,就做点笔记做点精简和总结,
以后有用得到的地方,也方便查找。本文章只有比较C与C++ 有联系的不同之处,并没有C++独有的内容。
..添加目录栏目 2021/9/17
目录
1、C到C++的升级
变量定义
- 所有的变量都可以在需要使用时再定义
int c = 0;
for(int i=1; i<=3; i++)
{
for(int j=1; j<=3; j++)
{
c += i * j;
}
}
- register 关键字请求编译器将局部变量存储于寄存器中,C++ 依然支持,有自己的优化方式,C++ 中可以取得 register 变量的地址,如果有对变量取地址的操作,register 会直接忽略。 register 基本上使用中看不到,但是为了兼容C语言,鸡肋,形同虚设,不用!!!
- 在C语言中,可以重复定义多个同名的全局变量, 在C++中,不允许定义多个同名的全局变量
C语言中多个同名的全局变量最终会被连接到全局数据区的同一个地址空间上,C++直接拒绝这种二义性的做法
struct关键字的加强
- C语言中的struct定义了 一组变量的集合
- C语言中struct 定义的 标识符 并不 是一种新的类型
- C++ 中的 struct用于定义一个全新的类型

int f() 和 int f(void)
- C++中所有的 标识符都必须显示的声明类型
- C语言中的 默认类型 在C++ 中是不合法的

布尔类型
bool 是C++ 中的基本数据类型,可以定义bool类型的 全局变量,常量,指针,数组
三目运算符

C++对三目运算符做了什么?
- 当三目运算符的 可能返回都是变量时,返回的是变量引用(引用的解释见第 3 大点,3、引用部分)
- 当三目运算符的 可能返回中有常量时,返回的是值
2、进化后const的分析
C与C++中const的比较
在C语言中:

int main()
{
const int c = 0;
int* p = (int*)&c;
*p = 5;
printf("c = %d\n", c);//c语言中可以改变 c= 5,C++中 c= 0
printf("*p = %d\n", *p);
printf("End...\n");
return 0;
}



void f()
{
#define a 3
const int b = 4;
}
void g()
{
printf("a = %d\n", a); //C++编译不会报错,这个地方编译器看到的a 是3
printf("b = %d\n", b); //C++编译报错,编译器发现b作用域不在这里
}
编译器压根不知道 宏 的存在,预处理器直接进行了文本替换
关于const的疑问


#include<stdio.h>
int main()
{
const int x = 1;
const int& rx = x;//rx代表一个只读变量,这个变量所对应的内存空间是编译器为x常量分配的空间
int& nrx = const_cast<int&>(rx);//nrx 对应的空间 和 rx一直,但是他没有只读属性
nrx = 5;
printf("x = %d\n", x);
printf("rx = %d\n", rx);
printf("nrx = %d\n", nrx);
printf("&x = %p\n", &x);
printf("&rx = %p\n", &rx);
printf("&nrx = %p\n", &nrx);
volatile const int y = 2; //不是真正意义上的常量
int* p = const_cast<int*>(&y); //将y的地址的只读属性去掉
*p = 6;
printf("y = %d\n", y);
printf("p = %p\n", p);
const int z = y; //编译不确定z的值,所以z是只读变量
p = const_cast<int*>(&z);
*p = 7;
printf("z = %d\n", z);
printf("p = %p\n", p);
char c = 'c';
char& rc = c; // rc代表 c
const int& trc = c;//const引用他的类型不同的话,他会得到一个新的只读变量 trc
rc = 'a';
printf("c = %c\n", c);
printf("rc = %c\n", rc);
printf("trc = %c\n", trc);
return 0;
}

3、引用
引用的概念

- 引用作为 变量别名 而存在,因此在一些场合 可以代替指针
- 引用相对指针来说具有更好的 可读性 和实用性
void swap(int& a, int& b){
int t = a;
a = b;
b = t;
}
const 引用
- 在C++ 中可以声明 const 引用, const type& name = var;
- const 引用 让变量拥有 只读属性
int a = 4;
const int& b = a;
int* p =(int*)&b;
b = 5; //Error, 只读变量
*p = 5; //OK. 修改变量 a 的值
- 当使用 常量 对 const引用进行初始化是,C++ 编译器会为常量值分配空间,并将引用作为这段空间的别名 使用常量对 const 引用初始化后将生产一个只读变量
#include <stdio.h>
int main()
{
const int& b = 1;
int* p = (int*)&b;
printf("b = %d\n", b);
printf("*p = %d\n", *p);
printf("p =%p\n", p);
// b = 5; //ERROR
*p = 5;
printf("b = %d\n", b);
printf("*p = %d\n", *p);
printf("p =%p\n", p);
return 0;
}

引用的本质

- C++编译器在 编译过程中 用 指针常量 作为引用的 内部实现,因此引用所占的空间大小与指针相同。
- 从使用的角度,引用只是一个别名,C++为了实用性而隐藏了引用的存储空间这一细节
- 引用可以大多数情况下代替指针,可以尽可能的避开内存错误
关于引用的疑问



#include<stdio.h>
int a = 1;
struct SV
{
int& x;
int& y;
int& z;
};
int main()
{
int b = 2;
int* pc = new int(3);
SV sv = {a, b, *pc};
int& array[] = {a, b, *pc};//&array[1] - &array[0] = 应该是4,数组\
所以在C++里面不支持引用数组
printf("&sv.x= %p\n",&sv.x);
printf("&sv.y= %p\n",&sv.y);
printf("&sv.z= %p\n",&sv.z);
delete pc;
return 0;
}

- 指针是一个变量 引用是一个变量的新名字
- const 引用能够生产 新的只读变量
- 编译时不能直接确定初始值的 const 表示符都是 只读变量
4、内联函数
- C++中推荐使用 内联函数替代宏代码片段
- C++中使用 inline 关键字声明内联函数
inline int func(int a, int b)
{
return a < b ? a : b;
}
- C++编译器可以讲一个函数进行内敛编译,被C++编译器内敛编译的函数叫做 内联函数
- C++编译器直接将函数体插入函数调用的地方
- 内联函数 替代 宏定义更加安全 , 宏代码片段由预处理器处理,进行简单的文本替换,没有任何的编译过程,因此可能出现副作用。
- 内联函数 没有 普通函数调用时的 额外开销(压栈,跳转,返回)
- inline只是一种请求,C++编译器 不一定满足函数的内联请求!!!
- 现代 C++ 编译器 能够进行编译优化,一些函数及时没有 inline声明,也可能被内敛编译
- 一些现代 C++编译器 提供了 扩展语法,能够对函数进行 强制内联,如:
g++ :_attribute((always_inline)) 属性 (attribute后面也有一个__打不出来)
MSVC: _forceinline
_attribute_((always_inline)) //g++
_forceinline //MSVC
C++ 中 inline 内联编译的限制:
- 不能存在任何形式的 循环语句
- 不能存在 过多的条件判断语句
- 函数体不能过于庞大
- 不能对函数进行取址操作
- 函数内敛声明 必须在调用之前
5、函数参数的扩展
- C++中可以在 函数声明时 为参数提供一个默认值
- 当函数调用没有 提供参数的值,则使用默认值
- 参数的默认值 必须在 函数声明中指定
- 参数的默认值必须 从右向左提供
- 函数调用时 使用了默认值,则后续参数 必须使用默认值
在C++中可以为函数提供 站位参数
- 占位参数 只有参数类型声明,而 没有参数名声明
- 一般情况下,在函数体内部 无法使用占位参数
- 占位参数与默认参数结合起来使用
int func(int x, int){
return x;
}
//...
func(1,2); //ok
函数占位参数 为了 兼容C语言程序 中可能出现的不规范写法,看下面的例子:
void func(int = 0, int = 0){}
int main(int argc, char *argv[])
{
func();
func(1,2);
return 0;
}
6、函数重载分析
函数重载(Function Overload)
- 用同一个函数名定义不同的函数
- 当函数名和 不同的参数搭配 时函数的含义不同
- 函数重载 至少 满足下面的一个条件:
参数 个数 不同
参数 类型 不同
参数 顺序 不同
当 函数默认参数 遇上 函数重载会发生什么?
#include<stdio.h>
int func(int a, int b, int c = 0){
return a * b * c;
}
int func(int a, int b){
return a + b;
}
int main(int argc, char *argv[])
{
int c = func(1, 2);//编译器会报错,他不知道该调用什么 ERROR
return 0;
}
- 编译器最终寻找到候选函数不唯一,出现二义性,编译失败
函数重载的注意事项
- 重载函数在 本质上是相互独立的不同函数 ,占用不同的地址
- 重载函数的函数类型不同
- 函数返回值不能作为函数重载的依据
函数重载是由 函数名 和 参数列表 决定的!!!
重载与指针
- 严格匹配候选者的函数类型与函数指针的函数类型
- 函数重载 必然发生在同一个作用域中
- 无法直接通过函数名得到重载函数的入口地址,必须要还要根据函数指针类型或者参数列表
7、C++ 和 C相互调用

#include<stdio.h>
extern "C"
{
#include "add.h"
}
int main()
{
int c = add(1, 2);
...
}


8、动态内存分配
专用关键字 new
- C++中的动态内存分配可以 同时进行初始化
- C++中的动态内存分配是 基于类型进行的

p = new int[10]; //p指向的这段内存空间至少为 40字节,不能保证要多少给多少,可以保证至少这么多

#include<stdio.h>
int main()
{
int* pi = new int(1);
int* pa = new int[1];
float* pf = new float(2.0f);
char* pc = new char('c');
printf("*pi = %d\n",*pi);
printf("*pa = %d\n",*pa);
printf("*pf = %f\n",*pf);
printf("*pc = %c\n",*pc);
printf("pi = %p\n",pi);
printf("pa = %p\n",pa);
printf("pc = %p\n",pc);
delete pi;
delete pa;
delete pf;
delete pc;
return 0;
}

9、命名空间
在C语言中,所有的全局标识符共享同一个作用域,在大工程中标识符之间可能发生冲突
C++中提出了命名空间的概念
- 命名空间将 全局作用域分为不同的部分
- 不同命名空间中的标识符可以同名 而不会发生冲突
- 命名空间可以相互嵌套
- 全局作用域也叫 默认命名空间
//C++ 命名空间的定义
namespace Name
{
namespace Internal
{
}
}
C++命名空间的使用:
- 只用整个命名空间: using namespace name;
- 使用命名空间中的变量: using name::variable ;
- 使用默认命名空间中的变量: ::variable
#include<stdio.h>
namespace First
{
int i = 0;
}
namespace Second
{
int i = 1;
namespace Internal
{
struct P
{
int x;
int y;
};
}
}
int main()
{
using namespace First;
using Second::Internal::P;
printf("First::i = %d\n",i);
printf("Second::i = %d\n",Second::i);
P p ={2, 3};
printf("p.x = %d\n",p.x);
printf("p.y = %d\n",p.y);
return 0;
}

10、新型的类型转换
C方式强制类型转换 过于粗暴(任意类型之间都可以进行转换),难于定位
C++将强制类型转换分为 4 种不同的类型

static_cast 强制类型转换
- 用于 基本类型 间的转换
- 不能 用于 基本类型指针 间的转换
- 用于 有继承关系类对象 之间的转换 和 类指针之间的转换
const_cast 强制类型转换
- 用于去除 变量的只读属性
- 强制转换的 目标类型 必须是 指针 或 引用
reinterpret_cast 强制类型转换
- 用于 指针类型间 的强制转换
- 用于 整数 和 指针类型间的强制转换
dynamic_cast 强制类型转换
- 用于有继承关系的类指针间的转换
- 用于有交叉关系的类指针间的转换
- 具有类型检查的功能
- 需要虚函数的支持
#include<stdio.h>
void static_cast_demo(){
int i = 0X12345;
char c = 'c';
int *pi = &i;
char* pc = &c;
c = static_cast<char>(i); //将i int类型转换成 char类型,OK
// pc = static_cast<char*>(pi);//整型指针 转换,Error
}
void const_cast_demo(){
const int& j = 1; //产生了一个只读变量
int& k = const_cast<int&>(j);//将 j 的只读属性去掉,变成普通变量,用k来修改这个变量j 的值
const int x = 2; //产生了一个常量
int& y = const_cast<int&>(x); // 这个去掉只读属性,但是x进入了符号表,编译器会为x分配了空间,\
得到的内存里面为这个常量分配空间,y就是这个空间的别名
// int z = const_cast<int>(x);//const_cast只能用于引用 和指针转换, Error
k = 5;
printf("k = %d\n", k);
printf("j = %d\n", j);
y = 8;
printf("x = %d\n", x);
printf("y = %d\n", y);
printf("&x = %d\n", &x);
printf("&y = %d\n", &y);
}
void reinterpret_cast_demo(){
int i = 0;
char c = 'c';
int *pi = &i;
char* pc = &c;
pc = reinterpret_cast<char*>(pi);//OK
pi = reinterpret_cast<int*>(pc);
pi = reinterpret_cast<int*>(i);//将整形转换为指针,OK
// c = reinterpret_cast<char>(i);//基本类型的转换,Error
}
void dynamic_cast_demo(){
int i = 0;
int* pi = &i;
// char * pc = dynamic_cast<char*>(pi);//类指针之间,还需要有虚函数
}
int main()
{
static_cast_demo();
const_cast_demo();
reinterpret_cast_demo();
dynamic_cast_demo();
return 0;
}

2万+

被折叠的 条评论
为什么被折叠?



