在学习c++基础总结了笔记,并分享出来。
04-c++day02
目录:
一、C++对C的扩展
1、练习
练习1、设计一个类,求圆的周长
练习2、设计一个学生类
2、内联函数
(1)内联函数的引出——宏函数的缺陷
(2)内联函数和编译器的处理
3、函数默认参数
4、函数重载
5、external “C”浅析
二、类和对象
1、C++语言的封装
2、建议将成员属性设置为私有
三、总结
一、C++对C的扩展
1、练习
练习1、设计一个类,求圆的周长
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 using namespace std;
4
5 const double pi = 3.14;
6
7 //1.设计一个类,求圆的周长
8 //周长公式 2 * pi * r
9 class Circle //class代表声明一个类,后面紧跟的是类的名称
10 {
11 public: //公共权限
12
13 //求圆的周长
14 //在类里面写函数——成员函数
15 double calculateZC()
16 {
17 return 2 * pi * m_R;
18 }
19
20 //设置半径的成员方法
21 //成员函数 可以修改成员属性
22 void setR(int r)
23 {
24 m_R = r;
25 }
26
27 //半径——成员属性
28 int m_R;
29
30 }
31 void test01()
32 {
33 //通过类 来创建一个圆
34 Circle c1;//圆(对象)
35 //c1.m_R = 10;//给这个对象来进行半径的赋值
36 //通过成员函数 间接给园设置半径
37 c1.setR(10);
38
39 //输出c1的周长
40 cout << "c1的周长:" << c1.calculateZC() << endl;
41 }
42
43 int main()
44 {
45 test01();
46
47 system("pause");
48 return EXIT_SUCCESS;
49 }
练习2、设计一个学生类:
属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号。
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 #include<string>
4 using namespace std;
5
6 /*
7 设计一个学生类:属性有姓名和学号,可以给姓名和学号赋值,可以显示学生的姓名和学号。
8 */
9
10 class Student
11 {
12 public://公共权限
13
14 //设置姓名
15 void setName(string name)
16 {
17 m_name = name;
18 }
19 //设置学号
20 void setId(int id)
21 {
22 m_Id = id;
23 }
24
25 //打印信息
26 void showInfo()
27 {
28 cout << "姓名为:" << m_Name << "学号为:" << m_Id << endl;
29 }
30
31 string m_Name;//姓名
32 int m_Id;//学号
33
34 }
35
36 void test01()
37 {
38 //创建一个学生 实例化——通过类来创建对象的过程
39 Student st;
40 st.setName("张三");
41 st.setId(1);
42
43 //通过st的属性打印了st信息
44 cout << "st的姓名为:" << st.m_Name << "st的学号为:" << st.m_Id << endl;//若<<处报错,记得包含string头文件
45
46 //通过成员函数 来打印st的信息
47 st.showInfo();
48 }
49
50 int main()
51 {
52 test01();
53
54 system("pause");
55 return EXIT_SUCCESS;
56 }
类和对象的关系???
类是对对象的抽象,对象是对类的实例。
2、内联函数
在c++中,预定义宏的概念是用内联函数来实现的,而内联函数本身也是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同之处在于内联函数会在适当的地方像预定义宏一样展开,所以不需要函数调用的开销。因此应该不使用宏,使用内联函数。
(1)内联函数的引出——宏函数的缺陷
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 using namespace std;
4
5 //1.定义一个加法
6 #define MyAdd(x,y) ((x)+(y))
7
8 void test01()
9 {
10 int ret = MyAdd(10, 20) * 20;//预期结果 600 但是10+20*20,加括号解决
11 cout << "ret = " << ret << endl;
12 }
13
14 //2.++a
15 #define MyCompare(a, b) ((a) < (b)) ? (a) : (b)
16
17 void test02()
18 {
19 int a = 10;
20 int b = 20;
21
22 int ret = MyCompare(++a, b);//预期结果11 ((++a) < (b)) ? (++a) : (b),但是12
23 cout << "ret = " << ret << endl;
24 }
25
26 //3.宏函数也没有作用域
27
28 int main()
29 {
30 test01();
31
32 system("pause");
33 return EXIT_SUCCESS;
34 }
(2)内联函数和编译器的处理
在普通函数(非成员函数)函数前面加上inline关键字使之成为内联函数。但是必须注意必须函数体和声明结合在一起,否则编译器将它作为普通函数来对待。
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 using namespace std;
4
5 //1.定义一个加法
6 #define MyAdd(x,y) ((x)+(y))
7
8 void test01()
9 {
10 int ret = MyAdd(10, 20) * 20;//预期结果 600 但是10+20*20,加括号解决
11 cout << "ret = " << ret << endl;
12 }
13
14 //2.++a
15 #define MyCompare(a, b) ((a) < (b)) ? (a) : (b)
16
17 inline void mycompare(int a, int b)
18 {
19 int ret = a < b ? a : b;
20 cout << "ret = " << ret << endl;
21 }
22
23 //1.内联函数注意事项
24 //2.类内部的成员函数 默认前面会加inline关键字
25 inline void func();//内联函数声明
26 //void func(){};//如果函数实现时候,没有加inline关键字,那么这个函数依然不算内联函数
27 inline void func(){};
28
29 void test02()
30 {
31 int a = 10;
32 int b = 20;
33
34 //int ret = MyCompare(++a, b);//预期结果11 ((++a) < (b)) ? (++a) : (b)
35 //cout << "ret = " << ret << endl;
36
37 mycompare(++a, b);
38 }
39
40 //3.宏函数也没有作用域
41
42 int main()
43 {
44 test01();
45
46 system("pause");
47 return EXIT_SUCCESS;
48 }
但是c++内联编译会有一些限制,以下情况编译器可能考虑不会将函数进行内联编译:
》不能存在任何形式的循环语句
》 不能存在过多的条件判断语句
》函数体不能过于庞大
》不能对函数进行取址操作
内联仅仅只是给编译器一个建议,编译器不一定会接受这种建议,如果你没有将函数声明为内联函数,那么编译器也可能将此函数做内联编译。一个好的编译器将会内联小的、简单的函数。
3、函数默认参数
C++——函数默认参数以及占位参数.cpp
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 using namespace std;
4
5 //函数的默认参数 参数后面 = ...
6 //函数参数注意事项,如果有一个位置有了默认参数,那么从这个位置开始,从左往后都必须有默认参数
7 //传入参数,如果有参数,就用传入的参数,没有参数就用默认值
8
9 //如果函数声明里面有了默认参数,那么函数实现时候必须没有
10 //函数声明和实现里,只能有一个里面有默认参数,不要同时都出现默认参数
11 void myFunc(int a = 10, int b = 10);
12 //void myFunc(int a = 10, int b = 10){}
13 void myFunc(int a, int b){}
14
15 void func(int a, int b = 10, int c = 1)
16 {
17 cout << "a + b + c = " << a + b + c << endl;
18 }
19
20 void test01()
21 {
22 func(1, 2);
23 }
24
25 //函数 占位参数
26 //如果有了占位参数,函数调用时候必须提供这个参数,但是用不到参数
27 //占位参数,没有什么大用途,只有后面重载++符号才有一点点用
28 //占位参数,可以有默认值
29 void func2(int a, int = 1)
30 {
31
32 }
33
34 void test02()
35 {
36 func2(10, 1);
37 }
38
39 int main()
40 {
41 test01();
42
43 system("pause");
44 return EXIT_SUCCESS;
45 }
C语言——函数默认参数以及占位参数.c
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<stdio.h>
3 #include<string.h>
4 #include<stdlib.h>
5
6 //C语言中没有默认参数,C也没有占位参数
7 //void func(int a = 1, int)
8 //{
9 //
10 //}
11
12 void test01()
13 {
14
15 }
16
17
18 int main(){
19
20 test01();
21
22 system("pause");
23 return EXIT_SUCCESS;
24 }
4、函数重载
同样一个字在不同的场景下具有不同的含义。那么在c++中也有一种类似的现象出现,同一个函数名在不同场景下可以具有不同的含义。
在传统c语言中,函数名必须是唯一的,程序中不允许出现同名的函数。在c++中是允许出现同名的函数,这种现象称为函数重载。
函数重载的目的就是为了方便的使用函数名。
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 using namespace std;
4
5 //函数重载
6 //C++中函数名称可以重复
7 //必须在同一个作用域,函数名称相同
8 //函数的参数 个数不同 或者 类型不同 或者 顺序不同
9 void func()
10 {
11 cout << "无参数的func" << endl;
12 }
13
14 void func(int a)
15 {
16 cout << "有参数的func(int a)" << endl;
17 }
18
19 void func(double a)
20 {
21 cout << "有参数的func(double a)" << endl;
22 }
23
24 void func(double a, int b)
25 {
26 cout << "有参数的func(double a, int b)" << endl;
27 }
28
29 void func(int a, double b)
30 {
31 cout << "有参数的func(int a, double b)" << endl;
32 }
33
34
35 class Person
36 {
37 void func();//这个不是函数重载,作用域不同
38 }
39
40 //返回值可以作为函数重载的条件吗???不可以
41 //int func(int a, double b)
42 //{
43 // cout << "有参数的func(int a, double b)" << endl;
44 //}
45
46 void test01()
47 {
48 //func();
49 //func(1);
50 func(1, 1.3);
51 }
52
53 //当函数重载 碰到了默认参数时候,要注意避免二义性问题
54 void func2(int a, int b = 10)
55 {
56
57 }
58 void func2(int a)
59 {
60
61 }
62
63 void test02()
64 {
65 //func2(10);
66 }
67
68 //引用的重载版本
69 void func3(int &a)//引用必须要合法的内存空间
70 {
71 cout << "int &a" << endl;
72 }
73 void func3(const int &a)//const也是可以作为重载的条件 int tmp = 10; const int &a = tmp;
74 {
75 cout << "const int &a" << endl;
76 }
77 void test03()
78 {
79 int a = 10;
80 func3(a);
81 func3(10);//在没有const会报错
82 }
83
84 int main()
85 {
86 test01();
87
88 system("pause");
89 return EXIT_SUCCESS;
90 }
思考:为什么函数返回值不作为重载条件呢?
当编译器能从上下文中确定唯一的函数的时,如int ret = func(),这个当然是没有问题的。然而,我们在编写程序过程中可以忽略他的返回值。那么这个时候,一个函数为
void func(int x);另一个为int func(int x); 当我们直接调用func(10),这个时候编译器就不确定调用那个函数。所以在c++中禁止使用返回值作为重载的条件。
函数重载实现原理
编译器为了实现函数重载,也是默认为我们做了一些幕后的工作,编译器用不同的参数类型来修饰不同的函数名,比如void func(); 编译器可能会将函数名修饰成_func,当编译器碰到void func(int x),编译器可能将函数名修饰为_func_int,当编译器碰到void func(int x,char c),编译器可能会将函数名修饰为_func_int_char我这里使用”可能”这个字眼是因为编译器如何修饰重载的函数名称并没有一个统一的标准,所以不同的编译器可能会产生不同的内部名。
5、external “C”浅析
解决了C++文件中调用C语言的代码!
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 #include"test.h"
4
5 using namespace std;
6
7
8 //C++中想调用C语言方法
9
10 //extern "C" void show();//show方法 按照C语言方式做链接
11 //解决的问题就是 在C++中调用C语言的函数
12
13 int main()
14 {
15 show();//在C++中,函数是可以发生重载的,编译器会把这个函数名称偷偷改变(如:)showv最后找不到show)
16
17 system("pause");
18 return EXIT_SUCCESS;
19 }
test.h
1 #pragma once
2
3 #ifdef __cplusplus//两个下划线
4 extern "C" {
5 #endif//!__cplusplus
6
7
8 void show();
9
10 #ifdef __cplusplus//两个下划线
11 }
12 #endif
test.c
1 #include"test.h"
2
3 void show()
4 {
5 printf("hello world\n");
6 }
二、类和对象
1、C++语言的封装
C语言——C语言下的封装.c
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<stdio.h>
3 #include<string.h>
4 #include<stdlib.h>
5
6 struct Person
7 {
8 char mName[64];
9
10 int mAge;
11 };
12
13 void PersonEat(struct Person* p)
14 {
15 printf("%s 在吃人饭\n", p->mName);
16 }
17
18 void test01()
19 {
20 struct Person p1;
21 strcpy(p1.mName, "德玛西亚");
22
23 PersonEat(&p1);
24 }
25
26 struct Dog
27 {
28 char mName[64];
29
30 int mAge;
31 };
32
33 void DogEat(struct Dog* dog)
34 {
35 printf("%s 在吃狗粮\n", dog->mName);
36 }
37
38 void test02()
39 {
40 struct Dog d;
41 strcpy(d.mName, "旺财");
42
43 DogEat(&d);
44
45 struct Person p1;
46 strcpy(p1.mName, "老王");
47
48 DogEat(&p1);
49 }
50 //C语言封装 属性和行为分开处理了,类型检测不够
51
52
53 int main(){
54
55 test01();
56
57 system("pause");
58 return EXIT_SUCCESS;
59 }
C++——C++下的封装.cpp
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 using namespace std;
4
5 struct Person
6 {
7 char mName[64];
8
9 int mAge;
10
11 void PersonEat()
12 {
13 cout << mName << "吃人饭" << endl;
14 }
15 };
16
17 struct Dog
18 {
19 char mName[64];
20
21 int mAge;
22
23 void DogEat()
24 {
25 cout << mName << "吃狗粮" << endl;
26 }
27 };
28
29 //C++中的封装 严格类型转换检测,让属性和行为 绑定到一起
30 //1.属性和行为作为一个整体来表示生活中的事物
31 //2.控制权限 public 公有权限 protected 保护权限 private 私有权限
32 void test01()
33 {
34 Person p1;
35 strcpy(p1.mName, "老王");
36 p1.PersonEat();
37 //p1.DogEat();
38 }
39
40 //C++中struct和class是一个意思,唯一的不同 默认的权限,struct是public,但是class默认权限是private
41 class Animal
42 {
43 private:
44 //如果我不声明权限,默认的权限是private
45 void eat();
46 int mAge;
47
48 public:
49 int mHeight;
50
51 protected://保护权限,类内部可以访问,(当前类的子类可以访问),类外部不可以访问
52
53 int mWeight;
54
55 void setWeight()
56 {
57 mWeight = 100;
58 }
59 };
60
61 //所谓私有权限,就是私有成员(属性、函数),在类内部可以访问,类外部不可以访问
62 //公有权限,在类内部和类外部都可以访问
63 void test02()
64 {
65 Animal an;
66 //an.eat();
67 //an.mAge;//私有不可以访问到
68
69 an.mHeight = 100;//公有权限在类外部可以访问到
70
71 //an.mWeight = 100;//保护权限,在类外不可访问到
72 }
73
74 //public类内 类外 都可以访问
75 //protected类内可以访问,类外不可以访问(子类可以访问)
76 //private类内可以访问,类外不可以访问(子类不可以访问)
77
78 int main()
79 {
80 test01();
81
82 system("pause");
83 return EXIT_SUCCESS;
84 }
2、建议将成员属性设置为私有
自己提供公共的对外接口来进行set或者get方法访问。
1 #define _CRT_SECURE_NO_WARNINGS
2 #include<iostream>
3 #include<string>
4 using namespace std;
5
6 class Person
7 {
8 public:
9 //设置年龄
10 void setAge(int age)
11 {
12 if(age < 0 || age > 100)
13 {
14 cout << "你这个老妖精" << endl;
15 return;
16 }
17 m_Age = age;
18 }
19 //获取年龄,读权限
20 int getAge()
21 {
22 return m_Age;
23 }
24
25 //读姓名
26 string getName()
27 {
28 return m_Name;
29 }
30 //写姓名
31 void setName(string name)
32 {
33 m_Name = age;
34 }
35
36 //只写的情人
37 void setLover(string lover)
38 {
39 m_lover = lover;
40 }
41
42 private:
43 int m_Age = 0;//年龄 读写
44 string m_Name;//公有权限
45 string m_lover;//情人
46 }
47
48
49 void test01()
50 {
51 Person p1;
52 p1.setName("老王");
53
54 cout << "p1的姓名:" << p1.getName() << endl;
55
56 //年龄
57 p1.setAge(120);
58
59 cout << "p1的年龄:" << p1.getName() << endl;
60
61 //情人 只能设置,外部我不告诉你
62 p1.setLover("仓井");
63 }
64
65 int main()
66 {
67 test01();
68
69 system("pause");
70 return EXIT_SUCCESS;
71 }
三、总结
1 设计类 抽象类
1.1 class 类名{
1.2 public 公共权限
1.3 设置 成员属性
1.4 设置 成员函数
1.5 }
1.6 使用类 创建对象 实例化对象
1.7 类名 对象名
1.8 通过对象 来设置属性 调用成员函数
1.9 类和对象 关系???
1.9.1 类是对对象的抽象
1.9.2 对象是对类的实例
2 内联函数 解决宏缺陷问题
2.1 给编译器一个建议,加上关键字,编译器不一定按照内联处理
2.2 不加关键字,也许编译器还偷摸的给你加inline
2.3 成员函数 默认加上关键字
2.4 函数声明加了关键字,函数实现也要加inline关键字
3 函数默认参数
3.1 参数可以有默认值
3.2 如果有一个位置有了默认值,那么从这个位置开始,从左往右都必须有默认值
3.3 函数声明和实现 只能有一个有默认值
4 函数占位参数
4.1 void func(int) 占位参数 调用时候必须要提供这个参数
4.2 占位参数也可以有默认值
4.3 c语言中没有默认参数 和占位参数
5 函数重载的基本语法
5.1 函数名称相同 又在同一个作用域下
5.2 函数参数个数不同、类型不同、顺序不同都可以满足重载条件
5.3 函数的返回值可以作为函数重载条件吗? 不可以
5.4 当函数重载碰到了函数默认参数 要注意避免二义性
6 extern C浅析
6.1 解决了C++文件中调用C语言的代码
6.2 ifdef __cplusplus extern “C” {
6.3 }
7 C++语言的封装
7.1 将属性和行为作为一个整体,来表示生活中具体的事物
7.2 有访问权限
7.3 class 和struct唯一区别 默认权限不同
7.3.1 class默认是private
7.3.2 struct 默认是public
7.4 public 是类内类外都可以访问到
7.5 protected 类内可以,类外不可以
7.6 private 类内可以,类外不可以
8 建议将所有成员属性设置为私有
8.1 自己提供公共的对外接口来进行 set或者get方法访问
在学习c++基础总结了笔记,并分享出来。