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++教程
浙公网安备 33010602011771号