• 2310C++构造对象


    原文

    本文展示一个构造对象方式,用户无需显式调用构造器.对有参构造器类,该实现在构造改对象时传递默认值来构造.
    当然用户也可指定(绑定)某个参数的值.实现思路参考boost-ext/di的实现.看下示例:

    构 成员{
        整 x=10;
    };
    构 成员1{
        整 x=11;
    };
    类 例子1{:
        例子1(成员 x,成员1 x1){
            输出<<x.x<<行尾;//10
            输出<<x1.x<<行尾;//11
        }
    };
    整 主(){
        动 e1=远前::对象创建者<>().元 创建<例子1>();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    示例比较简单,构造一个对象创建者对象,并调用他的创建来创建一个例子1的对象,因为使用对象创建者来构造,所以不需要传递参数,它会自动构造.

    好处是,构造对象时,可无需考虑该对象构造器几个参数或类型,想要增加参数时,无需修改代码,当然指定参数的话除外.
    该用法也叫依赖注入.

    构思主体实现

    还蛮酷炫,看看如何做到的?先来说下主体想法,首先最重要的当然是对象创建者,该类如何知道要构造对象的构造器参数类型是什么呢,知道参数类型才能构造一个参数传递,同时参数也同样需要对象创建者来构造,依次递归.

    上边说到了有两个问题要解决,第一个就是如何识别构造器的参数类型,第二个是要构造构造器参数时,如果递归构造?

    识别构造器参数类型

    使用任何类型来识别构造器参数,简单示例:

    构 任何类型{<型名 T>
        符号 T(){
            中 T{};
        }
    };
    构 成员{};
    构 例子{
        例子(成员 m,){
        }
    };
    整 主(){
        例子(任何类型(),2);0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    调用任何类型()可匹配至任意类型,然后在构造例子时,编译器会去找相应类型来构造.大家可能发现我使用的是多个参数来举例任何类型,如果参数是单个任何类型会有冲突,因为拷贝构造器也是一个参数,所以编译器会识别冲突,该问题后边也要处理.

    类 例子{:
        例子(成员 m){
            输出<<m.x<<行尾;
        }
    };
    整 主(){
        例子 e(任何类型{});0;
    }
    //--------以下报错
    注意:候选人:'例子::例子(成员)'
    |例子(成员 m){
    |^~~~~~~
    :注意:候选人:'常式 例子::例子(常 例子&)'
    类 例子{
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    递归构造构造器的参数

    因为构造器参数可能是个类对象,该对象的构造器参数又是其他类对象,识别类型后,继续调用函数来构造该对象,以此类推.

    保存绑定参数

    当然使用过程也不全部是使用默认构造,可能也需要传递指定参数与构造器参数绑定,但是构造器的参数类型又是多样的.

    这里先用元组来保存,若识别出来的类型保存数据类型是一致的,则不用构造而是直接传递该数据给构造器.

    代码实现

    开始写代码,肯定有个任何类型的类及对象创建者的类.对象创建者用来构造对象返回,会只用任何类型类来识别类型.

    对象创建者

    大概看下具体的实现:

    <型名...O>
    类 对象创建者{:<型名...T>
        显 对象创建者(T&&...o):依赖_(前向<T>(o)...){}
    //...:
        元组<常 O&...>依赖_;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    元组保存要绑定参数时,要保存数据就得拷贝,这里为了避免拷贝,元组中类型是左引用,但这样就得用户自己来维护要绑定参数的生命期.

    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
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    这里就是创建函数了:
    1,首先判断是不是已绑定了要创建的类对象,如果绑定了,则直接从元组中取出返回.
    2,未绑定的话,再判断是否可构造默认构造(即可无参构造),可以的话返回空对象.
    3,然后判断是不是参数构造器,参数这里分成了两种,是引用类型或非引用类型.因为,识别TT&会引起冲突,所以分开处理.举例说明:

    构 任何类型{<型名 T>符号 T(){
            中 T{};
        }<型名 T>符号 T&(){
            中 T{};
        }
    };
    类 例子{:
        例子(成员 m,){
            输出<<m.x<<行尾;
        }
    };
    例子 e(任何类型{},7);
    //报错如下:
    错误:转换 从'任何类型''成员'是 歧义
    例子 e(任何类型{},7);
    ^~~~~~~~~
    候选:'任何类型::符号 T()[带 T=成员]'
    符号 T(){
    ^~~~~~~~
    注意:候选:'任何类型::符号 T&()[带 T=成员]'
    符号 T&(){
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    4,最后是构造多参构造器,分开一个参数和多个参数的原因是,一个参数需要处理拷贝构造器单参构造器冲突,按参数给创建多参对象函数传递了1~10整数序列,表示目前最多只能支持10个参数的构造器.

    继续看多参的构造:

    <型名 T,大小型...N>
    T 创建多参对象(常 引序<N...>&){
        如 常式(是可构造值<T,<任何引用类型<对象创建者,远前无效,O...>,N>...>){
            中 T{<任何引用类型<对象创建者,远前无效,O...>,N>{}...};
        }{
            中 创建多参对象<T>(造引序<的大小...(N)-1>{});
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    首先判断是否可由多个任何引用类型类型来构造,尽量,直接构造对象,否则,就减少参数个数来重新匹配.

    任何类型

    然后再观察如何编写任何类型,先看任何第一类型的情况.为了避免和拷贝构造器冲突,简单优化下:

    构 任何第一类型{<型名 T,型名=允许如型<!是相同值<,T>>>
        常式 符号 T(){
            中 创建者_->元 创建<T>();
        }
     };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用替失非错来先排除构造拷贝器,用任何第一类型识别参数类型时,需要按模版参数传递要构造的类给,让T不一样,进而告诉编译器要调用的不是拷贝构造器而是其他函数.

    创建者_就是对象创建者对象,构造参数递归调用创建函数.多参也是类似,只是不需要额外判断拷贝构造器.

    还要注意,如果构造器类型是引用类型,在和绑定参数匹配时,会多一次拷贝,所以还要区分.

    <型名 创建者,型名 源,型名...O>
    构 任何第一引用类型{<型名 T,型名=允许如型<!是相同值<,退化型<T>>>,
            型名=允许如型<(是相同<退化型<T>,O>::||...)>>
        常式 符号 T&(){
            中 常转<T&>(创建者_->元 取依赖<T>());
        }<型名 T,型名=允许如型<!是相同值<,退化型<T>>>,
             型名=允许如型<(是相同<退化型<T>,O>::||...)>>
        常式 符号 T&&(){
            中 静转<T&&>(常转<T&>(创建者_->元 取依赖<T>()));
        }
        创建者*创建者_=空针;
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在和绑定参数匹配,且传递引用时,单独实现,直接返回,而不再调用创建者创建函数,并且强制转化.多参类型识别也是类似.

    源码在此

  • 相关阅读:
    【保姆级】新机器部署Nginx
    数组题目总结 ---- 田忌赛马
    力扣常见算法题
    第五章:存储过程【mysql数据库-进阶】
    pandas连接oracle数据库并拉取表中数据到dataframe中、筛选当前时间(sysdate)到一天之前的所有数据(筛选一天范围数据)
    Got a packet bigger than ‘max_allowed_packet‘ bytes_navicat_heidisql工具导入报错
    【从0入门JVM】-01Java代码怎么运行的
    上市公司退市的条件是什么
    山西电力市场日前价格预测【2023-11-07】
    c++学习笔记2_继承与多态
  • 原文地址:https://blog.csdn.net/fqbqrr/article/details/133513555