C语言与C++可以说是天差地别,所以我今天主要阐述一些基本的区别。
1、函数参数的默认值
在C语言中我们在函数的参数列表中写入几个参数就必须在调用此函数时给参数值,但是在C++中给了函数参数具有默认值的方法。在函数的声明或者定义的地方,给函数的参数加上默认的赋值,在函数调用处,可以不必给该参数传值,会默认的传入默认值。函数的默认值只能给一次。
注意:参数默认值的赋值,必须从右向左,依次赋默认值,函数参数的默认值,不可以重复赋值
#include<iostream>
using namespace std;
//错误,必须从右向左给默认值
//int fun(int a= 100, int b , int c);
//int fun(int a, int b = 100, int c);
//错误,只能给一次默认值
//int a(int a,int b, int c = 10);
//int a(int a,int b, int c = 10){}
//正确
//int fun(int a, int b = 100, int c = 30);
int fun(int a, int b, int c = 40)
{
cout << "a:" << a << endl;
cout << "b:" << b << endl;
cout << "c:" << c << endl;
return 0;
}
int main()
{
fun(10, 20);
return 0;
}
上述代码的结果为:
2、内联函数(inline)
内联函数是一用来建议编译器对一些特殊函数进行内联扩展,也就是说建议编译器将指定的函数体插入并取代每一处调用该函数的地方,从而节省了每次调用函数带来的额外时间开支。内联函数在编译期展开。
在debug版本中可以调试,会产生一个local的符号,也会进行函数栈帧开辟之类的动作。
在release版本中,不会产生符号,也不会进行栈帧开辟等动作,会在调用点进行展开。
1)关键字:inline
2)内联函数与宏函数以及静态函数的比较
inline函数 | static函数 | 宏函数 |
debug版本产生local符号 release版本不产生符号 | 产生local符号 | 不产生符号 |
debug版本进行栈帧操作 release版本直接在调用点展开(编译期) | 进行栈指针操作 | 在调用点展开(预编译期) |
可以调试 | 可以调试 | 不可以调试 |
有类型检查 | 有类型检查 | 没有类型检查 |
#include<iostream>
using namespace std;
//宏函数
#define SUM(a,b){a+b}
//内联函数
inline int sum2(int a, int b)
{
return a + b;
}
3、申请(new)和释放(delete)动态内存
在写C语言的代码时,当我们需要动态的去申请一块内存时,我们会想到利用malloc以及free来进行内存的申请,但是这块从堆上开辟的内存,我们还需要进行初始化才可以使用,虽然提供了calloc这种可以初始化的函数,但是也并不是很好用,所以在C++中系统为我们提供了new和delete方法来动态申请内存。
在C++中是以类和对象为基础的,每个类都有自己的构造函数和析构函数,所以new/delete方式管理内存相对于malloc/free的方式管理的优势就体现出来了,因为它们会在对象一创建出来便被初始化,出了作用域便被自动清理。
1)开辟内存的方式:
开辟单变量地址空间:
格式1:指针变量名=new 类型标识符;
格式2:指针变量名=new 类型标识符(初始值);
格式3:指针变量名=new 类型标识符 [内存单元个数];
开辟数组空间:
指针变量名=new 类型名[下标表达式];
2)释放内存的方式:
释放单变量地址空间:
delete 指针变量名,释放单变量地址空间;
释放数组空间:
delete [ ] 指针变量名,释放一个数组的空间;
int main()
{
int *p = new int; //指针p指向一块空间
int *p = new int(); //指针p指向一块空间,初始化为0
int *p = new int[]; //指针p指向分配的数组,未初始化
int *p = new int[10]; //指针p指向分配的大小为10的数组,未初始化
int *p = new int[](); //指针p所指向的地址初始化为0
delete p; //回收p所指向的值
delete [] p; //回收p所指向的数组
}
3)开辟和释放自定义类型的空间
new会先调用operator new函数,申请足够的内存。然后调用自定义类型的构造函数,初始化成员变量,最后返回自定义类型指针。delete先调用析构函数,然后调用operator delete函数释放内存。
4、函数重载
重载函数是函数的一种特殊情况,为方便使用,C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个运算符完成不同的运算功能。返回值不参与重载,重载函数的调用类型要明确。
重载的要求:函数名相同 参数列表不同 处于同一作用域
例:
#include<iostream>
using namespace std;
bool compare(int a, int b)
{
cout << typeid(a).name() << " " << typeid(b).name() << endl;
return a == b;
}
bool compare(float a, float b)
{
cout << typeid(a).name() << " " << typeid(b).name() << endl;
return a == b;
}
bool compare(char a, char b)
{
cout << typeid(a).name() << " " << typeid(b).name() << endl;
return a == b;
}
bool compare(char *a, char*b)
{
cout << typeid(a).name() << " " << typeid(b).name() << endl;
return !strcmp(a, b);
}
int main()
{
compare('a', 'b');
compare(10, 20);
compare(10.2f, 20.3f);
compare("a", "b");
return 0;
}
执行的结果为:
5、引用和指针的区别
指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。
指针是指保存一个变量的地址的变量,而引用是指一个变量的另一个名字。
指针需要进行指针的传递和解引用才可以对值进行修改,而引用是在我们用到引用的地方系统将其替换成解引用的指针,实质上引用的内部是由指针实现的。
我们输入以下代码:
int main()
{
int a = 0;
int *p = &a;
int &b = a;
*p = 1;
b = 2;
return 0;
}
通过反汇编我们可以看到在引用的底部就是利用指针来实现的。
可以从上图中看到*p = 1通过了两部替换了a的值,也就是进行了解引用然后对值进行替换,但是引用确可以一部直接进行解引用和值的替换。所以引用在直接使用时是指针解引用。
引用的特点:
1)引用只能在定义时被初始化一次,之后不可变;指针可变;
2)引用没有const,指针有const,const的指针不可变;
3)引用不能为空,指针可以为空;所以我们一般对于指针来说需要判断指针是否为空,防止解引用时非法访问内存。而引用不 需要进行判断是否为空,因为它不可以为空。
4)引用是类型安全的,而指针不是 (引用比指针多了类型检查)