在分析区别前,先来简单介绍一下C++的特点吧
C++基本介绍
- C语言运算符++,是自增的意思 ,C++在C语言的基础上进行自增(扩展),基本上兼容C语言
- C++ 是一种静态类型的、编译式的、通用的、大小写敏感的、不规则的编程语言,支持过程化编程、面向对象编程和泛型编程
C++的特点
-
支持面向对象(C语言面向过程),C++宏观上支持面向对象,微观上支持面向过程
-
支持运算符重载,让自定义的类型(struct/class)的数据也支持+-*/等运算操作
-
支持异常机制,错误处理(C语言中在<errno.h>库中,C++在<stdexcept>中)
-
支持泛型编程,对类型抽象化
根据C++的特点,我们就可以分析出C与C++的大致区别
C与C++的区别
-
C++基本上可以完全兼容C语言
-
C语言面向过程,C++面向对象,C++在宏观上面向对象,在微观上面向过程
-
C++支持运算符重载
-
C++支持异常机制
-
C++支持泛型编程
这样分析可能不够细致,不过别急,我们接着往下看
struct在C和C++中的区别
-
C++用struct定义结构体类型后,定义变量时可以不用struct关键字
-
sizeof(空结构体) 在C语言中为0,在C++中为1 ,C++这样做主要是为了标识开辟的内存
-
C++中可以在struct中定义函数
-
在struct中定义的函数,可以直接访问结构体中定义的变量,即属性
-
在函数中直接访问的是调用该函数的结构体变量的属性
-
-
C++struct中的变量可以用static修饰
-
用static修饰的属性,相当于进行了声明,需要在struct外进行定义
-
在用sizeof求结构体大小时,并不会包含static成员的大小
-
用static修饰的属性属于整个类,而不是只属于某个结构体变量,在内存中只有一份,同一类型的结构体的变量都可以共享这个静态属性
-
可以直接通过 类名::静态属性 访问静态属性,当然也可以通过对象名.静态属性来访问
-
-
C++struct中属性和方法可以用访问控制属性(public、protected、private)加以访问限制
-
C++中struct允许继承
enum在C和C++中的区别
- C++中的enum是一种独立的数据类型,不能直接用整数对它进行赋值,但C语言中可以
enum Direction {UP=0,DOWN,RIGHT,LEFT};
Direction d = UP;
d = 0, d = 1,d = 1024 ; // 错误的 但是C语言允许
int num = d;
num = LEFT; // 可以的
union在C和C++中的区别
- C++中支持匿名联合(既没有类型,也没有定义联合变量)
- C++中匿名联合表示的是变量在内存中的布局方式,可以直接访问匿名联合中的属性
//在编译时,以联合的形式编译布局a,b,c,即让a,b,c共用一块内存
union{
int a;
int b;
char c;
};
a = 1024;
b = 9527;
c = 'A';
字符串C和C++中的区别
- 在C语言中,字符串时用字符数组来表示的;而在C++中,既保留了C风格的字符串,同时引入了string类型的字符串
string s1; //空字符串
string s2 = "Hello world"; //C指针的初始化方式
string s3("Hello world");
string s4 = {"HEllo world"};
string s5 = s4;
string s6(10,'X'); //"XXXXXXXXXX"
//指针是一种天然的迭代器
string s7(iterator begin,iterator end); //[begin,end) 用这个区间中的字符构造字符串
函数在C和C++中的区别
-
C语言函数的隐式声明:gcc编译器在编译代码时,如果遇到一个调用的函数在此之前没有定义和声明,则为其隐式声明一个函数,且为其声明函数的返回值类型为int
-
C++中,g++编译器不会再为函数提供隐式函数声明,即C++调用函数之前,必先声明或定义
-
C++函数的形参列表为空,就相当于C语言中形参列表为void,即不能传参
C++中支持函数重载
- 函数重载: 即在同一个作用域下,可以定义同名的函数,但函数的参数列表不能相同
-
同一作用域下,函数名相同,参数列表不同即构成重载,在编译调用重载的函数时,会根据实参的个数和类型来绑定调用的函数,称为静态重载
-
前提条件:在同一个作用域下
-
必要条件:函数名相同,参数列表不同(形参个数不同,同位置的类型不同,指针和引用的常属性不同,即用const修饰与否)
-
重载与形参名和返回值类型无关
char max(char a,char b)
{
return a<b ? b : a;
}
int max(int a,int b)
{
return a<b ? b : a;
}
double max(double a,double b)
{
return a<b ? b : a;
}
// 指针与引用的常属性不同,也能构成重载
void func1(int *p)
{
}
void func1(const int *p)
{
}
void func2(int &a)
{
}
void func2(const int &a)
{
}
- 为什么C++支持重载,而C语言不支持重载?
- 在C++中,g++编译在编译函数时,会对函数进行改名操作;而在C语言中,gcc编译器不会对函数名进行更名操作
- 可以用 extern "C" 来指定让g++使用C风格的方式来编译函数
extern "C" //如果需要在C语言中调用C++的函数,那就必须使用extern "C"来编译C++的代码
void func(){}
extern "C"{
void f1(){};
void f2(){}
}
C++中支持函数默认值(缺省值)
-
在定义函数时,形参可以声明默认值,就好像定义变量直接初始化一样
-
如果一个函数有默认值,则在调用该函数时,可以为该形参传递实参,也可以选择不传,不传则采用默认值
-
函数默认值遵循靠右原则:如果一个形参有默认值,则其后面(右边)所有的形参都必要要有默认值,即有默认值的形参一定是靠右的
-
如果函数的声明和定义是分离的,则缺省值只能出现在函数声明中
void func(int a=1024);// 声明和定义分离时默认值需在函数声明中
void func(int a){
}
C++中支持函数哑元
- 只有类型,没有形参名的形参谓之哑元
- 在调用哑元函数时,一定要传递一个该类型的实参,即哑元只关心类型,不关心值的大小
void func(int){ // 这里int 形参就是哑元
}
C++中支持函数内敛
- 内敛函数用inline修饰,它只是一种优化方式,可以提高代码的运行效率,但最终是否使用还是要取决于编译器
- 内敛函数直接用函数的二进制指定替换函数的调用指令,可以加快程序执行速度 ,但会使可执行程序变大
- 所以简单且重复调用的函数适合声明为内联函数
使用动态内存在C和C++中的区别
-
动态内存:使用堆内存,需要程序员手动申请和手动释放
-
C语言中有一套函数来操作动态内存:malloc/calloc/realloc/free
-
C++中申请动态内存用new / new[],释放动态内存用delete/delete[]
// 申请单个数据的内存
数据类型* p = new 数据类型(初始值);
int *p1 = new int; // new 一个int类型的初始空间,默认初始化为0
int *p2 = new int(1024); // new 一个int类型的初始空间,并初始化为1024
int *p3 = new int{111}; // c++11 初始化列表
delete p1;
delete p2;
delete p3;
// 变量的初始化
int a = 1024;
struct Stu = {1,"mark"};
string s("hello");
// C++11 统一的初始化方式 {} 称为初始化列表
int a = {1024};
struct Stu = {1,"mark"};
string s{"hello"};
// 申请数组的动态内存
数据类型* p = new 数据类型[个数]
int *p1 = new int[10];
数据类型* p = new 数据类型[个数]{初始值}
int *p2 = new int[5]{1,2,3,4,5};
new/delete 和 malloc/free的区别
-
new/delete 是C++中申请动态内存的操作符(不是函数),malloc和free是C语言标准库中申请动态内存的函数
-
new和new[] 会调用类的构造函数,但malloc和calloc不会调用类的构造函数;delete和delete[] 会调用类的析构函数,但free不会调用类的析构函数
-
new 申请动态内存时,需要给定特定的类型,不需要给定类型的字节宽度;但malloc需要给定申请动态内存的字节大小,即new只关心类型;malloc只关心大小
-
申请多个内存空间时,new用的是new[] ,但malloc只需要传递内存大小即可
-
new和new[] 返回的是特定类型的指针,但malloc和calloc返回的是void * ,需要转换为对应类型的指针
-
new和new[] 申请动态内存失败时,是抛出bad_alloc的异常;但malloc和calloc 申请动态内存失败时,返回的是NULL
-
new申请内存用delete释放,new[] 申请的数组内存用delete[] 释放;但malloc/calloc 申请的动态内存一律用free来释放
const在C和C++中的区别
- C语言中的const仅仅表示只读,而C++中定义的const是真正的常量,g++编译器会对const进行替换处理,在编译时对使用const修饰的变量会用const常量的值直接替换(相当于C语言中的宏),但与C语言不同的是C++中会为这个常量分配内存
int main()
{
const int a = 1024;
int *p = &a;
*p = 9527;
cout << *&a << endl; // 1024
cout << *p << endl; // 9527
}
- C++中用const定义的常量必须初始化
const int num; // 编译错误
- C++中变量和对象的const一旦缺失,就会报错
最后,再讲一下C++中特有的引用与C语言中的指针的区别
指针与引用的区别
1、指针是实体变量,但是引用不是实体变量(定义的变量大小固定),引用的大小是根据目标的类型决定的
sizeof(int *) ---4 sizeof(double *) --- 4 // 32位Linux系统为例
sizeof(int &) ---4 sizeof(double &) ---8
2、指针可以不初始化,但引用必须初始化
3、指针可以初始化为NULL,但引用不能为空
4、指针可以改变指向,但引用不能更改引用的目标(可以改变目标的值,但不能改变指向)
5、可以定义指针的引用,但不能定义引用的指针
int n = 1024;
int *pn = &n;
int *&rpn = pn; //指针的引用 rpn == pn
int &ra = n;
int &*pn = &ra; // 编译错误,无法定义 int& 类型的指针
6、可以定义指针的指针,但不能定义引用的引用
int n=1024;
int *pn = &n;
int **pn = &pn; // 指针的指针 二级指针
int &rn = n;
int &&rrn = rn; // 错误的 ,C++11 中定义int&& 类型的为右值引用,没有二级引用
7、可以定义数组的引用,但不能定义引用的数组
int arr[5] = {1,2,3,4,5};
int (&ra)[5] = arr; // 数组的引用,ra == arr
void func(int (&arr)[5])
{
sizeof(arr); // 20 ,区别于指针,不会退化为指针
}
int& brr[5]; // 编译错误,不能声明引用的数组
8、引用的本质还是指针,底层是用指针实现的