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

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++教程

posted @ 2022-05-01 21:44  Thomas_kaka  阅读(100)  评论(0)    收藏  举报