深拷贝和浅拷贝

  • 浅拷贝:简单的赋值拷贝操作
  • 深拷贝:在堆区重新申请空间,进行拷贝操作
  • 如果属性是在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。

示例:在类中成员属性定义一个指针成员(下面程序中的 int* m_height; ),在有参构造函数中,利用new在堆区开辟一个空间,让该指针指向这个内存,完成赋值。若用户自己不定义拷贝赋构造函数,使用编译器定义的浅拷贝函数会出现重复释放内存空间的问题。代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include<iostream>
using namespace std;
class Person {
public:
Person() {
cout << "Person 的默认构造函数调用。" << endl;
}
Person(int a,int height) {
age = a;
m_height = new int(height);//堆区开辟空间
cout << "Person 的有参构造函数调用。" << endl;
}
~Person() {
//析构代码,将堆区开辟的数据做释放操作
if (m_height != NULL) {
delete m_height;
m_height = NULL;//避免野指针
}
cout << "Person 的析构函数调用。" << endl;
}
int age;
int* m_height;
};
void test01() {
Person p1(19,160);
cout << "p1的年龄" << p1.age <<" 身高为:"<< *p1.m_height << endl;
Person p2(p1); //编译器定义了一个浅拷贝函数
cout << "p2的年龄" << p2.age << " 身高为:" << *p2.m_height << endl;
}
int main() {
test01();
return 0;
}

注解:根据栈先入后出的规则,先析构P2,此时地址为0x0011所指向的内存已经释放,然后析构P1的时候便引发堆区的内存重复释放。

解决方法:深拷贝

示例:用户定义自己的深拷贝构造函数,也就是利用new 重新开辟一个新的空间

1
2
3
4
5
6
7
//自己实现拷贝构造函数 解决浅拷贝的问题
Person(const Person& p) {
cout << "Person的拷贝构造函数的调用" << endl;
age = p.age;
//m_height = p.m_height;//编译器默认实现是这行代码
m_height = new int(*p.m_height);//深拷贝,在堆区重新开辟内存
}

注解:拷贝时指向了另外一个内存,便不会出现重复释放内存的问题了

构造函数调用规则

  • 默认情况下,c++编译器会给一个类添加三个函数

    (1)默认构造函数(无参,函数体为空)

    (2)默认析构函数(无参,函数体为空)

    (3)默认拷贝构造函数,对属性进行值拷贝

    (4)赋值运算符 operator= ,对属性进行值拷贝

  • 构造函数调用规则如下:

    (1)如果用户只定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造

    (2)如果用户只定义拷贝构造函数,c++不会在提供其他构造函数

示例1:不定义默认构造函数,只定义有参构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
using namespace std;
class Person {
public:
Person(int a) {
age = a;
cout << "Person 的有参构造函数调用。" << endl;
}
~Person() {
cout << "Person 的析构函数调用。" << endl;
}
int age;
};
int main() {
//Person p;//报错,写了有参构造函数,编译器不会提供默认构造函数
Person p(10);
Person p1(p);//有默认的拷贝构造函数
cout << "p1的年龄" << p1.age << endl;
return 0;
}

运行结果:

1
2
3
4
Person 的有参构造函数调用。
p1的年龄10
Person 的析构函数调用。
Person 的析构函数调用。

示例二:只定义拷贝构造函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>
using namespace std;
class Person {
public:
Person(const Person &p) {
age = p.age;
cout << "Person 的拷贝构造函数调用。" << endl;
}
~Person() {
cout << "Person 的析构函数调用。" << endl;
}
int age;
};
int main() {
//Person p;//报错,写了有参构造函数,编译器不会提供默认构造函数
//Person p(10);报错
//Person p1(p);
cout << "p1的年龄" << p1.age << endl;
return 0;
}

拷贝函数调用时机

  • c++中拷贝构造函数调用时机通常有三种情况

    (1)使用一个已经创建完毕的对象来初始化一个新对象

    (2)值传递的方式函数参数传值

    (3)以值方式返回局部对象

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<iostream>
using namespace std;


//3、以值方式返回局部对象
class Person {
public:
Person() {
cout << "Person 的默认构造函数调用。" << endl;
}
Person(int a) {
age = a;
cout << "Person 的有参构造函数调用。" << endl;
}
Person(const Person &p) {
age = p.age;
cout << "Person 的拷贝构造函数调用。" << endl;
}

~Person() {
cout << "Person 的析构函数调用。" << endl;
}
int age;
};
//使用一个已经创建完毕的对象来初始化一个新对象
void test01() {
Person p1(10);
Person p2(p1);
cout << "p2 的年龄为:" << p2.age << endl;
}
//值传递的方式函数参数传值
void dowork(Person p) {

}
void test02() {
Person p;
dowork(p);
}
//值方式返回局部对象
Person dowork2() {
Person p1;//默认构造函数
cout << (int)&p1 << endl;
return p1;
}
void test03() {
Person p = dowork2(); //拷贝构造函数,相当于Person p = p1;,拷贝之后在析构 dowork2 函数返回的局部对象
cout << (int)&p << endl;
}
int main() {
cout << "test01" << endl;
test01();
cout << "\ntest02" << endl;
test02();
cout << "\ntest03" << endl;
test03();
return 0;
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
test01
Person 的有参构造函数调用。
Person 的拷贝构造函数调用。
p2 的年龄为:10
Person 的析构函数调用。
Person 的析构函数调用。

test02
Person 的默认构造函数调用。
Person 的拷贝构造函数调用。
Person 的析构函数调用。
Person 的析构函数调用。

test03
Person 的默认构造函数调用。
17823492
Person 的拷贝构造函数调用。
Person 的析构函数调用。
17823740
Person 的析构函数调用。

拷贝函数调用时机

  • c++中拷贝构造函数调用时机通常有三种情况

    (1)使用一个已经创建完毕的对象来初始化一个新对象

    (2)值传递的方式函数参数传值

    (3)以值方式返回局部对象

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include<iostream>
using namespace std;


//3、以值方式返回局部对象
class Person {
public:
Person() {
cout << "Person 的默认构造函数调用。" << endl;
}
Person(int a) {
age = a;
cout << "Person 的有参构造函数调用。" << endl;
}
Person(const Person &p) {
age = p.age;
cout << "Person 的拷贝构造函数调用。" << endl;
}

~Person() {
cout << "Person 的析构函数调用。" << endl;
}
int age;
};
//使用一个已经创建完毕的对象来初始化一个新对象
void test01() {
Person p1(10);
Person p2(p1);
cout << "p2 的年龄为:" << p2.age << endl;
}
//值传递的方式函数参数传值
void dowork(Person p) {

}
void test02() {
Person p;
dowork(p);
}
//值方式返回局部对象
Person dowork2() {
Person p1;//默认构造函数
cout << (int)&p1 << endl;
return p1;
}
void test03() {
Person p = dowork2(); //拷贝构造函数,相当于Person p = p1;,拷贝之后在析构 dowork2 函数返回的局部对象
cout << (int)&p << endl;
}
int main() {
cout << "test01" << endl;
test01();
cout << "\ntest02" << endl;
test02();
cout << "\ntest03" << endl;
test03();
return 0;
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
test01
Person 的有参构造函数调用。
Person 的拷贝构造函数调用。
p2 的年龄为:10
Person 的析构函数调用。
Person 的析构函数调用。

test02
Person 的默认构造函数调用。
Person 的拷贝构造函数调用。
Person 的析构函数调用。
Person 的析构函数调用。

test03
Person 的默认构造函数调用。
17823492
Person 的拷贝构造函数调用。
Person 的析构函数调用。
17823740
Person 的析构函数调用。

sort详解

sort类函数

  • sort()函数是c++中的一种排序方法,实现的方法类似于快速排序的方法,时间复杂度为 n*log2(n)。
  • sort 类函数总结
函数名 功能描述
sort 对给定区间进行排序
stable_sort 对给定所有元素进行稳定排序
partial_sort 对给定区间所有元素部分排序
partial_sort_copy 对给定区间复制并排序
nth_element 找出给定区间的某个位置对应的元素
is_sorted 判断区间是否有序
partition 将符合某个条件的元素放在前面
stable_partition 相对稳定的将符合某个条件的元素放在前面

sort()

  • 头文件:#include

  • 语法:sort(begin,end,cmp)

    begin: 待排序数组内存的起始地址

    end: 待排序数组结束地址的下一位

    cmp: 此参数可以不写,此时默认非降序排序(简单一点升序)

int 型排序

示例一:没有cmp参数

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
#include <algorithm>
using namespace std;
int main() {
int arr[5] = {3,1,5,2,6};
sort(arr, arr + 5);
for (int i = 0;i < 5;i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}

运行结果:

1
1 2 3 5 6

示例二:利用cmp 参数实现降序排序,当cmp函数返回值为true时,表示cmp 函数的参数将会按照自定义的规则排序。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
#include <algorithm>
using namespace std;
bool cmp(int x, int y) {
return x > y;//第一个参数比第二个参数大,则排在前面
}
int main() {
int arr[5] = {3,1,5,2,6};
sort(arr, arr + 5, cmp);//使用重载函数形式,表明将要使用自定义的排序规则
for (int i = 0;i < 5;i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}

运行结果:

1
6 5 3 2 1

示例三:利用 functional 提供的一堆基于模板的比较函数对象:equal_to<类型>、not_equal_to<类型>、

greater<类型>、greater_equal<类型>、less<类型>、less_equal<类型>。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<iostream>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
int arr[5] = {3,1,5,2,6};
sort(arr, arr + 5, greater<int>());
for (int i = 0;i < 5;i++) {
cout << arr[i] << " ";
}
cout << endl;
sort(arr, arr + 5, less<int>());
for (int i = 0;i < 5;i++) {
cout << arr[i] << " ";
}
cout << endl;
return 0;
}

运行结果:

1
2
6 5 3 2 1
1 2 3 5 6

string 类排序

  • 一个字符串间字符的排序
1
2
3
4
5
6
7
8
9
10
11
12
13
#include<iostream>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
string str = "hello";
sort(str.begin(), str.end());//顺序排序
cout << str << endl;
sort(str.rbegin(), str.rend());//逆序排序
cout << str << endl;
cout << endl;
return 0;
}

运行结果:

1
2
ehllo
ollhe
  • 字符串间的排序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <string>
#include<iostream>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
string str[4];
for (int i = 0;i < 4;i++) {
getline(cin,str[i]);
}
sort(str, str+4,less<string>());
for (int i = 0;i < 4;i++) {
cout << str[i] << endl;
}
return 0;
}
  • 结构体类型的排序:先按照a降序,如果a相同在按照b降序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream> 
#include <algorithm>
struct test {
int a, b;
};
bool cmp(test& t1, test& t2) {
if (t1.a != t2.a)
return t1.a > t2.a;
return t1.b > t2.b;
}
using namespace std;
int main() {
test t[4];
for (int i = 0;i < 4;i++) {
cin >> t[i].a >> t[i].b;
}
sort(t, t + 4, cmp);
for (int i = 0;i < 4;i++) {
cout << t[i].a << " " << t[i].b << endl;
}
return 0;
}

构造函数的分类和调用

  • 分类方式

    (1)按参数分:有参构造和无参构造

示例:

1
2
3
4
5
6
7
8
9
10
11
class Person {
public:
Person() {
cout << "Person 的无参构造函数的调用" << endl;
}
Person(int a) {
age = a;
cout << "Person 的有参构造函数的调用" << endl;
}
int age;
};

​ (2)按类型分:普通构造和拷贝构造

示例:

1
2
3
4
5
6
7
8
9
10
class Person {
public:
//拷贝构造函数语法: 类名(const 类名 &对象名)
Person(const Person &p) {
//将传入的person属性传到我身上
age = p.age;
cout << "Person 的拷贝构造函数的调用" << endl;
}
int age;
};
  • 调用方式

    (1)括号法

示例:

1
2
3
Person p;       //默认构造函数的调用
Person p2(10); //调用有参构造函数
Person p3(p2); //调用拷贝构造函数

​ (2)显示法

示例:

1
2
Person p4 = Person(10);//有参构造
Person p5 = Person(p4);//拷贝构造

​ (3)隐式转换法

1
2
Person p6 = 10;//等同于 Person p6 = Person(10);
Person p7 = p6;//等同于 Person p6 = Person(p6);
  • 注意事项

    (1)当调用默认构造函数加 () ,编译器认为是一个函数声明,不会认为在创建对象。

示例:通过观察是否调用构造函数来判断

1
2
3
4
5
6
7
8
9
void test01() {
cout<<"aaa"<<endl;
Person p1(); //编译器认为是一个函数声明,不会认为在创建对象
}
int main() {
test01();
system("pause");
return 0;
}

(2)匿名函数:当前行执行结束后,系统会自动回收这个对象

1
Person(20); //单独拿出来就是匿名对象

(3)利用拷贝构造函数初始化匿名对象,要注意重定义

1
2
Person p; 
Person (p);//编译器自动认为 Person(p); 等同于 Person p; 造成重定义
  • 整体代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include<iostream>
using namespace std;
class Person {
public:
Person() {
cout << "Person 的无参构造函数的调用" << endl;
}
Person(int a) {
age = a;
cout << "Person 的有参构造函数的调用" << endl;
}
//拷贝构造函数 类名( const 类名 &对象名)
Person(const Person &p) {
//将传入的person属性传到我身上
age = p.age;
cout << "Person 的拷贝构造函数的调用" << endl;
}
~Person() {
cout << "Person 的析构函数的调用" << endl;
}
int age;
};
//调用
void test01() {
Person p;//默认构造函数的调用
Person p2(10);//调用有参构造函数
Person p3(p2);//调用拷贝构造函数

//注意:调用默认构造函数不要加 ()
Person p1();//编译器认为是一个函数声明,不会认为在创建对象

cout << "p2的年龄:" << p2.age << endl;
cout << "p3的年龄:" << p3.age << endl;

//3、显示法
Person p4 = Person(10);//有参构造
Person p5 = Person(p4);//拷贝构造

Person(20);//单独拿出来就是匿名对象,当前行执行结束后,系统会自动回收这个对象

//注意:不要利用拷贝构造函数初始化匿名对象
//Person (p);//报错,编译器自动认为 Person(p); == Person p;重定义
cout << "aaaa" << endl;

//5、隐式法
Person p6 = 10;//等同于 Person p6 = Person(10);
Person p7 = p6;//等同于 Person p6 = Person(p6);
}
int main() {
test01();
system("pause");
return 0;
}

运行结果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Person 的无参构造函数的调用
Person 的有参构造函数的调用
Person 的拷贝构造函数的调用
p2的年龄:10
p3的年龄:10
Person 的有参构造函数的调用
Person 的拷贝构造函数的调用
Person 的有参构造函数的调用
Person 的析构函数的调用
aaaa
Person 的有参构造函数的调用
Person 的拷贝构造函数的调用
Person 的析构函数的调用
Person 的析构函数的调用
Person 的析构函数的调用
Person 的析构函数的调用
Person 的析构函数的调用
Person 的析构函数的调用
Person 的析构函数的调用

封构造函数和析构函数

  • c++中的面向对象来源于生活,每个对象都有初始设置和对象销毁前清理数据的设置。
  • 对象的初始化和清理也是一个很重要的问题,一个对象或者变量没有初始状态,也不及时清理,会造成一定的安全问题。
  • c++利用构造函数和析构函数解决了上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作,这是编译器强制做的事情,因此如果程序员不提供构造函数和析构函数,那么编译器提供的这两个函数是空实现。

1. 构造函数

  • 主要作用在于创建对象是为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用。

  • 语法:类名(){}

    (1)构造函数,没有返回值

    (2)函数名和类名相同

    (3)构造函数可以有参数,因此可以发生重载

    (4)程序在调用对象时自动调用,而且只调用一次

2. 析构函数

  • 主要作用在于对象销毁前系统自动调用执行一些清理工作。

  • 语法: ~类名(){}

    (1)没有返回值

    (2)函数名和类名相同,在名称前面加上符号~

    (3)析构函数不可以有参数,因此不可以发生重载

    (4)程序在对象销毁前会自动调用析构,而且值调用一次

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include<iostream>
using namespace std;
class Person {
public:
Person() {
cout << "Person 的构造函数的调用" << endl;
}
~Person() {
cout << "Person 的析构函数的调用" << endl;
}
};
void test01() {
Person p;//栈上的数据,test01执行完后,释放这个对象
}
int main() {
test01();
cout << "退出 test01" << endl;
Person p;//自动调用一次构造函数
system("pause");
//执行完main才会调用析构函数
return 0;
}

运行结果:

1
2
3
4
5
6
Person 的构造函数的调用
Person 的析构函数的调用
退出 test01
Person 的构造函数的调用
请按任意键继续. . .
Person 的析构函数的调用

封装小例子——点和圆

  • 案例描述:判断点和圆的位置

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#include <iostream>
using namespace std;
//圆类:
//半径长度、圆心坐标
class Circle {
private:
double c_r;//半径长度 可读可写
double c_x;//圆心横坐标 可读可写
double c_y;//圆心纵坐标 可读可写
public:
//获取半径长度
double getC_r() {
return c_r;
}
//设置圆的半径
void setC_r(double R) {
c_r = R;
}
//获取圆心横坐标
double getC_x() {
return c_x;
}
//设置原坐标
void setC_x(double x) {
c_x = x;
}
//获取圆心纵坐标
double getC_y() {
return c_y;
}
//设置纵坐标
void setC_y(double y) {
c_y = y;
}
};
//点类
//坐标 可读可写
class point {
private:
double p_x;
double p_y;
public:
//获取圆心横坐标
double getP_x() {
return p_x;
}
//设置原坐标
void setP_x(double x) {
p_x = x;
}
//获取圆心纵坐标
double getP_y() {
return p_y;
}
//设置纵坐标
void setP_y(double y) {
p_y = y;
}
};
void RalationOf_PC(Circle &c,point &p) {
int dist = pow(c.getC_x() - p.getP_x(), 2) + pow(c.getC_y() - p.getP_y(), 2);
//cout << "点到圆的距离的平方为:" << dist << endl;
if (dist > pow(c.getC_r(),2)) {
cout << "点在圆外" << endl;
}
if (dist < pow(c.getC_r(), 2)) {
cout << "点在圆内" << endl;
}
if (dist == pow(c.getC_r(), 2)) {
cout << "点在圆上" << endl;
}
}
int main() {
Circle c;
c.setC_r(2);
c.setC_x(1);
c.setC_y(0);
point p;
p.setP_x(3);
p.setP_y(0);
RalationOf_PC(c, p);
return 0;
}

运行结果:

1
点在圆上

封装

c++三大特性(封装)

1. 封装的意义

  • 属性 成员属性 成员变量

    行为 成员函数 成员方法

  • 将属性和行为作为一个整体,表现生活中的事物

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
//设计一个学生类,属性:姓名和学号
//,可以给姓名和学号赋值,可以显示学生的姓名和学号
#include <iostream>
using namespace std;
class student
{
public:
//给姓名赋值
void setName(string Name) {
name = Name;
}
//给学号赋值
void setId(string Id) {
id = Id;
}
//显示学生姓名
void showStudent() {
cout << "姓名:" << name << "\n学号:" << id << endl;
}
private:
string name;
string id;
};
int main() {
student s1;
s1.setId("04161034");
s1.setName("hxx");
s1.showStudent();
return 0;
}

运行结果:

1
2
姓名:hxx
学号:04161034
  • 将属性和行为加以权限控制

    (1)公共权限 public 类内可以访问 类外可以访问
    (2)保护权限 protected 类内可以访问 类外不可以访问
    (3)私有权限 private 类内可以访问 类外不可以访问

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 #include <iostream>
using namespace std;
class Person {
//公共权限
public:
string name;//姓名
protected:
string car;
private:
int password;//银行卡密码
public:
void set() {
name = "张三";
car = "卡车";
password = 123456;
}
};
int main() {
Person p1;
p1.name = "hxx";
//p1.car = "小车";//报错,类外不可访问
//p1.password = 123;//报错
return 0;
}

2. struct 和 class 的区别

  • 在c++中struct 和 class 的区别在于默认的访问权限不同

    (1)struct 默认权限为公共

    (2)class 默认权限为私有

3. 成员属性设置为私有

  • 优点

    (1)将所有成员属性设置为私有,可以自己控制读写权限

    (2)对于写权限,可以检测数据的有效性

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
using namespace std;
class Person {
private:
//姓名 可读可写
string name;
//年龄 可读可写
int age;
public:
//利用公共行为来设置姓名
void setName(string Name) {
name = Name;
}
//获取姓名
string getName() {
return name;
}
//设置年龄
void setAge(int Age) {
if (Age > 150 || Age < 0) {
age = 0;
cout << "输入年龄不正常" << endl;
return;//退出
}
age = Age;
}
//获取年龄
int getAge() {
return age;
}
};
int main() {
Person p;
p.setName("张三");
cout << "姓名为:" << p.getName() << endl;
p.setAge(10);
cout << "年龄为:" << p.getAge() << endl;
return 0;
}

运行结果:

1
2
姓名为:张三
年龄为:10

函数提高

1. 函数默认参数

  • 在c++中,函数的形参列表中的形参可以有默认值。

  • 语法:返回值类型 函数名 (参数 = 默认值){}

  • 注意:

    (1)如果某个位置已经有了默认参数,那么聪慧这个位置往后,从左到右都必须有默认值。
    (2)函数的声明和实现只能有一个地方写默认参数。

示例:

1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
int func(int a, int b = 10, int c = 20) {
return a + b + c;
}
int main() {
cout << func(10) << endl;
cout << func(10, 10, 10) << endl;
return 0;
}

运行结果:

1
2
40
30

2. 函数的占位参数

  • c++中函数的形参列表里可以有占位参数,用来做占位,调用函数时必须填补该位置。
  • 语法:返回值类型 函数名 (数据类型){}
  • 占位参数可以有默认参数。
1
2
3
4
5
6
7
8
9
10
#include <iostream>
using namespace std;
void func(int a , int = 10) {
cout << "this is a func" << endl;
}
int main() {
func(10, 10);//占位参数必须填补
func(20);//占位参数有默认参数
return 0;
}

3.函数重载

3.1 函数重载概述

  • 作用:函数名可以相同,提高复用

  • 函数重载满足条件

    (1)同一个作用域下

    (2)函数名称相同

    (3)函数参数类型不同或者个数不同或者顺序不同

  • 注意:函数的返回值不可以作为函数重载的条件

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>
using namespace std;
void func() {
cout << "func() 的调用" << endl;
}
void func(int a) {
cout << "func(int a) 的调用" << endl;
}
void func(double a) {
cout << "func(double a) 的调用" << endl;
}
void func(double a, int b) {
cout << "func(double ,int)的调用" << endl;
}
void func(int a, double b) {
cout << "func(int ,double)的调用" << endl;
}
//函数的返回值不可以作为函数重载的条件
//int func() {
// cout << "func() 的调用" << endl;
//}
int main() {
func();
func(10);
func(2.1);
func(1, 2.1);
func(2.1, 1);
return 0;
}

运行结果:

1
2
3
4
5
func() 的调用
func(int a) 的调用
func(double a) 的调用
func(int ,double)的调用
func(double ,int)的调用

3.2 函数重载注意事项

  • 引用作为重载条件

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
using namespace std;

void func(int &a) {
cout << "func(int &a) 的调用" << endl;
}
void func(const int &a) {
cout << "func(const int &a) 的调用" << endl;
}
int main() {
int a = 10;
func(a);//a本身是变量,可读可写

func(10);
//若是调用func(int &a),相当于执行一条int &a = 10;这是不合法的。
//调用func(const int &a),相当于是 const int &a = 10; 这是合法的。
return 0;
}

运行结果:

1
2
func(int &a) 的调用
func(const int &a) 的调用
  • 函数重载碰到函数默认值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <iostream>
using namespace std;
void func(int a,int = 10) {
cout << "func(int a,int 10) 的调用" << endl;
}
void func(int a) {
cout << "func(int a)的调用" << endl;
}
int main() {
int a = 10;
//func(10);//报错,当函数重载碰到默认参数,会出现二义性
func(10,10);
return 0;
}