当前位置: 首页 > news >正文

【C++编程语言】之类和对象---对象初始化和清除

目录

        • 1.构造函数和析构函数-----对象的创建和清除
        • 2.构造函数的分类及调用
        • 3.拷贝构造函数调用时机
        • 4.构造函数的调用规则
        • 5.深拷贝和浅拷贝
        • 6.初始化列表(不重要)

1.构造函数和析构函数-----对象的创建和清除

​ 对象的初始化和清除也是非常重要的安全问题

​ 一个对象或者变量没有初始化,对其使用后果是未知

​ 同样的使用完一个对象或变量,没有及时清除,也会造成一定的安全问题。

​ C++利用了构造函数和析构函数解决上述问题,这个函数将会被编译器自动调用,完成对象初始化和清除工作。对象的初始化和清除工作是编译器强制我们做的事情,因此如果我们不提供构造和析构函数,编译器会提供编译器创造的构造函数和析构函数是空实现。

构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数有编译器自动调用,无需手动调用。

析构函数:主要作用于对像销毁前系统自动调用,执行一些清理工作。

构造函数语法:类名(){}

​ 1.构造函数,没有返回值也不写void

​ 2.函数名称与类名相同

​ 3.构造函数可以有参数,因此可以发生重载

​ 4.程序在调用对象时候会自动调用构造,无需手动调用,而且只调用一次。

析构函数语法 :~类名(){}

​ 1.析构函数,没有返回值也不写void

​ 2.函数名称与类名相同,在名称前面加上符号~

​ 3.析构函数不可以有参数,因此不可以发生重载

​ 4.程序在对象销毁前会自动调用函数,需手动调用,而且只调用一次。

//对象的初始化和清理
//1.构造函数,进行初始化操作
class Person{
	public:
    	Person(){
            cout <<"Person的构造函数"<<endl;
        }
    	~Person(){
             cout <<"Person的析构函数"<<endl;
        }
}
void test01(){
     Person p;
}
int main(){
     test01();
    //输出:Person的构造函数 Person的析构函数  
    /* test01函数的数据存放在栈区,内存在函数调用完释放,
    	所有对象的析构函数也调用了。
    */
     Person p;//输出:Person的构造函数
    //对象创建在主函数中,不会立即释放
}

2.构造函数的分类及调用

​ 两种分类方式:

​ 按参数分类:有参构造和无参构造(默认构造)

​ 按类型分类:普通类型和拷贝类型

​ 三种调用方式:

​ 括号法,显示法,隐式转换法

class Perosn{
    public:
    	//无参构造,普通类
    	Person(){
            cout<<"Person的无参构造"<<endl;
        }
    	//有参构造,普通类
    	Person(int a){
            int age = a;
            cout<<"Person的有参构造"<<endl;
        }
    	//拷贝构造函数
    	Person(const Person &p){
            int age = p.a;//将传去的人身上的所有属性,拷贝到我身上 
            cout<<"Person的拷贝构造"<<endl;
        }
}
void main(){
 	//1.括号法
    Person p1;//调用默认构造函数
    /*
		注意:调用默认构造函数时不要加(),如果加了Person p1(); 
			  编译器会认为是一个函数声明,不会创建对象
	*/
    Person p2(10);//调用有参构造函数
    Person p3(p1)//调用拷贝构造函数
        
    //2.显示法
     Person p4;//调用默认构造函数
     Person p5 = Person(10);//调用有参构造函数
     Person p6 = Person(p5);//调用拷贝构造函数
     //Person(10);单独拿出,是一个匿名对象。
     			//特点:当前行执行结束后,系统会立即回收掉匿名对象
    Person(p4);//是错误的,会报p4重定义
    /*
		注意:不要利用拷贝函数初始化匿名对象 
		编译器会认为Person(p4);等价于 Person p4;所以重定义p4了 
	*/
    //3.隐式转换法
    Person p7= 10;//相当于 Person p7 = Person(10); 
    Person p8= p7;//拷贝构造
}

3.拷贝构造函数调用时机

​ C++中拷贝构造函数调用时机通常有三种情况

​ 1.使用一个已经创建完毕的对象来初始化一个新对象。

​ 2.值传递的方式给函数参数传值

​ 3.以值方式返回局部对象

class Person{
    public:
    	int m_Age;
    	Person(){
            cout<<"Person的无参构造"<<endl;
        }
    	Person(int age){
            m_Age = age;
            cout<<"Person的有参构造"<<endl;
        }
    	Person(const Person &p){
            cout<<"Person的拷贝构造"<<endl;
        }
    	~Person(){
            cout<<"Person的析构构造"<<endl;
        }
}

//2.值传递的方式给函数参数传值
void doWork(Person p){
    
}

//3.以值方式返回局部对象
Person doWork2(){
    Person p;
    return p;//此时的p是一个局部对象,当次函数调用结束就会销毁,
    		 //所以要把值拷贝给p3。
}
void main(){
    //1.使用一个已经创建完毕的对象来初始化一个新对象。
    Person p1(20);
    Person p2(p1);
    cout << p2.m_Age<<emdl;//20
    
	//2.值传递的方式给函数参数传值
    Person p;
	doWork(p);//会调用拷贝构造函数,因为当把实参的值传给函数的形参时
			  //实际上是把实参的值复制给形参,也就是拷贝
    
	//3.以值方式返回局部对象
    Person p3 = doWork2();
}

4.构造函数的调用规则

​ 默认情况下,C++编译器至少给一个类添加三个函数

​ 1.默认构造函数(无参,函数体空)

​ 2.默认析构函数(无参,函数体空)

​ 3.默认拷贝构造函数,对属性进行值拷贝(值拷贝)

构造函数调用规则:

​ 1.如果用户定义有参构造函数,c++不提供默认无参构造,但会提供默认拷贝构造

​ 2.如果用户定义拷贝构造函数,c++不会提供其他普通构造函数。

5.深拷贝和浅拷贝

深浅拷贝是面试经典问题,也是常见的一个坑。

​ 浅拷贝:简单的赋值拷贝操作。

​ 深拷贝:在堆区重新申请空间,进行拷贝操作。

class Person{
    public:
    	int m_Age;
    	int *m_Height;
    	Person(int age){
            m_Age = age;
        }
    	Person(int age,int height){
            m_Age = age;
            m_Height = new int(height);//把数据存放到堆区  
        }
   		~Person(){
            //析构函数,将堆区开辟堆区数据做释放操作
            if(m_Height!=NULL){
                delete m_Height;
                m_Height = NULL;
            }
         	cout << "析构 "<<endl;   
        }
    	//自己实现拷贝函数,解决浅拷贝带来的问题
    	Person(const Person &p){
              m_Age = p.m_Age;
              m_Height = new int(*p.m_Height);
        }
};
void main(){
    Person p1(18,160);
    cout << "p1的年龄为:"<< p1.m_Age<<endl;
    //调用系统提供的拷贝函数,浅拷贝
    Person p2(p1);
    
    /*
	当数据写在堆区用浅拷贝会带来一种问题是,堆区的数据会重复释放以此报错
	*/
    Person p3(18,182);
    cout << p3.m_Age<< *p3.m_Height <<endl;
    //自己重写拷贝函数,把堆区的数据换地存放
    Person p4(p3);
}

浅拷贝造成错误的原因如下图所示: 当对象p2释放内存是会把堆区的内存也一起释放,但是当轮到对象p1是否内存是,堆区的内存已经释放了,以此造成错误。

在这里插入图片描述

6.初始化列表(不重要)

​ 作用:C++提供了初始化列表语法,用来初始化属性

​ 语法:构造函数():属性1(值1),属性2(值2)…{}

class Person{
    public:
    	int m_A;
    	int m_B;
    	int m_C;
    	//传统初始化操作
    	Person(int a, int b, int c){
            m_A = a;
            m_B = b;
            m_C = c;
        }
    
    	//初始化列表
    	Person():m_A(10),m_A(20),m_A(30){
            
        }
    
    	//两者结合
    	Person(int a, int b, int c):m_A(a),m_A(b),m_A(c){
            
        }
}
void main(){
    //传统初始化操作
    Person p(10,20,30);
    
    //初始化列表
    Person p;
    
    //两者结合初始化列表
    Person p(10,20,30);
    
}

相关文章:

  • Task05|joyfulpandas|变形
  • 【SpringBoot】yaml配置文件语法—总结回顾
  • jenkins 发布项目到k8s tomcat
  • 基于ssm(非maven)学生考勤管理系统
  • C++11新特性(一)
  • WebRTC源码之摄像头视频数据采集源码分析
  • stm32f4xx-PWM输出
  • 【博客474】为什么k8s控制面pod使用的ip是node ip,而非pod cidr中的ip
  • 2022 华为 Java 高级面试题及答案
  • SpringCloud集成RocketMQ
  • 计算机java毕业设计选题汇总(2022)
  • Ruby on Rails 实践:课程导读
  • OpenGL基本架构知识
  • 神奇的卡尔曼滤波,行人追踪的福音
  • 第三章 教育法律法规
  • __proto__ 和 prototype的关系
  • 《Java8实战》-第四章读书笔记(引入流Stream)
  • 《微软的软件测试之道》成书始末、出版宣告、补充致谢名单及相关信息
  • avalon2.2的VM生成过程
  • CNN 在图像分割中的简史:从 R-CNN 到 Mask R-CNN
  • co模块的前端实现
  • css布局,左右固定中间自适应实现
  • Essential Studio for ASP.NET Web Forms 2017 v2,新增自定义树形网格工具栏
  • Nginx 通过 Lua + Redis 实现动态封禁 IP
  • Odoo domain写法及运用
  • php中curl和soap方式请求服务超时问题
  • scrapy学习之路4(itemloder的使用)
  • SegmentFault 社区上线小程序开发频道,助力小程序开发者生态
  • 给github项目添加CI badge
  • 快速构建spring-cloud+sleuth+rabbit+ zipkin+es+kibana+grafana日志跟踪平台
  • 普通函数和构造函数的区别
  • 使用Envoy 作Sidecar Proxy的微服务模式-4.Prometheus的指标收集
  • 突破自己的技术思维
  • 你对linux中grep命令知道多少?
  • 东超科技获得千万级Pre-A轮融资,投资方为中科创星 ...
  • 说说我为什么看好Spring Cloud Alibaba
  • (1)(1.19) TeraRanger One/EVO测距仪
  • (poj1.2.1)1970(筛选法模拟)
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (补)B+树一些思想
  • (离散数学)逻辑连接词
  • .NET 3.0 Framework已经被添加到WindowUpdate
  • .NET Core工程编译事件$(TargetDir)变量为空引发的思考
  • .Net(C#)常用转换byte转uint32、byte转float等
  • .net/c# memcached 获取所有缓存键(keys)
  • /etc/skel 目录作用
  • @DataRedisTest测试redis从未如此丝滑
  • @RequestMapping-占位符映射
  • @软考考生,这份软考高分攻略你须知道
  • [30期] 我的学习方法
  • [AX]AX2012开发新特性-禁止表或者表字段
  • [BUG] Hadoop-3.3.4集群yarn管理页面子队列不显示任务
  • [C# 网络编程系列]专题六:UDP编程
  • [Codeforces1137D]Cooperative Game
  • [c语言]小课堂 day2