C++ 学习笔记(3) 封装

    科技2022-08-28  111

    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; }
    Processed: 0.014, SQL: 9