2.7 C++ 新增的new和delete运算符

在C语言中,动态分配内存用 malloc() 函数,释放内存用 free() 函数,在C++中,这两个函数仍然可以使用。

示例代码: malloc()和free()函数

 

#include <iostream>#include <cstdlib>using namespace std; //声明默认的命名空间int main(){      int *p=(int*)malloc(4); //申请空间    *p=1234;    cout<<"*p="<<*p<<endl;    free(p); //释放空间return 0;}

输出结果:

PS D:\linux-share-dir\c_code> .\a.exe      

*p=1234

 

C++除了可以使用malloc()和free()函数外,又新增了两个关键字,new 和 delete。

new 用来动态分配内存,delete 用来释放内存;用 new 和 delete 分配内存更加简单。

示例代码:用 new 和 delete 分配内存

 

 

#include <iostream>using namespace std; //声明默认的命名空间int main(){      int *p=new int; //申请一个int大小的空间    *p=1234;    cout<<"*p="<<*p<<endl;    delete p; //释放空间return 0;}

 

new 操作符会根据后面的数据类型来推断所需空间的大小。

示例代码:连续分配多个字节空间

 

 

#include <iostream>using namespace std; //声明默认的命名空间int main(){      char *p=new char[50]; //申请一个50字节大小的空间    cout<<"请输入一个字符串:";    cin>>p;//从标准输入获取数据    cout<<"p="<<p<<endl;    delete[] p; //释放连续的空间return 0;}

输出结果:

PS D:\linux-share-dir\c_code> .\a.exe      

请输入一个字符串:hello

p=hello

和malloc()一样,new 也是在堆区分配内存,必须手动释放,否则只能等到程序运行结束由操作系统回收。为了避免内存泄露,通常 new 和 delete、new[] 和 delete[] 操作符应该成对出现,并且不要和C语言中 malloc()、free()一起混用

在C++中,建议使用 new 和 delete 来管理内存,它们可以使用C++的一些新特性,最明显的是可以自动调用构造函数和析构函数

2.8 C++函数的默认参数(缺省参数)

在C++中,定义函数时可以给形参指定一个默认的值,这样调用函数时如果没有给这个形参赋值,那么就使用这个默认的值。如果用户指定了参数的值,那么就使用用户指定的值,否则使用参数的默认值。

示例代码:

 

 

#include <iostream>using namespace std; //声明默认的命名空间 //定义函数的默认参数void func(int a=666,int b=888){    cout<<"a="<<a<<",b="<<b<<endl;} int main(){      func(); //不传参数,使用默认参数    func(123); //传递第一个参数,后面的使用默认参数    func(123,456); //传递实参return 0;}

输出结果:

PS D:\linux-share-dir\c_code> .\a.exe      

a=666,b=888

a=123,b=888

a=123,b=456

C++规定,默认参数只能放在形参列表的最后,而且一旦为某个形参指定了默认值,那么它后面的所有形参都必须有默认值。实参和形参的传值是从左到右依次匹配的,默认参数的连续性是保证正确传参的前提。

示例:

 

 

void func(int a, int b=10, int c=20){ } //正确写法void func(int a, int b, int c=20){ }//正确写法void func(int a, int b=10, int c=20, int d){ }//错误写法void func(int a, int b=10, int c, int d=20){ }//错误写法

 

 

除了在函数定义时,可以指定函数的默认参数,也可以在函数声明处指定默认参数。

示例代码: 在函数声明时指定默认参数

 

 

#include <iostream>using namespace std; //声明默认的命名空间void func(int a=666,int b=888); //声明函数缺省参数//定义函数的默认参数void func(int a,int b){    cout<<"a="<<a<<",b="<<b<<endl;} int main(){      func(); //不传参数,使用默认参数    func(123); //传递第一个参数,后面的使用默认参数    func(123,456); //传递实参return 0;}

输出结果:

PS D:\linux-share-dir\c_code> .\a.exe      

a=666,b=888

a=123,b=888

a=123,b=456

声明是用户可以看到的部分,用户都是信任函数的声明,因此编译器禁止声明和定义同时定义缺省参数值。

比如:下面语法就是错误的

 

 

void func(int a=666,int b=888); //声明函数缺省参数void func(int a=666,int b=888)//定义函数的默认参数{cout<<"a="<<a<<",b="<<b<<endl;}

 

上面代码中,声明和定义时,都定义了缺省参数,在同一个文件里写是错误的。

在多文件编程时,我们通常的做法是将函数声明放在头文件中,并且一个函数只声明一次,但是多次声明同一函数也是合法的。

C语言有四种作用域,分别是函数原型作用域局部作用域(函数作用域)、块作用域文件作用域(全局作用域),C++ 也有这几种作用域。

 

2.9 C++函数重载详解

函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。重载函数通常用来命名一组功能相似的函数,这样做减少了函数名的数量,避免了名字空间的污染,对于程序的可读性有很大的好处。

例如: 希望交换两个变量的值,这两个变量有多种类型,可以是 int、float、char、bool 等,需要通过参数把变量的地址传入函数内部,对于C语言代码,程序员通常需要分别设计出四个不同名的函数,来完成不同的4种类型交换。

示例代码:C语言设计出四个不同名的函数,完成交换功能

 

 

void func1(int *a,int *b){…};void func2(char *a,char *b) {…};void func3(float *a,float *b) {…};void func4(bool *a,bool *b) {…};

 

但在C++中,就很容易实现, C++ 允许多个函数拥有相同的名字,只要它们的参数列表不同就可以,这就是函数的重载(Function Overloading),借助重载,一个函数名可以有多种用途,方便用户调用,用户也无需记住多个函数名字。

示例代码:c++代码

 

 

#include <iostream>using namespace std; //声明默认的命名空间//函数声明void func(int *a,int *b);void func(char *a,char *b);void func(float *a,float *b);void func(bool *a,bool *b);//交换整型void func(int *a,int *b){    int tmp;    tmp=*a;    *a=*b;    *b=tmp;}//交换字符类型void func(char *a,char *b){    char tmp;    tmp=*a;    *a=*b;    *b=tmp;}//交换浮点数void func(float *a,float *b){    float tmp;    tmp=*a;    *a=*b;    *b=tmp;}//交换bool类型void func(bool *a,bool *b){    bool tmp;    tmp=*a;    *a=*b;    *b=tmp;} int main(){      /*1. 交换整型变量*/    int int_a=100,int_b=200;    cout<<"交换之前:"<<"a="<<int_a<<",b="<<int_b<<endl;    func(&int_a,&int_b);    cout<<"交换之后:"<<"a="<<int_a<<",b="<<int_b<<endl;        /*2. 交换字符类型变量*/    char char_a='A',char_b='B';    cout<<"交换之前:"<<"a="<<char_a<<",b="<<char_b<<endl;    func(&char_a,&char_b);    cout<<"交换之后:"<<"a="<<char_a<<",b="<<char_b<<endl;     /*3. 交换浮点类型变量*/    float float_a=1.123,float_b=2.456;    cout<<"交换之前:"<<"a="<<float_a<<",b="<<float_b<<endl;    func(&float_a,&float_b);    cout<<"交换之后:"<<"a="<<float_a<<",b="<<float_b<<endl;     /*4. 交换字符类型变量*/    bool a=true,b=false;    cout<<"交换之前:"<<"a="<<a<<",b="<<b<<endl;    func(&a,&b);    cout<<"交换之后:"<<"a="<<a<<",b="<<b<<endl;    return 0;}

输出结果:

PS D:\linux-share-dir\c_code> g++ .\app.cpp

PS D:\linux-share-dir\c_code> .\a.exe

交换之前:a=100,b=200

交换之后:a=200,b=100

交换之前:a=A,b=B

交换之后:a=B,b=A

交换之前:a=1.123,b=2.456

交换之后:a=2.456,b=1.123

交换之前:a=1,b=0

交换之后:a=0,b=1

 

函数的重载的规则:

(1)、函数名称必须相同。

(2)、参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。

(3)、函数的返回类型可以相同也可以不相同。

(4)、仅仅返回类型不同不足以成为函数的重载。

C++代码在编译时会根据参数列表对函数进行重命名,当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这叫做重载决议(Overload Resolution)。

函数重载仅仅是语法层面的,本质上它们还是不同的函数,占用不同的内存,入口地址也不一样。