4. 封装

4.1.1 封装的意义

  1 #include<iostream>
2 #include<string>
3 using namespace std;
4
5 const double PI = 3.14;
6
7 //设计一个圆类,求圆的周长
8 //周长公式:2*PI*半径
9
10 class Circle {
11 //公共权限
12 public:
13 // 属性
14 int m_r;
15
16 //行为,一般为函数
17 double calZC() {
18 return 2 * PI * m_r;
19 }
20
21 };
22
23 //设计一个学生类,属性有学号和姓名;
24 //给学生姓名和学号赋值,并显示学号和姓名
25
26 class Student {
27 public:
28 string m_Name;
29 int m_ID;
30
31 void showStu() {
32 cout << "学生姓名:" << m_Name << " 学生学号:" << m_ID << endl;
33
34 }
35
36 void setName(string name) {
37 m_Name = name;
38 }
39
40 void setID(int id) {
41 m_ID = id;
42 }
43
44 };
45
46
47 class Person {
48 public: //公共权限,累内内类都可访问
49 string m_Name;
50
51 protected: //保护权限,类内可以访问,类外不能访问
52 string m_Car;
53
54 private: //私有权限,类内可以访问,类外不能访问
55 int password;
56
57 /// <summary>
58 ///在继承中, 保护权限子代可以访问,私有权限子代不可以访问;
59 /// </summary>
60 public:
61 void func() {
62 m_Name = "张三";
63 m_Car = "奔驰";
64 password = 123;
65 }
66 };
67
68
69 int main() {
70
71 //通过圆类创建具体的圆(对象)
72 //Circle cl;
73 //cl.m_r = 10;
74 //cout << "圆的周长为:" << cl.calZC() << endl;
75
76 //Student stu1;
77 ////stu1.m_Name = "小明";
78 //stu1.setName("小明");
79 ////stu1.m_ID = 123123;
80 //stu1.setID(123321);
81 //stu1.showStu();
82
83 //Student stu2;
84 //stu2.m_Name = "张三";
85 //stu2.m_ID = 2222;
86 //stu2.showStu();
87
88 Person p1;
89 p1.func();
90 cout << "Person 1的名字为:" << p1.m_Name << endl;
91
92
93
94 system("pause");
95
96 return 0;
97 }
98
99 //总结
100 //了解class的建立、三种权限;
101 //区分class与struct的区别:
102 //默认的访问权限不同,struct默认权限为公共权限,class默认为私有权限
103 //

4.1.2 成员属性私有化

 1 #include<iostream>
2 using namespace std;
3 #include<string>
4
5 //成员属性私有化
6 //1.自己控制读写权限
7 //2.检测数据的有效性
8
9 class Person {
10 public:
11 //写姓名
12 void setName(string name) {
13 m_Name = name;
14 }
15 //获取姓名
16 string getName() {
17 return m_Name;
18 }
19
20 //获取年龄
21 int getAge() {
22 //m_Age = 10;
23 return m_Age;
24 }
25
26 void setAge(int age) {
27 if (age < 0 || age > 150) {
28 m_Age = 0;
29 cout << "设置年龄有误!" << endl;
30 return;
31 }
32 m_Age = age;
33 }
34
35
36 private:
37 string m_Name; //可读可写
38 int m_Age; //可读
39
40 };
41
42
43 int main() {
44
45 Person p1;
46 p1.setName("张三");
47 cout << p1.getName() << endl;
48
49 p1.setAge(1000);
50 cout << p1.getAge() << endl;
51
52 p1.setAge(15);
53 cout << p1.getAge() << endl;
54
55 system("pause");
56
57 return 0;
58 }
59
60 //总结
61 //将成员属性私有化,可以自己控制读写的权限
62 //对于“写权限”,可以检测数据的有效性

4.2.1 构造函数与析构函数

 1 #include<iostream>
2 using namespace std;
3
4 //对象初始化和清理
5 class Person {
6 public:
7 //构造函数
8 Person() {
9 cout << "Person的构造函数!" << endl;
10 }
11
12 //析构函数
13 ~Person() {
14 cout << "Person的析构函数!" << endl;
15 }
16
17 };
18
19
20 void test01() {
21 Person p1; //局部变量,在栈区,test01执行完后释放这个对象;
22 }
23
24
25 int main() {
26
27 test01();
28
29 system("pause");
30
31 return 0;
32 }
33
34 //总结
35 //构造函数:
36 // 作用:初始化,成员属性赋值
37 // 语法: 类名(){}
38 // 没有返回值,也不写void
39 // 可以有参数,可以发生重载
40 // 自动调用,无需手动,只调一次
41 //
42 //析构函数:
43 // 作用:清零,对象销毁前,系统自动调用,执行清理工作
44 // 语法: ~类名(){}
45 // 不能有参数,不能发生函数重载
46 // 自动调用,无需手动,只调一次
47 //
48 //构造函数与析构函数是必须有的实现,如果我们不写,编译器自动提供空实现的构造和析构

4.2.2 构造函数的分类与调用

 1 #include<iostream>
2 using namespace std;
3
4 class Person {
5 public:
6 Person() {
7 cout << "Person的无参构造函数(默认构造函数)!" << endl;
8 }
9
10 Person(int a) {
11 age = a;
12 cout << "Person的有参构造函数!" << endl;
13 }
14
15 //拷贝构造函数(记住写法!)
16 Person(const Person &p) {
17 //将传入的所有属性,拷贝到当前;
18 age = p.age;
19 cout << "Person的拷贝构造函数!" << endl;
20 }
21
22
23 ~Person() {
24 cout << "Person的析构函数!" << endl;
25 }
26
27 int age;
28 };
29
30 //调用
31 void test01() {
32 //1.括号法
33
34 //Person p1; // Percon的默认构造法函数,不加()
35 //Person p2(10); // 有参构造
36 //Person p3(p2); // 拷贝构造
37
38 ////注意:
39 ////调用默认构造函数时,不要加()
40 //cout << "p2的年龄为:" << p2.age << endl;
41 //cout << "p3的年龄为:" << p3.age << endl;
42
43
44 //2.显示法
45 //Person p1;
46 //Person p2 = Person(10); //有参构造
47 //Person p3 = Person(p2); //拷贝构造
48
49 //Person(10); //匿名对象 特点:当前行执行结束后,系统会立即回收匿名对象;
50 ////注意:
51 ////不要利用拷贝构造函数来初始化匿名对象,编译器会认为这是个声明;
52 ////Person(p3); //报错
53
54 //3.隐式转换法
55
56 Person p4 = 10; //相当于 Person p4 = Person(10);
57 Person p5 = p4; // 拷贝构造
58
59
60 }
61
62
63 int main() {
64
65 test01();
66
67 system("pause");
68
69 return 0;
70 }
71
72 //总结
73 //分类:有参/无参;普通/拷贝
74 //调用:括号法/显示法/隐士转换法
75 //
76 //

4.2.3 拷贝构造函数的调用时机

 1 #include<iostream>
2 using namespace std;
3
4 class Person {
5 public:
6 Person() {
7 cout << "Person的无参构造函数!" << endl;
8 }
9
10 Person(int a) {
11 m_Age = a;
12 cout << "Person的有参构造函数!" << endl;
13 }
14
15 Person(const Person& p) {
16 m_Age = p.m_Age;
17 cout << "Person的拷贝构造函数!" << endl;
18 }
19
20 ~Person() {
21 cout << "Person的析构函数!" << endl;
22 }
23
24 int m_Age;
25
26 };
27
28 // 1.使用已经创建完的对象来初始化一个新对象
29 void test01() {
30 Person p1(20);
31 Person p2(p1);
32 cout << "p2的年龄为:" << p2.m_Age << endl;
33 }
34
35 //2.值传递的方式给函数参数传值
36 void doWork(Person p) {
37
38 }
39
40 void test02() {
41 Person p;
42 doWork(p);
43 }
44
45 //3.以值方式返回局部对象,返回的是一个新对象
46
47 Person doWork2() {
48 Person p1;
49 return p1;
50 }
51
52 void test03() {
53 Person p = doWork2();
54 }
55
56 int main() {
57
58 //test01();
59 //test02();
60 test03();
61
62 system("pause");
63
64 return 0;
65 }
66
67
68 //总结
69 // 1.使用已经创建完的对象来初始化一个新对象
70 // 2.值传递的方式给函数参数传值
71 // 3.以值方式返回局部对象
72 //

4.2.4 构造函数的调用规则

 1 #include<iostream>
2 using namespace std;
3
4 class Person {
5 public:
6 Person() {
7 cout << "Person的无参构造函数!" << endl;
8 }
9
10 Person(int a) {
11 m_Age = a;
12 cout << "Person的有参构造函数!" << endl;
13 }
14
15 Person(const Person& p) {
16 m_Age = p.m_Age;
17 cout << "Person的拷贝构造函数!" << endl;
18 }
19
20 ~Person() {
21 cout << "Person的析构函数!" << endl;
22 }
23
24 int m_Age;
25
26 };
27
28 void test01() {
29 Person p1;
30 p1.m_Age = 18;
31
32 Person p2(p1);
33 cout << "p2的年龄为:" << p2.m_Age << endl;
34
35
36 }
37
38 int main() {
39
40 test01();
41
42 system("pause");
43
44 return 0;
45 }
46
47 //总结
48 //C++默认给一个类添加三个函数:
49 //默认构造函数/默认析构函数/默认拷贝构造函数
50 //

4.2.5 深拷贝与浅拷贝

 1 #include<iostream>
2 using namespace std;
3
4 class Person {
5 public:
6 Person() {
7 cout << "Person的无参构造函数!" << endl;
8 }
9
10 Person(int age, int height) {
11 m_Age = age;
12 m_Height = new int(height); //创建堆区数据
13
14 cout << "Person的有参构造函数!" << endl;
15 }
16
17 //有参考构造函数,通过初始化列表实现
18 Person(int age, int num): m_Age(age), m_Num(num)
19 {
20 //m_Age = age;
21 //m_Height = new int(height); //创建堆区数据
22
23 cout << "Person的有参构造函数!" << endl;
24 }
25
26
27 Person(const Person& p) {
28 m_Age = p.m_Age;
29 //m_Height = p.m_Height; //编译器默认实现这行代码
30 //这行代码为浅拷贝,会导致堆区内存重复释放,程序报错
31
32 //利用深拷贝解决,重新开辟堆区空间
33 m_Height = new int(*p.m_Height);
34
35 cout << "Person的拷贝构造函数!" << endl;
36 }
37
38 ~Person() {
39 //析构代码,将堆区数据释放干净;
40 if (m_Height != NULL)
41 {
42 delete m_Height;
43 m_Height = NULL;
44 }
45
46 //浅拷贝带来的问题:堆区数据重复释放,
47 //使用深拷贝方法解决
48
49
50 cout << "Person的析构函数!" << endl;
51 }
52
53 int m_Age;
54 int m_Num;
55 int *m_Height;
56 };
57
58 void test01() {
59
60 Person p1(18, 160);
61 cout << "p1的年龄为:" << p1.m_Age << endl;
62 cout << "p1的身高为:" << *p1.m_Height << endl;
63
64 Person p2(p1);
65
66 cout << "p2的年龄为:" << p2.m_Age << endl;
67 cout << "p2的身高为:" << *p2.m_Height << endl;
68
69 }
70
71 int main() {
72
73 test01();
74
75 system("pause");
76
77 return 0;
78 }
79
80
81 //总结
82 //浅拷贝:简单的赋值拷贝操作
83 //深拷贝:在堆区重新申请空间,进行拷贝操作
84 //如果属性中有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题
85 //
86 // 其他结论
87 // 当其他类对象作为本类成员时。构造函数先构造对象,再构造自身,析构的顺序与构造相反
88 //

4.2.6 静态成员

#include<iostream>
using namespace std; class Person {
public: static int a; //静态成员变量,类内声明
int b; //非静态成员变量
static void func()
{
a = 100; //静态成员函数可以访问静态成员变量,共享的
cout << "静态成员函数" << endl;
cout << "静态成员变量a的值为:" << a << endl;
//b = 200; //报错,静态成员函数不能访问非静态成员变量;无法区分是哪个对象的b
} //静态成员函数也是有访问权限的
private:
static void func1() {
cout << "私有的静态成员函数" << endl;
} }; int Person::a = 10; //类外初始化 void test01() {
//1.通过对象访问
Person p;
p.func(); //2.通过类名访问
Person::func(); } int main() { test01(); system("pause"); return 0;
} // 总结
// 成员属性与成员函数加入static关键字,变为静态成员
//
// 静态成员:
// 1.所有对象共享同一份数据
// 2.在编译阶段分配内存
// 3.类内声明,类外初始化
//
// 静态成员函数:
// 1.所有对象共享同一个函数
// 2.静态成员函数只能访问静态成员变量(重)
//
//

4.3.1 成员变量和成员函数分开存储

 1 #include<iostream>
2 using namespace std;
3
4 //类内的成员变量和成员函数分开储存
5
6 class Person {
7 int m_A; //非静态成员变量,属于类的对象上
8 static int m_B; //静态成员变量,不属于类的对象上
9
10 void func() { //非静态成员函数,不属于类的对象上
11
12 }
13 static void func2() { //静态成员函数,不属于类的对象上
14
15 }
16
17 };
18
19 int Person::m_B = 0;
20
21
22 void test01() {
23 Person p;
24 //空对象占用内存空间为:1
25 //C++编译器为每个空对象分配一个字节空间,是为了区分空对象占用内存的位置
26 //每个空对象也应该有一个独一无二的内存地址
27 cout << "size of p = " << sizeof(p) << endl;
28 }
29
30 void test02() {
31 Person p;
32 cout << "size of p = " << sizeof(p) << endl;
33 }
34
35 int main() {
36
37 //test01();
38
39 test02();
40
41 system("pause");
42
43 return 0;
44 }
45
46
47 //总结
48 //在C++中,类内的成员变量和成员函数分开储存
49 //只有非静态成员变量才属于类的对象
50 //

4.3.2 this指针

 1 #include<iostream>
2 using namespace std;
3
4
5 class Person {
6 public:
7 Person(int age) {
8
9 //age = age; //名称冲突
10 //this指针指向的是被调用成员函数所属的对象
11 this->age = age; //通过this来解决
12
13 }
14
15 Person& PersonAddAge(Person& p) { //返回p2本体,需要用引用的方式返回
16 this->age += p.age;
17
18 //this指向p2的指针,而*this指向的就是p2这个对象的本体;
19 return *this;
20 }
21
22 int age;
23
24 };
25
26 void test01() {
27 Person p1(10);
28 cout << "p1的年龄为:" << p1.age << endl;
29
30 //链式编程思想
31 Person p2(20);
32 p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
33 cout << "p2的年龄为:" << p2.age << endl;
34
35 }
36
37 int main() {
38
39 test01();
40
41 system("pause");
42
43 return 0;
44 }
45
46 // 总结
47 // 已知:
48 // 成员函数与成员变量分开存储
49 // 每一个非静态成员函数只会诞生一份函数实例,也就是说多个同类型的对象会公用一块代码
50 // 那么问题是:这一块代码是如何区分哪个对象在调用自己呢?
51 //
52 // C++通过特殊的指针对象,this指针,解决上述问题
53 // this指针指向被调用的成员函数所属的对象
54 //
55 // this指针是隐含每一个非静态成员函数的一种指针
56 // this指针不需要定义,直接使用即可
57 //
58 // this指针的用途:
59 // 当形参和成员变量同名时,可以用this指针做区分
60 // 在类的非静态成员函数中返回对象本身,可以使用 return *this
61 //
62 //
63 // 其他结论
64 // C++中空指针也可以调用成员函数,但是需要注意有没有用到this指针
65 // 如果用到了this指针,需要加以判断,保证代码的健壮性
66 // if(this == NULL){
67 // return;
68 // }
69 //

4.3.3 const修饰成员函数

 1 #include<iostream>
2 using namespace std;
3
4
5 class Person {
6 public:
7 Person() {
8 cout << "Person的无参构造函数!" << endl;
9 }
10
11 Person(int a) {
12 cout << "Person的有参构造函数!" << endl;
13 }
14
15 void showPerson() const
16 {
17 //this指针的本质 是 指针常量 指针的指向不可以修改
18 // 在成员函数后加const,修饰的是this指向,让指针指向的值也不可以修改
19 //m_A = 100; //加入const后,变为常函数,常函数不能修改成员属性
20 //this->m_A = 100; //本质
21 this->m_B = 100; //加入mutable后可以修改
22
23 }
24
25 void func() {
26
27 }
28
29 int m_A;
30 mutable int m_B;
31
32 };
33
34 void test01() {
35 Person p1;
36 p1.showPerson();
37
38 const Person p2; //在对象前加入const,变为常对象
39 //p2.m_A = 100; //报错,指针的属性不可修改
40 p2.m_B = 100;
41
42 //常对象只能调用常函数
43
44 p2.showPerson(); //可以调用常函数
45 //p2.func(); //常对象 不可以调用普通成员函数,因为普通成员函数可以修改属性
46
47 }
48
49 int main() {
50
51 test01();
52
53 system("pause");
54
55 return 0;
56 }
57
58 // 总结
59 // const修饰成员函数
60 // 常函数:
61 // 成员函数后加const,成为常函数
62 // 常函数内不可以修改成员属性;
63 // 成员属性声明时加关键字mutable后,在常函数中就可以修改了
64 //
65 // 常对象:
66 // 声明对象前加入const称该对象为常对象
67 // 常对象只能调用常函数
68 //
69 //
70 //

4.4.1 友元

 1 #include<iostream>
2 using namespace std;
3
4 //建筑物类
5 class Building {
6 //goodGay 全局函数是building好朋友,可以访问building的私有成员
7 friend void goodGay(Building* building);
8
9 public:
10 Building() {
11 m_SittingRoom = "客厅";
12 m_BedRoom = "卧室";
13 }
14 public:
15 string m_SittingRoom; //客厅
16
17 private:
18 string m_BedRoom; //卧室
19 };
20
21 void goodGay(Building* building) {
22 cout << "好基友全局函数 正在访问:" << building->m_SittingRoom << endl;
23 cout << "好基友全局函数 正在访问:" << building->m_BedRoom << endl;
24 }
25
26
27
28 void test01() {
29 Building building;
30 goodGay(&building);
31 }
32
33
34
35 int main() {
36
37 test01();
38
39 system("pause");
40
41 return 0;
42 }
43
44
45 // 总结
46 // 友元,关键字friend
47 // 全局函数做友元
48 // 类做友元
49 // 成员函数做友元
50 //
51 //

参考《黑马程序员》C++教程

C++基础-4-封装(构造函数与析构函数,深拷贝与浅拷贝,静态成员,this,友元,const修饰成员函数)的更多相关文章

  1. C++学习基础十——子类构造函数与析构函数的执行

    1.子类构造函数的执行: 先执行父类的构造函数,再执行成员对象的构造函数,最后执行自身的构造函数. 当继承多个类时,构造函数的 执行顺序与继承时的顺序 相同,而与子类构造函数调用父类构造函数的顺序无关 ...

  2. JAVA_SE基础——35.static修饰成员函数

    在Java中适用static关键字修饰的方法称为静态方法. 声明静态方法的语法格式如下: 权限修饰符 static 数据类型 方法名(){ 方法体 } 静态方法 可以使用类名直接调用     类名.方 ...

  3. 12.C++-构造函数与析构函数调用顺序,const成员函数,const对象

    单个对象创建时,构造函数的调用顺序 1.首先判断该对象的类是否拥有父类,若有则先调用父类的构造函数 2.判断该对象的成员是否是其它类的成员,若是则调用成员变量的构造函数(调用顺序和声明顺序相同) 3. ...

  4. C++基础知识---static const初始化成员变量

    为了限制常数的范围class中.你必须要做出成为class成员:而要确保这是丝毫不亚于有一个恒定的实体.你必须要做出成为static员: Class Gameplayer { Private: Sta ...

  5. [原创] 基础中的基础(二):C/C++ 中 const 修饰符用法总结

    在这篇文章中,我总结了一些C/C++语言中的 const 修饰符的常见用法,供大家参考. const 的用法,也是技术性面试中常见的基础问题,希望能够帮大家梳理一下知识,给大家一点点帮助.作者是菜鸟一 ...

  6. C++基础学习9:构造函数和析构函数

    1.  构造函数用来对类对象进行初始化,它完成对内存空间的申请.赋初值等工作.  2.  析构函数主要是用来做清理工作的. 补充:函数名或变量名前面有"::"但是没有类名,说明这是 ...

  7. 从零开始学C++之构造函数与析构函数(三):深拷贝与浅拷贝、空类

    一.深拷贝与浅拷贝 说得简单点,假设一个类有指针成员,如果在拷贝的时候顺带连指针指向的内存也分配了,就称为深拷贝:如果只是分配指针本身的内存,那就是浅拷贝.浅拷贝造成的问题是有两个指针指向同块内存,d ...

  8. c#类—成员函数和封装及构造函数、析构函数、静态成员

    C# 类(Class) 当您定义一个类时,您定义了一个数据类型的蓝图.这实际上并没有定义任何的数据,但它定义了类的名称意味着什么,也就是说,类的对象由什么组成及在这个对象上可执行什么操作.对象是类的实 ...

  9. .NET 基础 一步步 一幕幕[面向对象之构造函数、析构函数]

    构造函数.析构函数 构造函数: 语法: //无参的构造函数 [访问修饰符] 函数名() :函数名必须与类名相同. //有参的构造函数 [访问修饰符] 函数名(参数列表):函数名必须与类名相同. 作用: ...

随机推荐

  1. Java 中的 ReadWriteLock 是什么?

    读写锁是用来提升并发程序性能的锁分离技术的成果.

  2. 解释 Spring 框架中 bean 的生命周期?

    Spring 容器 从 XML 文件中读取 bean 的定义,并实例化 bean. Spring 根据 bean 的定义填充所有的属性. 如果 bean 实现了 BeanNameAware 接口,Sp ...

  3. vsftd及虚拟用户

    临时需要搭建一个ftp,突然忘记怎么搞了,重新整一下,以后备用 vsftd及虚拟用户 1.安装vsftpd yum install vsftpd 2.添加用户(用于虚拟用户映射) adduser se ...

  4. CommonCollection1反序列化学系

    CommonsCollection1 1.前置知识 1.1.反射基础知识 1.1.1. 对象与类的基础知识 类(class),对象(object) 对象是类的实例化,中华田园犬(object)是狗(c ...

  5. Pandas数据统计函数

    Pandas数据统计函数 汇总类统计 唯一去重和按值计数 相关系数和协方差 0.读取csv数据 1.汇总类统计 2.唯一去重和按值计数 2.1 唯一性去重 一般不用于数值列,而是枚举.分类列 2.2 ...

  6. PN结

    摘自:https://blog.csdn.net/CPJ_phone/article/details/40979027                                          ...

  7. 浅谈Nodejs应用的主文件index.js的组成部分

    前言 Node妹子的问世,着实让我们前端攻城狮兴奋了一把,尤其本屌听说Javascript可以写服务端后,兴奋的像是看到了二次元萝莉的胖子...(●'◡'●).呃哼...YY先到这里,原谅本屌是个二次 ...

  8. CCF201403-2窗口

    问题描述 在某图形操作系统中,有 N 个窗口,每个窗口都是一个两边与坐标轴分别平行的矩形区域.窗口的边界上的点也属于该窗口.窗口之间有层次的区别,在多于一个窗口重叠的区域里,只会显示位于顶层的窗口里的 ...

  9. mysql 合并查询结果

     UNION 使用 UNION 关键字是,数据库系统会将所有的查询结果合并到一起,然后去除掉相同的记录:   UNION ALL 使用 UNION ALL,不会去除掉系统的记录:

  10. java集合总览

    在编写java程序中,我们最常用的除了八种基本数据类型,String对象外还有一个集合类,在我们的的程序中到处充斥着集合类的身影!java中集合大家族的成员实在是太丰富了,有常用的ArrayList. ...