【C++】构造函数、析构函数、拷贝构造函数

news2024/12/23 17:18:48

目录

构造函数 

析构函数

拷贝构造函数 


C++ 在 C 语言的基础上增加了面向对象编程,C++ 支持面向对象程序设计。类是 C++ 的核心特性,通常被称为用户定义的类型。

类用于指定对象的形式,它包含了数据表示法和用于处理数据的方法。类中的数据和方法称为类的成员。函数在一个类中被称为类的成员。

类定义是以关键字 class 开头,后跟类的名称。类的主体是包含在一对花括号中。类定义后必须跟着一个分号或一个声明列表。例如,我们使用关键字 class 定义 Box 数据类型,如下所示:

class Box

{

public:

        double getVolume(void)

        {

                return length * breadth * height;

        }

private:

        double length; // 盒子的长度

        double breadth; // 盒子的宽度

        double height; // 盒子的高度

};

类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。

定义 C++ 对象

Box Box1; // 声明 Box1,类型为 Box

Box Box2; // 声明 Box2,类型为 Box

 可以在类的外部使用范围解析运算符 :: 定义该函数

double Box::getVolume(void)

{

        return length * breadth * height;

}

 在 :: 运算符之前必须使用类名。调用成员函数是在对象上使用点运算符(.),这样它就能操作与该对象相关的数据:

Box myBox; // 创建一个对象

myBox.getVolume(); // 调用该对象的成员函数

构造函数 

类的构造函数是类的一种特殊的成员函数,它会在每次创建类的新对象时执行。

构造函数的名称与类的名称是完全相同的,并且不会返回任何类型,也不会返回 void。创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任 务并不是开空间创建对象,而是初始化对象。

1. 函数名与类名相同。

2. 无返回值。

3. 对象实例化时编译器自动调用对应的构造函数。

4. 构造函数可以重载。

无参的构造函数 

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line();  // 这是构造函数
 
   private:
      double length;
};
 
// 成员函数定义,包括构造函数
Line::Line(void)
{
    cout << "Object is being created" << endl;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line;
 
   // 设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

 带参的构造函数

#include <iostream>
 
using namespace std;
 
class Line
{
   public:
      void setLength( double len );
      double getLength( void );
      Line(double len);  // 这是构造函数
 
   private:
      double length;
};
 
// 成员函数定义,包括构造函数
Line::Line( double len)
{
    cout << "Object is being created, length = " << len << endl;
    length = len;
}
 
void Line::setLength( double len )
{
    length = len;
}
 
double Line::getLength( void )
{
    return length;
}
// 程序的主函数
int main( )
{
   Line line(10.0);
 
   // 获取默认设置的长度
   cout << "Length of line : " << line.getLength() <<endl;
   // 再次设置长度
   line.setLength(6.0); 
   cout << "Length of line : " << line.getLength() <<endl;
 
   return 0;
}

如果类中没有显式定义构造函数,则C++编译器会自动生成一个无参的默认构造函数,一旦 用户显式定义编译器将不再生成。 

class Line
{
public:
    // 这是构造函数
    /*Line(double len)
    {
        cout << "Object is being created, length = " << len << endl;
        length = len;
    }*/

private:
    double length;
};

int main()
{
    Line line;
    // 将Line类中构造函数屏蔽后,代码可以通过编译,因为编译器生成了一个无参的默认构造函数
        // 将Line类中构造函数放开,代码编译失败,因为一旦显式定义任何构造函数,编译器将不再生成
        // 无参构造函数,放开后报错:error C2512: “Line”: 没有合适的默认构造函数可用
    // 获取默认设置的长度

    return 0;
}

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为 是默认构造函数。

使用初始化列表来初始化字段 

初始化列表:以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个"成员变量"后面跟 一个放在括号中的初始值或表达式。

Line::Line( double len): length(len) {

cout << "Object is being created, length = " << len << endl;

}

多个字段需要初始化

C::C( double a, double b, double c): X(a), Y(b), Z(c) { .... } 

成员变量在类中声明次序就是其在初始化列表中的初始化顺序,与其在初始化列表中的先后次序无关

class A {

public:  

         A(int a)  :_a1(a) ,_a2(_a1)  

                {}   

private:

       int _a2;    

       int _a1;

};

int main() {    

        A aa(1);//先初始a2,再初始化a1

}

析构函数

类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。

与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

当我们用C语言写栈时,要对栈进行初始化和销毁,在写栈的时候我们可能会忘记把栈销毁,如果不对栈进行销毁,可能会造成内存泄漏,这是很危险的,但当我们学会C++的析构函数之后,析构函数就会自动调用,销毁栈,用起来就会方便很多。

typedef int DataType;
class Stack
{
public:
 Stack(size_t capacity = 3)
 {
     _array = (DataType*)malloc(sizeof(DataType) * capacity);
     if (NULL == _array)
     {
         perror("malloc申请空间失败!!!");
         return;
     }
     _capacity = capacity;
     _size = 0;
 }
 void Push(DataType data)
 {
     // CheckCapacity();
     _array[_size] = data;
     _size++;
 }
 // 其他方法...

//栈的析构函数
 ~Stack()
 {
     if (_array)
     {
         free(_array);
         _array = NULL;
         _capacity = 0;
         _size = 0;
     }
 }
private:
 DataType* _array;
 int _capacity;
 int _size;
};
void TestStack()
{
 Stack s;
 s.Push(1);
 s.Push(2);
}

编译器生成的默认析构函数,对自定类型成员调用它的析构函数。

class Time
{
public:
 ~Time()
 {
 cout << "~Time()" << endl;
 }
private:
 int _hour;
 int _minute;
 int _second;
};
class Date
{
private:
 // 基本类型(内置类型)
 int _year = 1970;
 int _month = 1;
 int _day = 1;
 // 自定义类型
 Time _t;
};
int main()
{
 Date d;
 return 0;
}

 在main方法中根本没有直接创建Time类的对象,为什么最后会调用Time类的析构函数?          main方法中创建了Date对象d,而d中包含4个成员变量,其中_year, _month, _day三个是 内置类型成员,销毁时不需要资源清理,最后系统直接将其内存回收即可;而_t是Time类对象,所以在d销毁时,要将其内部包含的Time类的_t对象销毁,所以要调用Time类的析构函数。但是main函数中不能直接调用Time类的析构函数,实际要释放的是Date类对象,所以编译器会调用Date 类的析构函数,而Date没有显式提供,则编译器会给Date类生成一个默认的析构函数,目的是在其内部调用Time类的析构函数,即当Date对象销毁时,要保证其内部每个自定义对象都可以正确销毁 ,main函数中并没有直接调用Time类的析构函数,而是显式调用编译器为Date类生成的默认析构函数。

如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数,比如 Date类;有资源申请时,一定要写,否则会造成资源泄漏,比如Stack类。

拷贝构造函数 

只有单个形参,该形参是对本类类型对象的引用(一般常用const修饰),在用已存在的类类型对象创建新对象时由编译器自动调用。

拷贝函数通常用于:

  • 通过使用另一个同类型的对象来初始化新创建的对象。

  • 复制对象把它作为参数传递给函数。

  • 复制对象,并从函数返回这个对象。

拷贝函数的形式:

classname ( const classname &obj) {

         // 构造函数的主体

拷贝构造函数是构造函数的一个重载形式。

拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错, 因为会引发无穷递归调用。 

在编译器生成的默认拷贝构造函数中,内置类型是按照字节方式直接拷贝的,而自定义类型是调用其拷贝构造函数完成拷贝的。

class Date {

public:

         Date(int year = 1900, int month = 1, int day = 1) {

                 _year = year;

                _month = month;

                 _day = day;

        }

          // Date(const Date d)  // 错误写法:编译报错,会引发无穷递归

        // 正确写法  

        Date(const Date& d)  

        {

        _year = d._year;

         _month = d._month;

        _day = d._day;

        }

private:

        int _year;

        int _month;

        int _day;

};

int main() {

Date d1;

Date d2(d1);

return 0;

}

若未显式定义,编译器会生成默认的拷贝构造函数。 默认的拷贝构造函数对象按内存存储字节序完成拷贝,这种拷贝叫做浅拷贝,或者值拷贝。 

class Time {

public:

        Time() {

                _hour = 1;

                _minute = 1;

                _second = 1;

}

        Time(const Time& t) {

                _hour = t._hour;

                 _minute = t._minute;

                 _second = t._second;

                cout << "Time::Time(const Time&)" << endl;

           }

private:

        int _hour;

        int _minute;

         int _second;

};

class Date {

        private: // 基本类型(内置类型)

        int _year = 1970;

        int _month = 1;

        int _day = 1; // 自定义类型

        Time _t;

        };

int main() {

        Date d1;        // 用已经存在的d1拷贝构造d2,此处会调用Date类的拷贝构造函数,但Date类并没有显式定义拷贝构造函数,则编译器会给Date类生成一个默认的拷贝构造函数         Date d2(d1);

         return 0; }

像Date类可以使用默认的拷贝构造函数,因为只有值的拷贝,是浅拷贝。

如果要实现栈的拷贝,栈的拷贝是深拷贝,需要完成深拷贝的构造函数。因为栈的内存是动态开辟出来的,浅拷贝只能拷贝内存的地址,并不能拷贝内存里的值,所以像这种情况不能使用默认的拷贝构造函数,需要自己定义拷贝构造函数。

void Push(const DataType& data) {

        // CheckCapacity();

         _array[_size] = data;

        _size++;

}

类中如果没有涉及资源申请时,拷贝构造函数是否写都可以;一旦涉及到资源申请时,则拷贝构造函数是一定要写的,否则就是浅拷贝。 

为了提高程序效率,一般对象传参时,尽量使用引用类型,返回时根据实际场景,能用引用 尽量使用引用。

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

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

相关文章

Nginx下载安装以及简单使用

本文来说下Nginx的下载安装以及常用功能的简单使用 文章目录下载nginx启动nginx关闭nginx下载nginx 下载地址:http://nginx.org/en/download.html 直接下载 nginx-1.18.0.zip&#xff0c;下载后解压&#xff0c;解压后如下 启动nginx 有很多种方法启动nginx (1)直接双击nginx.…

浅谈深度学习中的概率

摘要&#xff1a;本次就和大家聊一聊深度学习中的概率。本文分享自华为云社区《【MindSpore易点通】深度学习中的概率》&#xff0c;作者&#xff1a; chengxiaoli。 为什么会用到概率呢&#xff1f;因为在深度学习中经常会需要处理随机的数据&#xff0c;或者包含随机性的任务…

Java基础之《netty(2)—IO模型、BIO介绍、NIO介绍》

一、IO模型 1、I/O模型简单的理解&#xff1a;就是用什么样的通道进行数据的发送和接收&#xff0c;很大程度上决定了程序通信的性能。 2、Java共支持3种网络编程模型I/O模式&#xff1a;BIO、NIO、AIO。 3、Java BIO&#xff1a;同步并阻塞&#xff08;传统阻塞型&#xff…

DeepCTR:易用可扩展的深度学习点击率预测算法包

这个项目主要是对目前的一些基于深度学习的点击率预测算法进行了实现&#xff0c;如PNN,WDL,DeepFM,MLR,DeepCross,AFM,NFM,DIN,DIEN,xDeepFM,AutoInt等,并且对外提供了一致的调用接口。 关于每种算法的介绍这里就不细说了&#xff0c;大家可以看论文&#xff0c;看知乎&#x…

做平台没有会员流量?没有客户留存?不妨来试试这个!超A分享购

商业模式最具有拓客引流的创新新模式&#xff0c;当属是“分享购”商业模式。 分享购是一个能够融合总流量与资源的商业运营模式&#xff0c;能够完成全部生态布局的应用&#xff0c;通过融合自营品牌、供应链选品、CPS生活服务等优质&#xff0c;可以实现“交易、共享”的新型…

Spring Cloud框架(原生Hoxton版本与Spring Cloud Alibaba)初级篇 ---- 服务调用

目录一、Ribbon负载均衡服务调用概述Ribbon负载均衡演示RestTemplateRibbon核心组件IRuleRibbon负载均衡算法原理&#xff08;RoundRobinRule原理&#xff09;源码&#xff08;RoundRobinRule&#xff09;手写负载算法二、OpenFeign服务接口调用概述使用步骤OpenFeign超时控制O…

鉴源论坛 · 观模丨浅谈随机测试

作者 | 黄杉 华东师范大学软件工程学院博士 苏亭 华东师范大学软件工程学院教授 首发 | 鉴源论坛 观模 01 什么是随机测试 &#xff08;Random Testing&#xff09; 随机测试是一种使用随机、相互独立的程序输入来对计算机程序进行测试的黑盒软件测试&#xff08;在完全忽…

疫情联防联控解决方案-最新全套文件

疫情联防联控解决方案-最新全套文件一、建设背景1、疫情防控数据涉及多方&#xff0c;共享难2、疫情数据使用实时性和准确性差3、敏感信息容易泄露和滥用4、建设成本高&#xff0c;后续扩展性弱二、思路架构三、建设方案四、获取 - 疫情联防联控全套最新解决方案合集一、建设背…

OA系统必备的五个基本要素

如果你正在寻找可以将工作流程提升到一个新水平的OA系统&#xff08;或办公自动化系统&#xff09;&#xff0c;请查看其是否具备以下五个基本功能。 1、简单的工作流设计器 一个现代的OA系统应该从业务用户的角度出发。 当你坐下来试用一个OA系统时&#xff0c;先从制定自…

spring中使用getBean获取ioc容器创建的对象常见异常

1、获取bean对象的三种基本方式 0、前提准备 <?xml version"1.0" encoding"UTF-8"?> <beans xmlns"http://www.springframework.org/schema/beans"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocat…

线性代数 --- 投影Projection 五(投影矩阵的性质)

投影矩阵的性质 1&#xff0c;投影矩阵不可逆。 行列式的值为0&#xff0c;条件数无穷大&#xff0c;说明该矩阵不可逆是一个奇异矩阵singular matrix。 同样&#xff1a;行列式的值为0&#xff0c;条件数趋近于无穷大&#xff0c;说明该矩阵不可逆是一个奇异矩阵singular matr…

C# async / await 用法

目录 一、简介 二、异步等待返回结果 三、异步方法返回类型 四、await foreach 五、Task.Delay 结束 一、简介 await 运算符暂停对其所属的 async 方法的求值&#xff0c;直到其操作数表示的异步操作完成。 异步操作完成后&#xff0c;await 运算符将返回操作的结果&…

遨博机械臂——末端工具ROS驱动

文章目录知识目标1. 机械臂末端工具&#xff08;EOAT&#xff09;2. 电动夹爪3. 气动吸盘参考知识目标 学习机械臂常用末端工具构成&#xff1b;学习aubo机械臂安装电动夹爪及启动吸盘的方法&#xff1b;学习电动夹爪及气动吸盘ROS驱动的使用方法。 1. 机械臂末端工具&#x…

【附源码】计算机毕业设计JAVA校园社团管理平台

【附源码】计算机毕业设计JAVA校园社团管理平台 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA my…

“3%”与“低个位数”,暴雪为什么要跟网易玩数字游戏?

北京时间11月17日上午&#xff0c;暴雪娱乐发布声明称&#xff0c;由于同网易的现有授权协议将在2023年1月23日到期&#xff0c;将暂停在中国大陆的大部分暴雪游戏服务&#xff0c;包括《魔兽世界》《炉石传说》《守望先锋》《星际争霸》《魔兽争霸Ⅲ&#xff1a;重置版》《暗黑…

[iOS]砸壳

进行砸壳&#xff0c;需要有台越狱手机。如何越狱&#xff0c;参考前一篇“[iOS]手机越狱”。 没有越狱设备的话&#xff0c;可以去某宝寻求帮助&#xff0c;有帮忙砸壳。 1.添加源 打开Cydia软件&#xff0c;软件源中选择编辑&#xff0c;添加源https://cydia.iphonecake.co…

文件预览服务器kkfileview安装部署(linux 版)

1、安装 LiberOffice 安装包 命令下载 wget https://kkfileview.keking.cn/LibreOffice_7.1.4_Linux_x86-64_rpm.tar.gz tar -zxvf LibreOffice_7.1.4_Linux_x86-64_rpm.tar.gz cd LibreOffice_7.1.4.2_Linux_x86-64_rpm/RPMS yum install -y *.rpm 2、验证office是否安装成功…

【MySQL】MVCC详解与MVCC实现原理(MySQL专栏启动)

&#x1f4eb;作者简介&#xff1a;小明java问道之路&#xff0c;专注于研究 Java/ Liunx内核/ C及汇编/计算机底层原理/源码&#xff0c;就职于大型金融公司后端高级工程师&#xff0c;擅长交易领域的高安全/可用/并发/性能的架构设计与演进、系统优化与稳定性建设。 &#x1…

Vue2 Element | 一文带你快速搭建网页界面UI

&#x1f451; 博主简介&#xff1a;    &#x1f947; Java领域新星创作者    &#x1f947; 阿里云开发者社区专家博主、星级博主、技术博主 &#x1f91d; 交流社区&#xff1a;BoBooY&#xff08;优质编程学习笔记社区&#xff09; 前言&#xff1a;在学习本篇文章内容…

Cholesterol胆固醇丨艾美捷Cholesterol胆固醇化学性质

胆固醇是由甾体部分和一条长的侧链组成。人体中胆固醇的总量大约占体重的0.2%&#xff0c;每100克组织中&#xff0c;骨质约含10毫克&#xff0c;骨骼肌约含100毫克&#xff0c;内脏多在150~250毫克之间&#xff0c;肝脏和皮肤含量稍高&#xff0c;约为300毫克。脑和神经组织中…