【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语言,数据结构等技术问题!
【C++深度剖析教程4】C++的二阶构造模式相关推荐
- C++中的二阶构造模式
1 C++中的二阶构造模式 1.1 半成品对象 首先回顾下构造函数: 类的构造函数用于对象的初始化. 构造函数与类同名并且没有返回值. 构造函数在对象定义时自动被调用. 思考如下几个问题: 如何判断构 ...
- 【C++深度剖析教程39】实现C++数组类模板
上一篇文章在那个学习了多参数类模板与特化的分析:点击链接查看上一篇文章:类模板深度剖析 本篇文章学习记录: 数值型模板参数 实现C++数组类模板 1.模板中的数值型参数 模板参数可以是数值型参数.也就 ...
- 【C++深度剖析教程38】类模板深度剖析
加qq1126137994 微信:liu1126137994 一起学习更多技术!!! 1.多参数类模板 类模板可以定义任意多个不同的类型参数 类模板可以被特化: 指定类模板的特定实现 部分类型参数必须 ...
- 【C++深度剖析教程25】继承中的构造与析构
今天来学习C++中继承的构造与析构,有兴趣一起学习的加qq:1126137994 1.问题 如何初始化父类成员?父类构造函数与子类构造函数有什么关系? 子类对象是如何构造的? 子类中可以定义构造函数 ...
- 【C++深度剖析教程7】C++之类中的函数重载
函数重载的回顾(接上一篇文章): 函数重载的本质为相互独立的不同的函数 C++中通过函数名和函数参数确定函数调用 无法直接通过函数名得到重载函数的入口地址 函数重载必然发生在同一个作用域中 类中的成员 ...
- 【C++深度剖析教程40】使用数值型模板技术计算1+2+3+...+N的值
上一篇文章学习了数值型模板技术,并利用相关技术,实现了C++的数组类模板.点击文章查看上一篇文章:点击链接查看 本篇文章,继续利用模板技术来解决一个问题. 如果想求1+2+3+-+N的结果,有很多种方 ...
- 【C++深度剖析教程37】类模板的概念和意义
加qq1126137994 微信:liu1126137994 一起学习更多技术!!! 1.类模板 一些类主要用于存储和组织数据元素 类中数据的组织方式和数据元素的具体类型无关 如 数组类,链表类,st ...
- 【C++深度剖析教程36】深入理解函数模板
加qq1126137994 微信:liu1126137994 一起学习更多技术!!! 1.函数模板深入理解 编译器从函数模板通过具体类型产生不同的函数 编译器会对函数模板进行两次编译 *对模板进行编译 ...
- 【C++深度剖析教程35】函数模板的概念和意义
加qq1126137994 微信:liu1126137994 一起学习更多技术!!! 1.问题引入: C++中有几种变量交换的方法? 定义宏代码块和定义函数 #include <iostream ...
最新文章
- 解题报告(一)B、(CF453D) Little Pony and Elements of Harmony(FWT经典套路 + 任意模数 k 进制FWT + 快速幂)(2)
- (素材源码)猫猫学IOS(十六)UI之XIB自定义Cell实现团购UI
- collapse 聚合
- GDCM:转储GEMS Ultrasound MovieGroup的测试程序
- shell脚本中取消高亮显示_Linux中强大的top命令
- java switch case多个条件_JAVA基础程序设计之判断与循环
- php 第二次出现位置,php – Preg Replace – 替换匹配的第二次出现
- 20190810:存在重复(三种解法)
- Java基础篇:为Box类添加一个方法
- 搜索引擎蜘蛛及网站robots.txt文件详解[转载]
- EM 算法的推导和解释
- Linux常用命令大全(非常全面)
- 第一章 基本句型及补语
- 块数据3.0:秩序互联网与主权区块链
- Python 安装 包时 VC 14 找不到错误终极解决办法
- idea安装及学生邮箱获取一年使用权
- 开源流媒体解决方案,流媒体服务器,推拉流,直播平台,SRS,WebRTC,移动端流媒体,网络会议,优秀博客资源等分享
- Apple pay 苹果支付
- [20160213]关于ansi语法.txt
- 【HLS教程】HLS入门与精通