C++面向对象的三大特性为:封装、继承、多态
C++认为万事万物都皆为对象,对象上有其属性和行为
4.1 封装
4.1.1封装的意义
封装的意义:
- 将属性和行为作为一个整体,表现生活中的事物
- 将属性和行为加以权限控制
定义类,类的实例
在设计类的时候,属性和行为写在一起,表现事物<br />**语法:**class 类名{ 访问权限: 属性 / 行为 };
示例1:设计一个圆类,求圆的周长
//圆周率
const double PI = 3.14;
//1、封装的意义
//将属性和行为作为一个整体,用来表现生活中的事物
//封装一个圆类,求圆的周长
//class代表设计一个类,后面跟着的是类名
class Circle
{
public: //访问权限 公共的权限
//属性
int m_r;//半径
//行为
//获取到圆的周长
double calculateZC()
{
//2 * pi * r
//获取圆的周长
return 2 * PI * m_r;
}
};
int main() {
//通过圆类,创建圆的对象,实例化
// c1就是一个具体的圆
Circle c1;
//通过.语法可以访问到对象里的方法和值;
c1.m_r = 10; //给圆对象的半径 进行赋值操作
//2 * pi * 10 = = 62.8
cout << "圆的周长为: " << c1.calculateZC() << endl;
system("pause");
return 0;
}
设计类的权限
类在设计时,可以把属性和行为放在不同的权限下,加以控制
访问权限有三种:
- public 公共权限 类内可以访问 类外可以访问
- protected 保护权限 类内可以访问 类外不可以访问,但是子类可以访问;
- private 私有权限 类内可以访问 类外不可以访问 ```cpp //三种权限 //公共权限 public 类内可以访问 类外可以访问 //保护权限 protected 类内可以访问 类外不可以访问 //私有权限 private 类内可以访问 类外不可以访问
class Person { //姓名 公共权限 public: string m_Name;
//汽车 保护权限
protected: string m_Car;
//银行卡密码 私有权限
private: int m_Password;
public: void func() { m_Name = “张三”; m_Car = “拖拉机”; m_Password = 123456; } };
int main() {
Person p;
p.m_Name = "李四";
//p.m_Car = "奔驰"; //保护权限类外访问不到
//p.m_Password = 123; //私有权限类外访问不到
system("pause");
return 0;
}
<a name="JCOLd"></a>
### 4.1.2 struct 结构体和class区别
在C++中 struct和class唯一的**区别**就在于 **默认的访问权限不同**<br />区别:
- **struct 默认权限为公共**
- **class 默认权限为私有**
```cpp
//定义类
class C1
{
int m_A; //默认是私有权限
};
//定义结构体
struct C2
{
int m_A; //默认是公共权限
};
int main() {
//类的实例
C1 c1;
c1.m_A = 10; //错误,访问权限是私有
//结构体的实例
C2 c2;
c2.m_A = 10; //正确,访问权限是公共
system("pause");
return 0;
}
4.13 成员属性设置为私有
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
定义类的时候,
在公共权限中设置,函数来获取或者设置属性;
私有权限中创建,属性;
通过类中的函数来实现,属性是否可读,可写;
class Person {
public:
//姓名设置可读可写
void setName(string name) {
m_Name = name;
}
string getName()
{
return m_Name;
}
//获取年龄
int getAge() {
return m_Age;
}
//设置年龄
void setAge(int age) {
if (age < 0 || age > 150) {
cout << "你个老妖精!" << endl;
return;
}
m_Age = age;
}
//情人设置为只写
void setLover(string lover) {
m_Lover = lover;
}
private:
string m_Name; //可读可写 姓名
int m_Age; //只读 年龄
string m_Lover; //只写 情人
};
int main() {
Person p;
//姓名设置
p.setName("张三");
cout << "姓名: " << p.getName() << endl;
//年龄设置
p.setAge(50);
cout << "年龄: " << p.getAge() << endl;
//情人设置
p.setLover("苍井");
//cout << "情人: " << p.m_Lover << endl; //只写属性,不可以读取
system("pause");
return 0;
}
4.2 对象的初始化和清理
- 生活中我们买的电子产品都基本会有出厂设置,在某一天我们不用时候也会删除一些自己信息数据保证安全
- C++中的面向对象来源于生活,每个对象也都会有初始设置以及 对象销毁前的清理数据的设置。
4.2.1 构造函数 和 析构函数
对象的初始化和清理也是两个非常重要的安全问题
一个对象或者变量没有初始状态,对其使用后果是未知
同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
c++利用了构造函数和析构函数解决上述问题,
这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供
编译器提供的构造函数和析构函数是空实现(函数里都是空的什么都没写)。
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数(初始化)
构造函数语法:类名(){}
- 构造函数,没有返回值也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
程序在调用(创建)对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数(清理)
析构函数语法:~类名(){}
析构函数,没有返回值也不写void
- 函数名称与类名相同,在名称前加上符号 ~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
class Person
{
public:
//构造函数
//如果不写构造和析构,系统会自动写一个空的;
Person()
{
cout << "Person的构造函数被调用" << endl;
}
//析构函数
~Person()
{
cout << "Person的析构函数被调用" << endl;
}
};
void test01()
{
Person p;
}
int main() {
//调用函数test01,来创建对象P
//这时候会调用构造和析构,因为函数里创建的P是一个局部变量,函数调用完就释放
test01();
//这里虽然也是在函数中创建对象P,但是只调用了构造函数,没有调用析构函数;
//因为下面的 system("pause"); 把main函数停止了,main并没有实行完毕
//如果在dos中,按下了回车,就会调用析构函数;
Person p;
system("pause");
return 0;
}
4.2.2 构造函数的分类及调用
两种分类方式:
按参数分为: 有参和无参构造
Person(){}
Person(int a){}
按类型分为: 普通构造和拷贝构造
Person(const Person& p){}
三种调用方式:
括号法
有参构造调用
Person p1 (10);
拷贝构造调用
Person p2 (p1);
**显示法**<br />有参构造调用<br />**Person p3 = Person(10); **<br />拷贝构造调用<br />**Person p4 = Person(p1); **
**隐式转换法**<br />有参构造调用<br /> // Person p4 = Person(10); <br /> Person p6 = 10; <br />拷贝构造调用<br /> // Person p5 = Person(p4); <br /> Person p7 = p4;
注意事项
调用无参构造函数,不要使用(),;编译器会认为这是一个函数声明;
//不能使用()
//编译器认为是
错误 Person p2();
//调用无参构造
Person p2;
不要调用拷贝构造函数,初始化匿名对象;
Person p1;
//调用拷贝构造,创建匿名对象
//编辑器会认为是 Person(p1) === Person p1
错误 Person (p1);
class Person {
public:
//无参构造函数
Person() {
cout << "无参构造函数被调用!" << endl;
}
//有参构造函数
Person(int a) {
age = a;
cout << "有参构造函数被调用!" << endl;
}
//拷贝构造函数
Person(const Person& p) {
age = p.age;
cout << "拷贝构造函数被调用!" << endl;
}
//析构函数
~Person() {
cout << "析构函数被调用!" << endl;
}
public:
int age;
};
//创建对象p函数
void test01() {
//创建对象P
//这是一个局部变量
//没有传参,调用了无参构造函数;
Person p;
}
void test02() {
//括号法,也就是传参;
//创建P1对象,传参10,调用有参构造函数
Person p1(10);
//创建对象p2,传参P1对象,调用了拷贝构造函数
Person p2(p1);
//注意1:调用无参构造函数不能加括号,如果加了编译器认为这是一个函数声明
错误--Person p2();
// 显式法
//调用了有参构造,传参10
Person p3 = Person(10);
//调用了拷贝构造
Person p4 = Person(p2);
//创建了一个匿名对象,当前行结束之后,马上析构
Person(10)
//注意2:不能利用 拷贝构造函数 初始化匿名对象 编译器认为是对象声明
//编译器认为Person(p4) === Person p4
错误--Person(p4);
//隐式转换法
// Person p4 = Person(10);
Person p6 = 10;
// Person p5 = Person(p4);
Person p7 = p4;
}
int main() {
test01();
test02();
system("pause");
return 0;
}
4.2.3 拷贝构造函数调用时机
C++中拷贝构造函数调用时机通常有三种情况
- 使用一个已经创建完毕的对象来初始化一个新对象
- 值传递的方式给函数参数传值
- 以值方式返回局部对象