C++ 学习笔记(3) 封装
封装是C++面向对象三大特性之一
1. 封装的意义与权限
封装的意义(1):
- 将属性和行为作为一个整体,表现生活中的事物
- 将属性和行为加以权限控制
封装的意义(2):
类在设计时,可以把属性和行为放在不同的权限下,加以控制。
在c++中,共有三种权限:
公共权限 public : 类内可以访问 类外可以访问
保护权限 protected: 类内可以访问 类外不可以访问
私有权限 private: 类内可以访问 类外不可以访问
2.成员属性设置为私有
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
3.构造函数和析构函数
对象的初始化和清理是两个非常重要的安全问题。一个对象或者变量没有初始状态,对其使用后果是未知。同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
c++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供
编译器提供的构造函数和析构函数是空实现。
- 构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。
- 析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。
构造函数语法:类名(){}
- 构造函数,没有返回值也不写void
- 函数名称与类名相同
- 构造函数可以有参数,因此可以发生重载
- 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次
析构函数语法: ~类名(){}
- 析构函数,没有返回值也不写void
- 函数名称与类名相同,在名称前加上符号 ~
- 析构函数不可以有参数,因此不可以发生重载
- 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次
3.1 构造函数的分类及调用
两种分类方式:
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
利用有参构造函数,括号法初始化对象属性:
#include<iostream>
#include<string>
using namespace std;
class student {
public:
student() {
cout << "无参即默认构造函数" << endl;
}
student(int Age)
{
age = Age;
cout << "有参构造函数" << endl;
}
student(const student & tmp)
{
age = tmp.age;
cout << "自己建立的拷贝函数,即深拷贝" << endl;
}
~student()
{
cout << "默认析构函数" << endl;
}
public:
int age;
};
void test1()
{
student laowang(20);
cout << "利用有参构造函数,括号法初始化对象属性: " << laowang.age<<endl;
}
int main()
{
test1();
}
利用有参构造函数,显示法初始化对象属性:
#include<iostream>
#include<string>
using namespace std;
class student {
public:
student() {
cout << "无参即默认构造函数" << endl;
}
student(int Age)
{
age = Age;
cout << "有参构造函数" << endl;
}
student(const student & tmp)
{
age = tmp.age;
cout << "自己建立的拷贝函数,即深拷贝" << endl;
}
~student()
{
cout << "默认析构函数" << endl;
}
public:
int age;
};
void test1()
{
student laowang=student(30);
cout << "利用有参构造函数,显示法初始化对象属性: " << laowang.age<<endl;
}
int main()
{
test1();
}
利用有参构造函数,隐式转换法初始化对象属性:
#include<iostream>
#include<string>
using namespace std;
class student {
public:
student() {
cout << "无参即默认构造函数" << endl;
}
student(int Age)
{
age = Age;
cout << "有参构造函数" << endl;
}
student(const student & tmp)
{
age = tmp.age;
cout << "自己建立的拷贝函数,即深拷贝" << endl;
}
~student()
{
cout << "默认析构函数" << endl;
}
public:
int age;
};
void test1()
{
student laowang=30;
cout << "利用有参构造函数,隐式转换法初始化对象属性: " << laowang.age<<endl;
}
int main()
{
test1();
}
利用拷贝构造函数,显示法初始化对象属性:
#include<iostream>
#include<string>
using namespace std;
class student {
public:
student() {
cout << "无参即默认构造函数" << endl;
}
student(int Age)
{
age = Age;
cout << "有参构造函数" << endl;
}
student(const student & tmp)
{
age = tmp.age;
cout << "自己建立的拷贝函数,即深拷贝" << endl;
}
~student()
{
cout << "默认析构函数" << endl;
}
public:
int age;
};
void test1()
{
student xiaowang(10);
student laowang(xiaowang);
cout << "利用拷贝构造函数,显示法初始化对象属性: " << laowang.age<<endl;
}
int main()
{
test1();
}
显示结果为:
其中,创建了两个对象,首先调用了一次有参构造函数,进行初始化,再利用拷贝函数对laowang这个对象进行初始化。
3.2 构造函数调用规则
默认情况下,c++编译器至少给一个类添加3个函数
-
默认构造函数(无参,函数体为空)
-
默认析构函数(无参,函数体为空)
-
默认拷贝构造函数,对属性进行值拷贝(也叫浅拷贝)
构造函数调用规则如下:
-
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
-
如果用户定义拷贝构造函数,c++不会再提供其他构造函数
3.2 深拷贝与浅拷贝
浅拷贝:简单的赋值拷贝操作,包括默认拷贝构造函数。
深拷贝:在堆区重新申请空间,进行拷贝操作
#include<iostream>
#include<string>
using namespace std;
class student {
public:
student(int Age,int Height)
{
age = Age;
height = new int(Height);
cout << "有参构造函数" << endl;
}
student(const student & tmp)
{
age = tmp.age;
height = tmp.height;
//height = new int(*tmp.height);
cout << "自己实现拷贝构造函数,实现浅拷贝" << endl;
}
~student()
{
if (height != NULL)
{
delete height;
height = NULL;
}
cout << "析构函数释放堆中的指针数据" << endl;
}
//student()
public:
int age;
int* height;
};
int main()
{
student a(18,180);
student b(a);
cout << "b.age" << b.age << endl;
}
}
上述代码没有语法错误,但是在执行时会出现异常错误。这是因为由于浅拷贝在设计指针时产生了堆区内存重复释放的问题,如下图所示:
解决办法:利用深拷贝,在堆中利用new再开辟一个内存区域,如下图:
代码如下:
#include<iostream>
#include<string>
using namespace std;
class student {
public:
student(int Age,int Height)
{
age = Age;
height = new int(Height);
cout << "有参构造函数" << endl;
}
student(const student & tmp)
{
age = tmp.age;
//height = tmp.height;
height = new int(*tmp.height);
cout << "自己实现拷贝构造函数,实现深拷贝" << endl;
}
~student()
{
if (height != NULL)
{
delete height;
height = NULL;
}
cout << "析构函数释放堆中的指针数据" << endl;
}
//student()
public:
int age;
int* height;
};
int main()
{
student a(18,180);
student b(a);
cout << "b.age:" << b.age << endl;
}