原文
本文展示一个构造对象
方式,用户无需显式
调用构造器.对有参
构造器类,该实现在构造改对象
时传递默认值
来构造.
当然用户也可指定(绑定
)某个参数的值.实现思路
参考boost-ext/di
的实现.看下示例:
构 成员{整 x=10;
};
构 成员1{整 x=11;
};
类 例子1{
公:例子1(成员 x,成员1 x1){输出<<x.x<<行尾;//10输出<<x1.x<<行尾;//11}
};
整 主(){动 e1=远前::对象创建者<>().元 创建<例子1>();
}
示例比较简单,构造一个对象创建者
对象,并调用他的创建
来创建一个例子1
的对象,因为使用对象创建者
来构造,所以不需要传递参数
,它会自动构造
.
好处是,构造对象
时,可无需考虑该对象构造器
是几个参数或类型
,想要增加参数
时,无需修改代码
,当然指定参数
的话除外.
该用法
也叫依赖注入
.
构思主体实现
还蛮酷炫,看看如何做到的?先来说下主体想法
,首先最重要的当然是对象创建者
,该类如何知道要构造
对象的构造器
的参数类型
是什么呢,知道参数类型
才能构造一个参数
传递,同时参数
也同样需要对象创建者
来构造,依次递归
.
上边说到了有两个
问题要解决,第一个
就是如何识别
构造器的参数类型
,第二个是要构造构造器参数
时,如果递归构造
?
识别构造器参数类型
使用任何类型
来识别构造器参数
,简单示例:
构 任何类型{元<型名 T>符号 T(){中 T{};}
};
构 成员{};
构 例子{例子(成员 m,整){}
};
整 主(){例子(任何类型(),2);中 0;
}
调用任何类型()
可匹配至任意类型
,然后在构造例子
时,编译器会去找相应类型
来构造.大家可能发现我使用的是多个
参数来举例任何类型
,如果参数是单个任何类型
会有冲突
,因为拷贝构造器
也是一个参数,所以编译器
会识别冲突,该问题后边也要处理
.
类 例子{
公:例子(成员 m){输出<<m.x<<行尾;}
};
整 主(){例子 e(任何类型{});中 0;
}
//--------以下报错
注意:候选人:'例子::例子(成员)'
|例子(成员 m){
|^~~~~~~
:注意:候选人:'常式 例子::例子(常 例子&)'
类 例子{
递归构造构造器的参数
因为构造器
参数可能是个类对象
,该对象的构造器参数
又是其他
类对象,识别类型
后,继续调用函数
来构造该对象
,以此类推.
保存绑定参数
当然使用过程也不全部是使用默认构造
,可能也需要传递指定参数
与构造器参数
绑定,但是构造器的参数类型
又是多样的.
这里先用元组
来保存,若识别出来的类型
和保存
数据类型是一致
的,则不用构造
而是直接传递该数据
给构造器.
代码实现
开始写代码,肯定有个任何类型
的类及对象创建者
的类.对象创建者
用来构造对象返回
,会只用任何类型
类来识别
类型.
对象创建者
大概看下具体的实现:
元<型名...O>
类 对象创建者{
公:元<型名...T>显 对象创建者(T&&...o):依赖_(前向<T>(o)...){}
//...
私:元组<常 O&...>依赖_;
};
用元组
保存要绑定
参数时,要保存
数据就得拷贝
,这里为了避免拷贝
,元组
中类型是常
左引用,但这样就得用户自己
来维护要绑定
参数的生命期.
O
是要绑定
参数类型,构造器中为了避免拷贝
,用完美转发
来实现.依赖_
就是保存绑定参数
的数据结构
.
元<型名...O>
类 对象创建者{
//...
元<型名 T>T 创建(){如 常式((是相同<T,O>::值||...)){中 取<常 T&>(依赖_);}异 如 常式(是可默认构造值<T>){中 T{};}异 如 常式(是可构造<T,任何第一引用类型<对象创建者,T,远前无效,O...>>::值){中 T{任何第一引用类型<对象创建者,T,远前无效,O...>{本}};}异 如 常式(是可构造<T,任何第一类型<对象创建者,T,远前无效,O...>>::值){中 T{任何第一类型<对象创建者,T,远前无效,O...>{本}};}异{中 创建多参对象<T>(造引序<10>{});}
}
//...
};
这里就是创建
函数了:
1,首先判断是不是已绑定了要创建的类对象
,如果绑定
了,则直接从元组
中取出返回.
2,未绑定的话,再判断是否可构造
默认构造(即可无参
构造),可以的话返回空对象
.
3,然后判断是不是参数构造器
,参数这里分成了两种
,是引用
类型或非引用
类型.因为,识别T
和T&
会引起冲突,所以分开处理.举例说明:
构 任何类型{元<型名 T>符号 T(){中 T{};}元<型名 T>符号 T&(){中 T{};}
};
类 例子{
公:例子(成员 m,整){输出<<m.x<<行尾;}
};
例子 e(任何类型{},7);
//报错如下:
错误:转换 从'任何类型'到'成员'是 歧义
例子 e(任何类型{},7);
^~~~~~~~~
候选:'任何类型::符号 T()[带 T=成员]'
符号 T(){
^~~~~~~~
注意:候选:'任何类型::符号 T&()[带 T=成员]'
符号 T&(){
4,最后是构造多参
构造器,分开一个
参数和多个
参数的原因是,一个
参数需要处理拷贝构造器
及单参
构造器冲突,按参数给创建多参对象
函数传递了1~10
的整数序列
,表示目前最多只能支持10
个参数的构造器
.
继续看多参
的构造:
元<型名 T,大小型...N>
T 创建多参对象(常 引序<N...>&){如 常式(是可构造值<T,在<任何引用类型<对象创建者,远前无效,O...>,N>...>){中 T{在<任何引用类型<对象创建者,远前无效,O...>,N>{本}...};}异{中 创建多参对象<T>(造引序<的大小...(N)-1>{});}
}
首先判断是否可由多个任何引用类型
类型来构造,尽量,直接构造对象
,否则,就减少参数个数
来重新匹配.
任何类型
然后再观察如何编写任何类型
,先看任何第一类型
的情况.为了避免和拷贝构造器
冲突,简单优化下:
构 任何第一类型{元<型名 T,型名=允许如型<!是相同值<源,T>>>常式 符号 T(){中 创建者_->元 创建<T>();}};
使用替失非错
来先排除构造拷贝器
,用任何第一类型
识别参数类型
时,需要按模版参数
传递要构造的类给源
,让T
与源
不一样,进而告诉编译器
要调用的不是拷贝构造器
而是其他
函数.
创建者_
就是对象创建者
对象,构造
参数递归
调用创建
函数.多参
也是类似
,只是不需要额外判断拷贝构造器
.
还要注意
,如果构造器
类型是引用
类型,在和绑定参数
匹配时,会多一次拷贝,所以还要区分.
元<型名 创建者,型名 源,型名...O>
构 任何第一引用类型{元<型名 T,型名=允许如型<!是相同值<源,退化型<T>>>,型名=允许如型<(是相同<退化型<T>,O>::值||...)>>常式 符号 T&(){中 常转<T&>(创建者_->元 取依赖<T>());}元<型名 T,型名=允许如型<!是相同值<源,退化型<T>>>,型名=允许如型<(是相同<退化型<T>,O>::值||...)>>常式 符号 T&&(){中 静转<T&&>(常转<T&>(创建者_->元 取依赖<T>()));}创建者*创建者_=空针;
};
在和绑定参数
匹配,且传递引用
时,单独
实现,直接返回
,而不再调用创建者
的创建
函数,并且强制转化
.多参
类型识别也是类似
.
源码在此