新学期开始了,c++课程逐渐深入,今天来了解一下c++的类和对象中的封装与数据的初始化和清理!
PS.本博客参考b站up黑马程序员的相关课程,老师讲得非常非常好!
封装
封装是C++面向对象三大特性之一
1.封装的意义一:
- 将属性和行为作为一个整体,表现生活中的事物
- 将属性和行为加以权限控制
语法:
class 类名{访问权限: 属性/行为}
封装应用一:
在设计类的时候,属性和行为写在一起,表现事物
示例1:没计一个圆类,求圆的周长
#include<iostream>
using namespace std;
const double PI=3.14;//class代表设计一个类
class circle
{//访问权限 public://公共权限//属性(变量) //半径int m_r; //行为 //获取圆的周长double calculateZC() {return 2*PI*m_r;}
};int main()
{//通过圆类创建具体的圆(对象)//实例化(通过一个类创建一个对象) circle c1;//给圆对象的属性进行具体赋值c1.m_r=10;cout<<"圆的周长为:"<<c1.calculateZC()<<endl;return 0;
}
封装应用二:
创建一个学生类,属性有姓名和学号,可以给姓名和学号赋值并显示。
#include<iostream>
#include<string>
using namespace std;class student
{
public:
string m_Name;
int m_Id;
//行为 :显示姓名和学号
void showstudent()
{cout<<"姓名:"<<m_Name<<"学号:"<<m_Id;}
void setname(string name)
{m_Name=name;}
void setId(int Id);
{Id=m_Id;
}
};int main()
{student s1;//s1.m_Name="zhang";s1.setname("zhang"); //s1.m_Id=1;s1.setId(1)s1.showstudent();return 0;
}
专业术语补充:
类中的属性和行为,我们统一称为成员
//属性:成员属性,成员变量
//行为:成员函数,成员方法
2.封装意义二:
类在设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限分三种:
- public 公共权限 类内外都可以访问
- protected 保护权限 类内可以访问,类外不可以访问
- private 私有权限 类内可以访问,类外不可以访问
23的区别体现在继承
父的保护权限子也可以访问,父的私有权限子不可以访问
如图,类外只有public能访问到:
而类内是都可以访问的:
3.struct和class的区别
struct | 默认权限为公共 |
class | 默认权限为私有 |
4.成员属性设置为私有
优点1:自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
企业开发中常用
下面的代码展示了如何设置不同的读写权限
#include<iostream>
#include<string>
using namespace std;class Person
{
public:
void setName(string name)
{m_Name=name;
}string getName()
{return m_Name;
}void setIdol(string idol)
{m_idol=idol;
}private:string m_Name;//姓名 可读可写int m_age;//年龄 只读string m_idol;//偶像 只写
};int main()
{Person p;p.setName("张三");//获取name cout<<"姓名:"<<p.getName()<<endl; return 0;
}
对象的初始化和清理
生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全
C++中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置。
数调用时机
1.构造函数和析构函数
对象的初始化和清理也是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其使用后果是未知
同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
c++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。
- 构造函数;主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法
类名( ){ }
1.构造函数,没有返回值也不写void
2.函数名称与类名相同
3.构造函数可以有参数,因此可以发生重载
4.程序在调用对象时候会白动调用构造,无须手动调用,而且只会调用1次
析构函数语法
~类名() { }
1.析构函数,没有返回值也不写void
2.函数名称与类名相同,在名称前加上符号~
3.析构函数不可以没有参数,因此不可以发生重载
4.程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次
#include<iostream>
using namespace std;class Person
{public://1.构造函数初始化Person(){cout<<"构造函数Person的调用"<<endl; }~Person(){cout<<"析构函数~Person的调用"<<endl; }};void test01()
{Person p;
}
int main()
{test01();return 0;}
构造函数和析构函数自动调用
构造函数的分类和调用
两种分类方式:
按参数分:有参和无参构造
按类型分:普通构造和拷贝构造
三种调用方式
括号法
显示法
隐式转换法
括号法
#include<iostream>
using namespace std;
//构造函数的分类及调用
class Person
{public://1.构造函数初始化//无参构造:int age; 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; }};
//调用
void test01()
{//括号法Person p1;//默认构造函数的调用Person p2(10);//有参构造函数Person p3(p2);//拷贝构造函数cout<<"p2的年龄:"<<p2.age<<endl;cout<<"p3的年龄:"<<p3.age<<endl;}int main()
{test01();return 0;}
注意事项:调用默认构造函数时,不要加()
写括号后不创建对象,因为这行代码,编译器会认为是一个函数的声明
如Person p1(); 就是不正确的
显示法
#include<iostream>
using namespace std;
//构造函数的分类及调用
class Person
{public://1.构造函数初始化//无参构造:int age; 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; }};
//调用
void test01()
{//显示法 Person p1;Person p2=Person(10);//有参构造 Person p3=Person(p2);//拷贝构造 //注意事项2:不要利用拷贝构造函数来初始化匿名对象 //Person(10);//匿名对象:当前行执行结束后,系统立即回收}int main()
{test01();return 0;}
隐式转换法
#include<iostream>
using namespace std;
//构造函数的分类及调用
class Person
{public://1.构造函数初始化//无参构造:int age; 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; }};
//调用
void test01()
{//显示法 Person p1;Person p2=Person(10);//有参构造 Person p3=Person(p2);//拷贝构造 //注意事项2:不要利用拷贝构造函数来初始化匿名对象 //Person(10);//匿名对象:当前行执行结束后,系统立即回收//隐式转换法Person p4=10; //Person p4=Person(10);//有参构造 }int main()
{test01();return 0;}
注意事项2:不要利用拷贝构造函数来初始化匿名对象 ,编译器会以为是对象声明
拷贝构造函数使用时机
C++中拷贝构造函数调用时机通常有三种情况:
1.使用一个已经制建完毕的对象来初始化一个新对象
2.值传递的方式给函数参数传值
3.以值方式返回局部对象
构造函数调用规则
默认情况下,C++编译器至少给一个类添加3个函数
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对属性进行值拷贝
构造函数调用规则如下:
- 如果用户定义有参构造函数,C++不在提供默认无参构造,但是会提供默认拷贝构造
- 如果用户定义拷贝构造函数,c++不会再提供其他构造函数