【C++深度剖析教程4】C++的二阶构造模式

今天学习的是C++中的二阶构造模式,二阶构造模式只是设计模式中的简单的模式,是一种软件设计的方法,并没有我们想象的那么高深,设计模式也是一样,只不过是一系列的设计方法,只要我们懂得了原理,那么一切都是相通的。

回顾:构造函数的回顾

关于构造函数:

  • 类的构造函数用于对象的初始化
  • 构造函数与类同名并且没有返回值
  • 构造函数在对象定义时自动被调用

问题:

1.如何判断构造函数的执行结果?
2.在构造函数中执行return语句会发生什么?
3.构造函数执行结束是否意味着对象构造成功?

下面我们以码实例来分析问题:

#include <stdio.h>class Test
{int mi;int mj;
public:Test(int i, int j){mi = i;mj = j;}int getI(){return mi;}int getJ(){return mj;}
};int main()
{  Test t1(1, 2);printf("t1.mi = %d\n", t1.getI());printf("t1.mj = %d\n", t1.getJ());return 0;
}

这就是一个普普通通的代码,用来初始化两个变量的。编译输出结果为:

t1.mi = 1
t1.mj = 2

现在我在构造函数中加一个return语句如下:

#include <stdio.h>class Test
{int mi;int mj;
public:Test(int i, int j){mi = i;return;mj = j;}int getI(){return mi;}int getJ(){return mj;}
};int main()
{  Test t1(1, 2);printf("t1.mi = %d\n", t1.getI());printf("t1.mj = %d\n", t1.getJ());return 0;
}

执行结果为:

t1.mi = 1
t1.mj = 2527220

很显然mj变成了一个随机数,那么说明构造函数在构造对象t1时,执行到return语句后就返回了,并没有继续执行,那么到底是不是这样的呢?我们来做一个试验就知道了,添加一个bool型变量来判断构造函数执行到哪里去了,代码如下:

#include <stdio.h>class Test
{int mi;int mj;bool mStatus;public:Test(int i,int j) : mStatus(false){mi = i;return;mj = j;mStatus = true;}int getI(){return mi;}int getJ(){return mj;}int status(){return mStatus;}
};
int main()
{Test t1(1,2);if(t1.status()){printf("t1.mi = %d\n", t1.getI());printf("t1.mj = %d\n", t1.getJ());}   return 0;
}

编译执行后,没有输出结果,说明构造函数没有将对象初始化完成。
由此我们可以得出几条结论:

构造函数:

  • 只提供自动初始化成员变量的机会
  • 不能保证初始化逻辑一定成功
  • 执行return 语句后构造函数立即结束

从而我们知道,构造函数能决定的只是对象的初始状态,而不是对象的诞生!!!,上面的代码我们加了return语句后,t1这个对象就没有真正的被完全构造,所以不能正常使用这个对象。

这样的对象,我们叫它:半成品对象.

半成品对象的概念:

  • 初始化操作不能按照预期完成二得到的对象

  • 半成品对象是合法的C++对象,但是同时它也是Bug的重要来源

    一般企业中最难以调试的Bug,一是野指针(后面文章会写),其次就是这个半成品对象带来的Bug。

二阶构造

那么我们该如何避免这样的Bug呢?下面就引出二阶构造的含义:

  • 工程开发中的构造过程可分为
    • 资源无关的初始化操作
      *不可能出现异常情况的操作
    • 需要使用系统资源的操作
      *可能出现异常情况,如:内存申请,访问文件

二阶构造大体流程:
在这里插入图片描述
实例代码如下:

#include <stdio.h>class TwoPhaseCons 
{
private:TwoPhaseCons() // 第一阶段构造函数{   }bool construct() // 第二阶段构造函数{ return true; }
public:static TwoPhaseCons* NewInstance(); // 对象创建函数
};TwoPhaseCons* TwoPhaseCons::NewInstance() 
{TwoPhaseCons* ret = new TwoPhaseCons();// 若第二阶段构造失败,返回 NULL    if( !(ret && ret->construct()) ) {delete ret;ret = NULL;}return ret;
}int main()
{TwoPhaseCons* obj = TwoPhaseCons::NewInstance();printf("obj = %p\n", obj);delete obj;return 0;
}

我们来分析一下以上代码:
二阶构造示例:

class TwoPhaseCons 
{
private:TwoPhaseCons() // 第一阶段构造函数{   }bool construct() // 第二阶段构造函数{ return true; }
public:static TwoPhaseCons* NewInstance(); // 对象创建函数
};

第一阶段构造函数与第二阶段构造函数放到private里面了,外部无法调用。
但是在public中,定义的是static 型的NewInstance函数,返回TwoPhaseCons类型的对象,那么通过它就可以调用private里面的构造函数。例如在NewInstance函数里可以有如下代码: TwoPhaseCons* ret = new TwoPhaseCons();,因为处于NewInstance内部,所以它可以调用构造函数。

TwoPhaseCons* TwoPhaseCons::NewInstance() 
{TwoPhaseCons* ret = new TwoPhaseCons();// 若第二阶段构造失败,返回 NULL    if( !(ret && ret->construct()) ) {delete ret;ret = NULL;}return ret;
}
TwoPhaseCons* ret = new TwoPhaseCons()//调用第一阶段的构造函数,做一些初始化操作,不会引起异常的操作

通过判断语句:if( !(ret && ret->construct()) ) 可以判断两个阶段的构造过程是否都没有错误。

而在main函数中有一句话:TwoPhaseCons* obj = TwoPhaseCons::NewInstance();这是调用NewInstance()函数创建obj 对象,因为构造函数TwoPhaseCons为private类型,所以想创建对象,必须用public中的静态创建函数:static TwoPhaseCons* NewInstance(); // 对象创建函数而不能像之前那样直接用构造函数创建对象了如:TwoPhaseCons obj;

上面的程序的运行结果为:
obj = 0x8a0d008

这说明,我们在上面合法的创建看了对象obj。

同时我们也可以看出,用了二阶构造模式后,对象只能在堆空间上进行构造而不能在栈空间上构造,这样好么?答案是肯定的,因为工程上的对象往往是巨大的,一般都会放到堆空间上进行构造。

总结:

  • 构造函数只能决定对象的初始化状态
  • 构造函数中初始化操作的失败不影响对象的诞生
  • 初始化不完全的半成品对象是Bug的主要来源
  • 二阶构造人为的将初始化过程分成两部分
  • 二阶构造能够确保创建的对象都是完整初始化的

想获得各种学习资源以及交流学习的加我:
qq:1126137994
微信:liu1126137994
可以共同交流关于嵌入式,操作系统,C++语言,C语言,数据结构等技术问题!

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

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

相关文章

【C++深度剖析教程5】C++中类的静态成员函数

学习交流加&#xff08;可免费帮忙下载CSDN资源&#xff09;&#xff1a;个人微信&#xff1a; liu1126137994学习交流资源分享qq群1&#xff08;已满&#xff09;&#xff1a; 962535112学习交流资源分享qq群2&#xff08;已满&#xff09;&#xff1a; 780902027学习交流资源…

eclipse 工程中使用引入maven项目遇到maven-resources-plugin:2.6 找不到

1.开始eclipse 配置本地库 首先是从maven 官网下载maven 组件 其次是配置 maven 环境和java 配置jdk 类似这里就不做介绍了 配置完环境后修改\apache-maven-3.3.9\conf\settings.xml 中的<localRepository> 设置本地仓库 然后配置eclipse 下面图中执行较为重要&#xff…

移植uboot之修改代码支持NorFlash记录

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天我们的任务是修改uboot源码支持NorFlash。 上两篇关于uboot移植的文章&#xff0c;我们修改了uboot源代码&#xff0c;支持了串口的输出&#xff…

移植uboot之修改代码支持NorFlash记录续集

接着上一篇文章写的内容&#xff08;上一篇文章链接&#xff1a;移植uboot之修改代码支持NORFLASH&#xff09;&#xff0c;上一篇结尾测试flash的擦除读写功能&#xff0c;结果无法写flash&#xff0c;卡在了这里&#xff1a; 前面已经擦除成功&#xff0c;这里写内容写不进…

C#开发终端式短信的原理和方法

本文示例源代码或素材下载 简介 没发过短信的年轻人肯定是属于那种受保护的稀有动物&#xff0c;通讯发达的今天短信已经成为人们交流的重要手段&#xff0c;其中也蕴含着巨大的市场和经济利益&#xff0c;掌握短信技术的人才也受到各大公司的追捧是目前职场上耀眼的明星。本文…

移植uboot之修改代码支持NorFlash记录续集二

先说一个事&#xff1a;我会在最后把移植好的uboot&#xff0c;内核&#xff0c;分别做一个补丁文件&#xff0c;以后如果用到相同的uboot以及内核都可以直接下载我这个补丁进行打补丁操作就可以直接用~ 上一个移植uboot续集&#xff0c;我们解决了无法写flash的问题&#xff…

Mysql function(函数)

1.mysql 拼接函数 1. 1CONCAT(string1,string2,…) 说明 : string1,string2代表字符串,concat函数在连接字符串的时候&#xff0c;只要其中一个是NULL,那么将返回NULL 1.2 CONCAT_WS(separator,str1,str2,...)说明 : string1,string2代表字符串,concat_ws 代表 conca…

【C++深度剖析教程6】C++之友元

这几天在复习数学考试&#xff0c;都没有学C&#xff0c;今天抽空来学一点。 什么是友元&#xff1f; 友元是C中的一种关系友元发生在函数与类之间或者类与类之间友元关系是单向的&#xff0c;不能传递 在具体讲解友元的性质之前&#xff0c;我们先来看看一个程序&#xff…

call stack and stack buffer overflow

http://en.wikipedia.org/wiki/Call_stack http://en.wikipedia.org/wiki/Stack_buffer_overflow Stack_buffer_overflow里提到的frame pointer 的位置不一样&#xff0c;不同的系统实现应该是不一样的。 运行时的栈是从高地址向低地址分配的&#xff0c;堆是从低地址向高地址…

【C++深度剖析教程8】C++的操作符重载的概念

之前学习了类的函数重载的概念&#xff0c;今天学习操作符重载的概念。在这之前我们先看一个例子&#xff1a; 上面是一个复数的加法&#xff0c;a为复数的实部&#xff0c;b为复数的虚部&#xff0c;在main函数里我想实现复数c1与c2的加法。很显然&#xff0c;正常的号操作符…

大数据开发者应该知道的分布式系统 CAP 理论

无论你是一个系统架构师&#xff0c;还是一个普通开发&#xff0c;当你开发或者设计一个分布式系统的时候&#xff0c;CAP理论是无论如何也绕不过去的。本文就来介绍一下到底什么是CAP理论&#xff0c;如何证明CAP理论&#xff0c;以及CAP的权衡问题。 CAP理论概述 CAP理论&a…

设计模式记--Observer Pattern观察者模式

观察者模式——定义了对象之间的一对多依赖&#xff0c;这样一来&#xff0c;当一个对像改变状态时&#xff0c;它的所有依赖者都会收到通知并自动更新. 从定义可以看出,OBSERVER(观察者)模式逻辑上需要两组对象来实现.首先它必需要有发布者(Publish),也可称为被观察的目标 (…

【C++深度剖析教程9】初探C++标准库

在这之前&#xff0c;我写的C程序不能叫做标准的C程序&#xff0c;因为里面写的大多数还带有C语言的影子。今天我们来学习C标准库。 首先看一下例子&#xff1a;操作符<<的原生意义是按位左移。那么我们重载这个操作符&#xff0c;将变量或者常量&#xff0c;左移到一个…

员工考核UI网页界面(PS大屏文件资料)

现分享人员管理可视化数据统计网页UI、员工考核数据可视化UI网页界面模版的UI源文件&#xff0c;供UI设计师们快速获取PSD源文件完成工作。 若需更多 大屏组件&#xff0c;请移步小7的另一篇文章&#xff1a;数据可视化大屏组件&#xff0c;大屏PSD设计源文件(大屏UI设计规范)…

docker 使用中遇到的问题

1.执行docker run hello 命令建立镜像过程中报错 开始以为是加速器的问题&#xff0c;将/etc/docker/daemon.json 文件删除还是不行 后来执行了这两条命令&#xff1a;就不报错了如下图&#xff1a; systemctl daemon-reloadsystemctl restart docker.service 参考&#xff1…

maven 解决冲突

1.Maven之jar包冲突解决&#xff08;理解maven 产生冲突的原因&#xff09; 导致jar包冲突的原因 1、mvn的传递依赖特性&#xff1a;mvn编译打包除了会引入直接申明的依赖&#xff0c;还会引入间接申明的依赖 2、mvn的依赖仲裁规则&#xff1a; 1&#xff09;. 按照项目总控P…

jz2440开发板修改UBOOT支持NAND FLASH

很多天没有看嵌入式的东西了&#xff0c;今天来看一下&#xff0c;继续之前移植uboot到jz2440开发板。今天我们来实现Uboot支持NAND FLASH。 在之前的文章里&#xff08;点击连接查看之前的记录&#xff09;&#xff0c;我们为了编译通过把NAND FLASH 给屏蔽掉了&#xff0c;现…

i.MX6网卡驱动程序fec.c的分析(AR8035网卡驱动程序的详细分析)之二

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 今天接着分析上次没有分析完的i.MX6网卡驱动程序。上一篇分析了iMX6网卡驱动程序的driver与device的加载过程&#xff08;点击可以查看上一篇文章&…

阅读ethercat官方文档关于ethercat网卡驱动程序的一些内容

学习交流加 个人qq&#xff1a; 1126137994个人微信&#xff1a; liu1126137994学习交流资源分享qq群&#xff1a; 962535112 改造iMX6&#xff08;fec&#xff09;网卡驱动程序前期工作之&#xff1a;阅读ethercat-1.5.2.pdf文档的第四章内容。 ethercat-1.5.2.pdf文档链接&a…

jz2440开发板移植U-boot之修改代码支持DM9000网卡

今天我们来移植U-boot到jz2440开发板&#xff0c;修改代码支持DM9000网卡。查看之前写的移植记录请点击链接&#xff1a;点击查看之前的移植记录 现在大多数开发板都支持DM9000网卡。我们的U-boot源码里面也是有DM9000网卡的驱动程序的。文件为Dm9000x.c&#xff08;drivers\n…