封装是C++面向对象三大特性之一
封装的意义(1):
将属性和行为作为一个整体,表现生活中的事物将属性和行为加以权限控制封装的意义(2):
类在设计时,可以把属性和行为放在不同的权限下,加以控制。 在c++中,共有三种权限: 公共权限 public : 类内可以访问 类外可以访问 保护权限 protected: 类内可以访问 类外不可以访问 私有权限 private: 类内可以访问 类外不可以访问
优点1:将所有成员属性设置为私有,可以自己控制读写权限
优点2:对于写权限,我们可以检测数据的有效性
对象的初始化和清理是两个非常重要的安全问题。一个对象或者变量没有初始状态,对其使用后果是未知。同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题
c++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。
对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供
编译器提供的构造函数和析构函数是空实现。
构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作。构造函数语法:类名(){}
构造函数,没有返回值也不写void函数名称与类名相同构造函数可以有参数,因此可以发生重载程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次析构函数语法: ~类名(){}
析构函数,没有返回值也不写void函数名称与类名相同,在名称前加上符号 ~析构函数不可以有参数,因此不可以发生重载程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次两种分类方式:
按参数分为: 有参构造和无参构造
按类型分为: 普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
利用有参构造函数,括号法初始化对象属性:
#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这个对象进行初始化。
默认情况下,c++编译器至少给一个类添加3个函数
默认构造函数(无参,函数体为空)
默认析构函数(无参,函数体为空)
默认拷贝构造函数,对属性进行值拷贝(也叫浅拷贝)
构造函数调用规则如下:
如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
如果用户定义拷贝构造函数,c++不会再提供其他构造函数
浅拷贝:简单的赋值拷贝操作,包括默认拷贝构造函数。
深拷贝:在堆区重新申请空间,进行拷贝操作
#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; }