c++—封装:构造函数、析构函数、成员操作

news2024/12/28 4:37:30

1. 封装的主要目的是解决代码的维护性问题,经过封装的函数代码独立性高;

2. 封装的演变历史,以栈为例子介绍:

        ①成员(top、data[ ])都在main函数里,动作方法(push、pop)等也在main函数里(尼泊尔电线);

        ②成员(top、data[ ])在结构体体struct里面,动作方法(push、pop)等还在main函数里面(相当于未完成装修的房间,虽然电线整体布置,但是裸露的);

        ③成员(top、data[ ])和动作方法(push、pop)都在结构体(类)里面,动作方法(push、pop)对外只提供接口,达到既知其接口,又可以访问其数据;(C语言重在函数,c++重在类)

3. 构造函数的作用及特点

        ①无函数返回值,存在默认和自定义等多种情况(引导面试官);

        ②函数名与类名相同;

        ③自动调用:在通过类实例化对象时,构造函数会自动调用;

        ④可以重载;

4. 构造函数的种类及优缺点

(1)默认无参构造函数 VS 自定义无参构造函数

        当类中无任何其他构造函数时,系统会默认生成;

        若有其他构造函数,则该默认无参构造函数不会生成;其中自定义无参构造函数,可以在函数体里面初始化自定义初始化成员变量的值;注意若自定义的有参构造函数只有一个形参时,即只对一个成员变量进行初始化,即形成了类型转换构造函数,容易产生歧义,慎用!

        下例中同样含有int类型(或char*)等类型与类类型(Student)之间转换的方法;

#include <iostream>
#include <string.h>

using namespace std;

class Student
{
public:
    //Student() = delete;  //使用delete可以禁止系统自动生成默认构造函数;
    Student() = default; //使用default可以显式的让系统自动生成默认构造函数;
    Student(int num, char *name, int age)
    {
        m_num = num;
        m_age = age;
        int len = strlen(name)+1;
        m_name = new char[len];
        strcpy(m_name,name);
    }
    explicit Student(int num)    //该构造函数只有一个参数,称为类型转换构造函数;int型→类类型Student;本例中前面加了关键字explicit,禁止系统隐式转换,所以s3无效,会报错;
    {
        m_num = num;
        m_age = 0;
        m_name = nullptr;
    }

    operator int()    //重载了int类型,即可以将类类型转换为int类型;
    {
        return m_num;
    }


private:
    int m_num;
    char * m_name;
    int age;
};

int main(int argc, char **argv)
{
    Student s1;  //会报错,因为此时已经有自定义有参构造函数,默认无参构造函数失效,系统不生成;
    Student s2(1,"zhangsan",12);
    Student s3 = 1;  //调用类型转换构造函数,慎用!容易产生歧义;这里系统内部发生隐式转换,将int型转换为Student(类类型);应习惯使用s4样例;因为有explicit,这里会报错;
    Student s4(1);

    int num1 = static_cast<int>(s4);  //利用重载operator int()将类类型装换为int类型;
    int num2 = s4.operator int();     //方式二;

    return 0;
}

(2)默认拷贝构造函数 VS 自定义拷贝构造函数

        利用已有对象初始化一个新的对象,两个成员之间的变量对等赋值,默认情况下是浅拷贝,但是存在“多次释放同一个空间的问题,即free() : double free”;

         自定义拷贝构造函数是深拷贝,参与拷贝的两个对象各自的指针成员变量指向自己独立的堆空间;

 引申1:面试题:解释浅拷贝与深拷贝?

        ①类对象拷贝是指利用已有对象初始化一个新的对象,若无自己定义,则默认浅拷贝,系统默认的拷贝构造函数和等号运算符重载(operator=)都是按照浅拷贝的方式,即两个成员之间的变量对等赋值(包含指针变量),但是当对象内有指针成员时,会导致出现“多次释放同一个空间的问题,即free() : double free”的段错误;

        ②深拷贝就是自己定义,新的类对象中的指针成员是新生成的独立的空间(new),其大小等同于待拷贝的对象指针成员所指向的空间大小,其他变量则是对等赋值,解决了浅拷贝中的段错误;并且可以利用下面的移动对象构造函数进行优化;

引申2:面试题:拷贝函数的调用时机?

        ①用已有的对象初始化新的对象;

        ②对象作为形参时;

        ③对象作为函数返回值时;

(3)赋值运算符重载函数

        默认运算符重载函数是浅拷贝,当优化为深拷贝后,其主要不足是当大量的接收函数创建的临时类对象时,需要不断地生成临时对象→拷贝对象→释放临时对象;整个过程耗时较长,且降低了程序性能;

        优化思路为直接将新建的临时对象的空间交给左值,不用经历“新建→拷贝→释放”这样的耗时过程了,就形成了移动拷贝构造函数;

(4)移动拷贝(对象)构造函数

        内部机制是将临时对象的指针成员空间赋给新对象,然后将临时对象的指针指向置为空(原对象就不可以再使用),其他值对等赋值,有移动拷贝构造函数和已移动等号运算符重载两种实现方式;在两种情况下系统自动调用移动对象函数:

        ①新对象先定义后接收函数的返回值(临时对象);

        ②利用std::move()进行强制转换(告诉编译器括号内的是右值,匹配移动对象构造函数);

        通常在编译时加上-fno-elide-constructors的选项,作用是关闭函数返回值优化,同时减少拷贝构造函数的调用,转换为调用对象移动函数,提高程序运行性能;

Test (Yest &&other)  //方式一:移动拷贝构造函数
{
    cout<<"move Test copy"<<endl;
    this->n_name = other.m_name;
    other.m_name = nullptr
}

Test & operator = (Test &&other)  //方式二:移动等号运算符重载
{
    cout<<"move operator = "<<endl;
    this->m_name = other.m_name;
    other.m_name = nummptr;
}

(5)委托构造函数

        ①该类型主要是为了减少多种构造函数形成的代码冗余,内部机制是调用已有的构造函数,初始化新的构造函数;

        ②执行的顺序是委托构造函数会将控制权交给代理构造函数,在代理构造函数执行完之后,在执行委托构造函数的主体;

        ③实现思路:先写全参的构造函数(称为代理函数),后写委托构造函数;

        ④缺陷:如果一个构造函数为委托构造函数,那么其初始化列表就不能对成员和基类进行初始化,只能使用调用委托构造函数时带来的参数值去匹配代理构造函数;

几个例子:

 5. 左值、右值、左引用、右引用

(1)左值的特点:

        ①指向特点内存的具有名称的值(具有对象名或者变量名);

        ②有一个相对稳定的内存地址;

        ③有一段较长的生命周期,长期存在的值;

(2)右值的特点:

        ①不指向稳定内存地址的匿名值(例如常数1,3,5...)(怎样理解?);

        ②生命周期很短,通常是暂时的;

(3)区分左右值,可以通过&运算符获取地址的就是左值,否则就是右值;

(4)左值转右值,以下两种均可以将对象转换为右值,进而匹配类对象中的移动对象构造函数(避免了拷贝函数的费时低性能):

        ①static_cast<type&&>(对象);

        ②std::move(对象);

(5)引用

        ①int num = 5;

        ②int &l_num = num;     //l_num是左值引用,绑定左值;

      ③const int &c_num = 5;  //c_num是const左值引用,const左值引用即可以绑定左值(常量),也可以绑定右值;

        ④int &&k_num = 5;       //k_num是右值引用,注意是两个&&,右引用绑定右值;

 6. 初始化列表

        初始化列表含有双重含义(定义+初始化)主要是解决那些定义就必须要初始化的变量,有以下几种:

        ①const修饰的成员变量;例如:const int index;

        ②验收引用的成员变量;例如:int &l_num;

        ③成员对象(另一个类的对象),即类对象里面含有另一个类的对象;

7. 类的平凡属性

        (1)当类对象中含有①自定义无参构造函数、②自定义拷贝构造函数、③赋值运算符重载函数、④移动拷贝构造函数、⑤类型转换构造函数、⑥委托构造函数时,就是非平凡类;否则,不带有以上函数时,就是平凡类;

即除了默认的构造函数(无参构造函数、默认拷贝构造函数),外都会破坏函数的平凡属性;

        (2)(带以上函数)非平凡类:需频繁进行赋值等操作,运行耗时较长;

        (3)(不带)平凡类:内存对内存,运行效率高,因为不用类对象复制、转移等操作;

        (4)采用default关键字,可以采用系统默认生成的构造函数,可以尽量保持类的平凡属性,从而提高程序的运行性能;是良好的代码习惯;示例如下:

Struct Node
{
    Node() = default;
    int num;
};

8. 对类中的成员进行操作

(1)static修饰(成员、函数)

        ①保持在C语言中的相关作用(修饰局部变量时,改变了局部变量的生存周期,由函数生存周期增加为程序生存周期,作用域未改变,还是函数内;修饰函数或者全局变量时,改变了函数或者全局变量的作用域,仅限该声明的.c文件内使用,在其他文件无法通过extern使用);

        ②static修饰类对象中的成员变量时,该成员变量成为类的静态变量(也称作类成员),可以直接通过类名访问而不用通过实例化对象,该静态变量被该类所有的对象共享,适用于对象之间的通信;且修饰的成员变量需要在类外初始化(需要加上类名::格式);

        此时这个成员不是该对象的成员,而是称作类成员(被类型所共有);

#include <iostream>

using namespace std;

class A
{
public:
    void set_num(int num)
    {
        m_num = num;
    }

    int get_num()
    {
        return n_num;
    }
 
    static void func()  //静态成员函数,也称类的成员函数;
    {
        cout<<m_index<<endl;   //不能访问非静态成员,只可以访问静态成员
    }
    
//private:    //若想利用类成员的共享特点,需要将该成员属性放到public,否则也会受private属性限制;
    int m_num;
    static int m_index;
}
int A::m_index = 7;  //用static修饰的成员属性,一定要在类外初始化;

int main(int argc, char **argv)
{
    A a;
    a.m_index = 1;    //访问类成员方式一:对象名.类成员名
    A a2;
    a2.m_index = 2;

    cout<<A::m_index<<endl;    //访问类成员方式二:类名::类成员名
                               //输出2
    return 0;
}

        ③static修饰成员函数时,该成员函数称为类的静态成员函数(也称作类的成员函数),可以直接通过类名访问而不用通过实例化对象,静态成员函数没有默认的this指针指针生成,所以该类函数不能访问非静态成员(即没有被static修饰的成员),主要用于与C语言编程中用于混合编程用于回调函数;

        ④static同样受限于private,所以static只有放到public区域才可以发挥作用;

        ⑤工程应用:主要有两点,一是修饰的静态成员变量可以用于该类对象之间的通信(因为可以共享);二是应用于C与c++混合接口编程;

(2)const修饰,const将修饰的变量转换为常量,既可以修饰函数,也可以修饰成员变量,

        ①修饰函数时,该函数只可以访问成员(输出),不可以改变成员的值,但是可以在对应成员前面加上mutable,那么该成员即可被该函数修改;

        ②修饰对象时,该对象只可以调用const修饰的函数;

(3)mutable修饰成员

         ①可以在被const修饰的函数中被修改;

9. noexept、explicit、default、delete、mutable关键字在类对象函数中的作用?

        (1)noexept修饰的函数,表明程序员向编译器保证该函数不会发射异常,那么编译器也就没有必要为处理这个“可能”发生的异常添加一些事先预备好的目标代码,这在一定程度上减少了函数编译后生成的目标代码;c++11为所有类的析构函数都加上了“隐式”noexcept声明;

使用的时机一般:当设计移动函数时,为其加上noexcept,以便此类的对象在使用时可以用移动操作来代替拷贝,提高了程序运行性能(因为有些标准库容器除非知道移动操作是无异常的,否则就会进行拷贝)。

        (2)explicit是禁止隐式转换的;例如当使用功能类型转换构造函数时,加上explicit则无法进行隐式转化,类型转换构造函数即失效(因为其内部进行了隐式转换);

        (3)default关键字通常修饰类对象中无参的构造函数,尽量保持类的平凡属性(单纯的结构体),对象之间赋值通过内存之间赋值操作,从而提高代码的运行性能;

        (4)delete关键字用以修饰类对象中的函数,禁止修饰的函数生成,表明在该类型对象中没有该函数,根据其位置分为两种情况:

         ①放在public时,该修饰的函数在编译时期检查与优化(节省时间,相当于错误提前出现,节省编译时间);

        ②放在private时,该修饰的函数在链接时检查优化(在编译期后,错误检查的晚,容易导致浪费编译时间);

        (5)mutable修饰的成员可以在被const修饰的函数中被修改,通常在类对象含有const修饰的函数时使用;

10.  封装为什么能够提高代码的维护性?

        ①封装的本质是将数据和函数(动作)绑定在同一个类对象里面,后续可以通过对象来完成操作;

         ②封装后的函数对外简单的接口,操作方便;

        ③封装的对象内部含有权限属性设置,函数与数据相对独立于安全;

        ④封装后的对象里面含有大量的构造函数、析构函数,极大地方便了开发者的对象定义与操作;

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/581620.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

力扣sql中等篇练习(二十八)

力扣sql中等篇练习(二十八) 1 每个城市最高气温的第一天 1.1 题目内容 1.1.1 基本题目信息 1.1.2 示例输入输出 1.2 示例sql语句 # Write your MySQL query statement below SELECT w.city_id,MIN(w.day) day,w.degree FROM Weather w INNER JOIN (SELECT city_id,MAX(degr…

chatgpt赋能python:Python中keys的概述

Python中keys的概述 在Python中&#xff0c;字典&#xff08;dictionary&#xff09;是一种非常常见的数据结构&#xff0c;它由一系列键&#xff08;keys&#xff09;和对应值&#xff08;values&#xff09;组成。键是唯一的&#xff0c;而值可以重复。在字典中&#xff0c;…

Lucene基础学习

一、基础知识 1.Lucene简介 2.入门实例 3.内建Query对象 4.分析器Analyzer 5.Query Parser 6.索引 7.排序 8.过滤 9.概念简介 10.Lucene入门实例 二、Lucene的基础 三、索引建立 1.lucene索引_创建_域选项 2.lucene索引_的删除和更新 3.lucene索引_加权操作和Luke的简单演示…

msvcp140.dll丢失怎么办?msvcp140.dll重新安装的解决方法

msvcp140.dll是微软编译器系统中的一个动态链接库文件&#xff0c;它存储了许多的代码和数据&#xff0c;能帮助计算机程序正常运行。当系统中出现了msvcp140.dll丢失的情况时&#xff0c;则会出现程序无法正常运行的错误。这篇文章将为大家介绍如何解决msvcp140.dll丢失的问题…

WookTeam是一款轻量级的开源在线团队协作工具

产品介绍 English Documentation wookteam 是一款轻量级的在线团队协作工具&#xff0c;提供各类文档工具、在线思维导图、在线流程图、项目管理、任务分发&#xff0c;知识库管理等工具。wookteam 支持团队在线聊天沟通&#xff0c;订阅任务动态实时推送。wookteam 全部开源…

JavaScript实现用while语句来计算1-10的和的代码

以下为实现用while语句来计算1-10的和的程序代码和运行截图 目录 前言 一、用while语句来计算1-10的和 1.1 运行流程及思想 1.2 代码段 1.3 JavaScript语句代码 1.4 运行截图 前言 1.若有选择&#xff0c;您可以在目录里进行快速查找&#xff1b; 2.本博文代码可以根据…

chatgpt赋能python:Python中的Dayup:如何用编程实现日益进步?

Python中的Dayup&#xff1a;如何用编程实现日益进步&#xff1f; 在Python编程语言中&#xff0c;Dayup是一个非常受欢迎的概念&#xff0c;它代表了一种积极向上的生活态度&#xff0c;即每天都在不断进步。这一概念起源于一位中国作家的作品&#xff0c;被广泛应用于生活、…

哈希表(模拟散列表 字符串哈希)

目录 一、哈希表的概念二、模拟散列表题目代码实现①拉链法②开放寻址法 三、字符串哈希题目思路注意点代码实现 一、哈希表的概念 哈希表&#xff08;又称为散列表&#xff09;&#xff0c;将一个比较大的值域映射到一个小的范围。 例如有哈希函数 h(x)&#xff0c;将区间 [ …

点到直线距离

点到直线距离最小二乘解释 推倒部分 形象描述是C到AB距离最短&#xff0c;也就是CD最短用数学语言描述是 m i n ∣ ∣ ( B − A ) λ A − C ∣ ∣ min||(B-A) \lambda A - C || min∣∣(B−A)λA−C∣∣ 其中 D ( B − A ) λ A D (B-A) \lambda A D(B−A)λA,其实本质…

c++—断言、异常

一、 断言&#xff0c;主要用于在函数入口处进行参数检查&#xff0c;是否符合参数设置要求&#xff1b; &#xff08;1&#xff09;true&#xff1a;继续执行&#xff1b;false&#xff1a;终止运行&#xff1b; &#xff08;2&#xff09;特点&#xff1a;在程序运行时才能起…

chatgpt赋能python:Python中Decimal模块的使用

Python中Decimal模块的使用 在Python中&#xff0c;用来处理浮点数的内置浮点数类型float&#xff0c;其精度受限于机器上的位数和操作系统的规范。当需要比float类型更高精度的计算时&#xff0c;Python提供了decimal模块。 Decimal模块的导入 from decimal import Decimal…

chatgpt赋能python:Python中的Curdir:介绍与使用

Python中的Curdir: 介绍与使用 Python中的Curdir是一个重要的概念&#xff0c;它表示当前工作目录。Curdir是操作系统中的概念&#xff0c;它在Python中也有着广泛的应用。Curdir不仅仅是一个字符串常量&#xff0c;还是一个有用的属性&#xff0c;通过它我们可以更方便地进行…

34.Mybatis-plus延续

一、Mybatis-Plus。 &#xff08;1&#xff09;ActiveRecord。 简介&#xff1a;ActiveRecord 是一种常见的设计模式之一。ActiveRecord 是一种面向对象的数据库操作模式&#xff0c;它将数据库表映射为类&#xff0c;将表中的行映射为对象。在 ActiveRecord 模式中&#xff…

LAMP安装部署

文章目录 一、LAMP平台与编译安装二、安装部署apache服务三、安装部署MySQL四、安装部署php 一、LAMP平台与编译安装 &#xff08;一&#xff09;、LAMP平台概述 LAMP架构是目前成熟的企业网站应用模式之一&#xff0c;指的是协同工作的一整台系统和相关软件&#xff0c;能够提…

第3章“程序的机器级表示”:访问信息

文章目录 3.4 访问信息3.4.1 操作数指示符3.4.2 数据传送指令3.4.3 数据传送示例 3.4 访问信息 一个 IA32 中央处理单元&#xff08;CPU&#xff09;包含一组八个存储 32 位值的寄存器&#xff0c;这些寄存器用来存储整数数据和指针。 下图显示了这八个寄存器。它们的名字都是…

Python - 批量下载ts文件并合并为mp4

&#xff08;一&#xff09;ts文件下载 网页文件下载其实都可以通过requests.get以文件流的形式获取&#xff0c;并以字节的形式写入本地文件即可。 代码如下&#xff1a; import os import requests def download(url, filenames, dirname):session requests.Session()for …

uniapp水文【uniapp】

文章目录 1、前言2、历史3、发展4、功能5、优缺点6、总结7、附录7.1、高频使用7.2、使用注意 1、前言 Uniapp是一种跨平台的移动应用开发框架&#xff0c;它允许开发者使用一套代码库&#xff0c;同时生成iOS、Android等多个平台的应用程序。这种技术方案可以大大降低开发成本…

NET框架程序设计-第1章.NET框架开发平台体系架构

1.1 .NET 框架基本组成 .NET 框架的核心便是通用语言运行时&#xff08;Commomn Language Runtime&#xff0c;简称 CLR&#xff09;&#xff0c;CLR 是一个可被各种不同的编程语言所使用的运行时。 托管模块(mangaed module)&#xff1a; 一个需要 CLR 才能执行的标准 Window…

实验 3:图形数据结构的实现与应用

东莞理工学院的同学可以借鉴&#xff0c;请勿抄袭 1.实验目的 通过实验达到&#xff1a; 理解和掌握图的基本概念、基本逻辑结构&#xff1b; 理解和掌握图的邻接矩阵存储结构、邻接链表存储结构&#xff1b; 理解和掌握图的 DFS、BFS 遍历操作的思想及其实现&#xff1b; …

威胁情报如何改进 DDoS 保护

分布式拒绝服务 (DDoS) 攻击已成为各种企业的主要威胁&#xff0c;从最小的跨国公司到最大的跨国公司。 根据 2022年全球威胁分析报告&#xff0c;恶意DDoS攻击较2021年增长了150%。此外&#xff0c;DDoS攻击的频率也出现显着上升&#xff0c;令人担忧。 在全球范围内&#x…