Effective C++学习第十一天

条款41:了解隐式接口和编译期多态

        面向对象编程世界总是以显式接口(源码可见的接口)和运行期多态(virtual)解决问题;对于templates及泛型编程的世界,隐式接口编译期多态显得更加重要;

        对于template,只有当参数具体确定时(具现化)才能确定具体操作,而这些具现化的行为发生在编译期,以不同的template参数具现化function templates会导致调用不同的函数,这就是所谓的编译期多态

         通常显示接口由函数的签名式(函数名称,参数类型,返回类型)构成,而隐式接口则不基于函数签名式,它是由有效表达式组成,表达式自身看起来很复杂,但它们要求的约束条件一般而言直接而又明确;

        template参数身上的隐式接口,和class对象上的显示接口,都是在编译器完成检查;

        对template参数而言,接口是隐式的,基于有效表达式;多态则是通过template具现化和函数重载解析发生于编译器

条款42:了解typename的双重意义

          在声明template类型参数时,class和typename意义完全相同;

          引申:template内出现的名称如果相依于某个template参数,称之为从属名称,如果从属名称在class内呈嵌套状,我们称它为嵌套从属名称,如c::const_iterator就是这样的名称,它其实是一个嵌套从属类型名称,也就是个嵌套从属名称并指涉某类型;如果一个名称(int)并不依赖于template参数的名称,这样的名称称为谓非从属名称;代码如下:

template<typename c>

void print2nd(const c&container)

{

           if(container.size()>=2){

             c::const_iterator iter(container.begin( ));

              ++iter;

              int value=*iter;

           }

}

        嵌套从属名称会导致编译困难,如c::const_iterator *p;C++解析代码有一个规定:如果解析器在template中遭遇一个嵌套从属名称,它便假设这个名称不是个类型,除非你告诉它是,所以缺省情况下,嵌套从属名称不是类型(有一个例外);正确的情况就是告诉C++解析器这个一个类型,在名称前加关键字typename就可以了;

       typename只被用来验明嵌套从属类型名称,其他名称不该有它的存在;typename作为嵌套从属类型名称的前缀词有一个例外:typename不可以出现在base classes list内的嵌套从属结构,也不可在member initialization list(成员初始列表)中作为base class修饰符,如:

template <typename T>

class derived:public base<T>::Nested{

public:

        explicit derived(int x):base<T>::Nested(x){

          typename base<T>::Nested temp;

        }

};

条款43:学习处理模块化基类内的名称

              考虑如下的程序代码:

template <typename company>                           template<typename company>

class msgsender{                                                    class loggingmsgsender:public msgsender<company>{

public:                                                                  public:

       void sendclear(const msginfo & info)                void sendclearmsg(const msginfo & info){

      {

          std::string msg;                                                    sendclear(info);//调用base class函数,代码编译不通过

         company c;

        c.sendcleartext(msg);                                         }

    }                                                                            };

};

      分析:当编译器遇到class template loggingmsgsender定义式,并不知道它继承什么样的class,当然它继承的是msgsender<company>,但其中的company是个template参数,不到后来(loggingmsgsender被具现化)无法确切知道它是什么,也就不知道是否有sendclear这个函数;C++编译器拒绝调用sendclear的原因:它知道base classtemplates有可能被特化,而那个特化的版本不提供和一般性template相同的接口

      解决C++不进入templatized base classes的方法有三种:1)在base class函数调用之前加上this->,也就是this->sendclear(info);2)使用using声明式,告诉编译器sendclear在base class内,如using msgsender<company>::sendclear;3)明确指出被调用函数位于base class内,如msgsender<company>::sendclear(info);但是如果调用的是virtual函数,那么就会关闭virtual绑定行为;

条款44:将与参数无关的代码抽离templates

         templates是节省时间和避免代码重复的一个奇方妙法,但是使用templates可能会导致代码膨胀,其二进制码带着重复的代码数据;可以通过使用共性和变性分析的方法解决这个问题,将多个class的共同部分搬离到新的class,并通过继承或复合的方式来得到公共属性;

         任何templates代码都不该与某个造成膨胀的templates参数产生相依的关系;

         因非类型模板参数而造成的代码膨胀,往往可消除,做法是以函数参数或class成员变量替换templates参数,如矩阵类求逆实现;

         因类型参数而造成的代码膨胀,往往可以降低,做法是让带有完全相同二进制表述的具现类型共享实现代码,如int和long实现;

       如果你实现某些成员函数而他们操作强型指针(如T*),你应该令他们调用另一个操作无类型指针(void *)的函数,后者完成实际工作;

条款45:运用成员函数模板接受所有兼容类型

           真实指针支持隐式转换,如derived class隐式转换成base class;但是同一个templates的不同具现体之间并不存在什么与生俱来的固有关系(如某个带有base-derived关系的B,D两类型分别具现某个template,产生的两个具现体并不带有base-derived关系);

           C++给我们提供一个称为member function templates,它为模板类提供构造函数;这一类构造函数通过对象u创建对象t,而u和t的类型是同一个template的不同具现体,我们称这个函数为泛化构造函数,如:

template<typename T>

class smartptr{

public:

       template<typename u>

      smartptr(const smartptr<u>& other);

};

         但是对于泛化构造函数,有时我们需要指定隐式转换的方向,例如我们不能将int *转换为double*,这时候我们可能需要一个指针指向class的原始数据,这样问题就变成只有存在两个底层指针可以转换的才是我们需要的结果;

        member function templates(成员函数模板)的效果不限于构造函数,它还支持赋值操作,如shared_ptr类的代码:

template<typename T>

class shared_ptr{

public:

    template<class Y>

     explicit shared_ptr(Y* p);//没有const,原因???

     template<class Y>

     shared_ptr(shared_ptr<Y>const&r);//没有explicit,允许隐式转换

     template<class Y>

     explicit shared_ptr(weak_ptr<Y>const&r);

    template<class Y>

     explicit shared_ptr(auto_ptr<Y>&r);//没有const,auto_ptr性质决定

    template<class Y>

     shared_ptr&operator=(shared_ptr<Y>const &r);

    template<class Y>

     shared_ptr&operator=(auto_ptr<Y>&r);//没有const,auto_ptr性质决定

};

     member function templates会声明一个泛化的copy构造函数和copy assignment操作符,但是它不会改变C++语言的规则,C++语言规定如果程序需要一个copy构造函数,你没有声明,编译器会自动帮你生成一个,相同规则也适用于赋值操作,因此如果你不想让系统声明正常的拷贝构造函数和赋值操作符,你得自己声明;

条款46:需要类型转换时请为模板定义非成员函数

         当不存在模板时,如果函数参数都存在隐式转换,则需要将这个函数定义成非成员函数;当存在模板时,这条规则又发生了变化,编译无法通过,因为在template具现化实参推导过程中从不将隐式类型转换函数纳入考虑;

        解决的方法:利用friend关键字,将非成员函数声明为友元函数(新用法),同时提供实现(实现在函数外,通过友元函数调用,这样可以实现代码冲击最小化)(如果只是单纯提供友元声明,那么可以通过编译,却没有办法完成连接,这种称为混合式代码调用,代码调用成功的原因是:函数在被调用的过程中参数可以实现隐式变换;

       类模板的声明式为:template<typename T> class rational;     

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

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

相关文章

Redis源码分析(零)学习路径笔记

文章目录第一阶段第二阶段 熟悉Redis的内存编码结构第三阶段 熟悉Redis数据类型的实现第四阶段 熟悉Redis数据库的实现第五阶段 熟悉客户端和服务器端的代码实现第六阶段 这一阶段主要是熟悉Redis多机部分的代码实现关于测试方面的文件有一些工具类的文件如下SORT命令的实现一些…

Effective C++学习第十二天

条款47&#xff1a;请使用traits classes表现类型信息STL有五类迭代器分类&#xff0c;input迭代器&#xff08;只读&#xff0c;一次&#xff0c;向前&#xff09;、output迭代器&#xff08;可写&#xff0c;一次&#xff0c;向前&#xff09;、forward迭代器&#xff08;读/…

Redis源码分析(一)redis.c //redis-server.c

Redis源码分析&#xff08;一&#xff09;redis.c //redis-server.c 入口函数 int main() 4450 int main(int argc, char **argv) {4451 initServerConfig();4452 if (argc 2) {4453 ResetServerSaveParams();4454 loadServerConfig(argv[1]);4455 …

Linux 学习

1.linux文本命令行语言环境设置命令 查看当前语言环境&#xff1a; echo &#xffe5;LANG 修改&#xff1a; LANG选择的语言环境’ 引申&#xff1a;https://blog.csdn.net/huoyunshen88/article/details/41113633 2.linux中的硬链接和软连接 linux中文件的储存方式&#xf…

vivo C/C++工程师视频面试总结 20180802

1.自我介绍&#xff1a;有点儿紧张了&#xff0c;直接把自己简历上的一些信息信息说了一遍&#xff0c;说完之后在介绍了一下自己的平时的爱好和兴趣&#xff0c;感觉面试官没有理我&#xff0c;直接进入下一环节了。 2.项目详情&#xff1a;主要是自己最近的一个项目和自己负…

Redis源码分析(二)redis-cli.c

文章目录1. int main()2. parseOptions(int argc, char **argv) 进行ip和port的改变3. lookupCommand(char *name) 查找命令&#xff0c;判断命令合法3.2 strcasecmp(name,cmdTable[j].name)3.1 redisCommand cmdTable[]4. cliSendCommand(int argc, char **argv)4.1 cliConnec…

C语言中有bool变量吗?

1.C/C中定义的数据类型&#xff1a; C语言中定义了6种基本数据类型&#xff1a;short,int,long,float,double,char 4种构造类型&#xff1a;数组&#xff0c;结构体&#xff08;struct&#xff09;&#xff0c;共用类型(union)&#xff0c;枚举类型(enum) 指针类型和空类型 C语…

redis源码剖析(三)——基础数据结构

文章目录SDS链表字典这篇文章关于 Redis 的基础数据:SDS SDS &#xff08;Simple Dynamic String&#xff09;是 Redis 最基础的数据结构。直译过来就是”简单的动态字符串“。Redis 自己实现了一个动态的字符串&#xff0c;而不是直接使用了 C 语言中的字符串。 sds 的数据结…

C++迭代器使用错误总结

指针和迭代器的区别&#xff1a; 迭代器&#xff1a; &#xff08;1&#xff09;迭代器不是指针&#xff0c;是类模板&#xff0c;表现的像指针。他只是模拟了指针的一些功能&#xff0c;通过重载了指针的一些操作符&#xff0c;->,*, --等封装了指针&#xff0c;是一…

redis源码剖析(四)跳表

文章目录整数集合跳跃表压缩列表总结整数集合 当一个集合只包含整数&#xff0c;且这个集合的元素不多的时候&#xff0c;Redis 就会使用整数集合 intset 。首先看 intset 的数据结构&#xff1a; typedef struct intset {// 编码方式uint32_t encoding;// 集合包含的元素数量…

vivo C/C++工程师 HR视频面试问题总结20180807

一开始没想到这次视频面是HR面试&#xff0c;还以为是技术面试&#xff0c;毕竟上次面试的时候技术问题问的相对比较少&#xff0c;所以面试准备方向有点儿错了&#xff0c;不过还是总结一下具体问题。 1&#xff09;自我介绍&#xff1a;吸取了上次自我介绍的经验&#xff0c;…

在Redis客户端设置连接密码 并演示密码登录

我们先连接到Redis服务 然后 我们要输入 CONFIG SET requirepass “新密码” 例如 CONFIG SET requirepass "A15167"这样 密码就被设置成立 A15167 我们 输入 AUTH 密码 例如 AUTH A15167这里 返回OK说明成功了 然后 我们退出在登录就真的需要 redis-cli -h IP地…

redis源码剖析(五)—— 字符串,列表,哈希,集合,有序集合

文章目录对象REDIS_STRING &#xff08;字符串&#xff09;REDIS_LIST 列表REDIS_SET &#xff08;集合&#xff09;REDIS_ZSET &#xff08;有序集合&#xff09;REDIS_HASH (hash表)int refcount&#xff08;引用计数器&#xff09;unsigned lru:REDIS_LRU_BITS对象 对于 Re…

函数sscanf小结

1.sscanf用于处理固定格式的字符串&#xff0c;包含在头文件<cstdio>中&#xff0c;函数原型为&#xff1a; int sscanf(const char *buffer,const char*format,[]argument ]...); 其中:buffer代表着要存储的数据&#xff0c;format 代表格式控制字符串&#xff0c;arg…

redis源码剖析(六)—— Redis 数据库、键过期的实现

文章目录数据库的实现数据库读写操作键的过期实现数据库的实现 我们先看代码 server.h/redisServer struct redisServer{...//保存 db 的数组redisDb *db;//db 的数量int dbnum;... }再看redisDb的代码&#xff1a; typedef struct redisDb {dict *dict; /*…

多益网络 视频面试面试总结20180816

1.首先是自我介绍&#xff1a;因为等了半个小时&#xff0c;所以有点儿紧张&#xff0c;只说了一下自己的学校&#xff0c;爱好和兴趣&#xff1b; 2.介绍了一个自己的最成功的项目&#xff1a;我介绍了一个关于GPS导航的项目&#xff0c;介绍了项目的内容和项目的一些工作&am…

redis源码剖析(七)—— Redis 数据结构dict.c

文章目录dict.hdict.cdict.h //定义错误相关的码 #define DICT_OK 0 #define DICT_ERR 1//实际存放数据的地方 typedef struct dictEntry {void *key;void *val;struct dictEntry *next; } dictEntry;//哈希表的定义 typedef struct dict {//指向实际的哈希表记录(用数组开链的…

简述linux中动态库和静态库的制作调用流程

假设现在有这些文件&#xff1a;sub.c add.c div.c mul.c mainc head.h&#xff08;前4个.C文件的头文件&#xff09; 1.静态库制作流程 gcc -c sub.c add.c div.c mul.c -->生成 .o目标文件文件 ar rcs libmycal.a *.o …

redis源码剖析(八)—— 当你启动Redis的时候,Redis做了什么

文章目录启动过程初始化server结构体main函数会调用initServer函数初始化服务器状态载入持久化文件&#xff0c;还原数据库开始监听事件流程图启动过程 初始化server结构体从配置文件夹在加载参数初始化服务器载入持久化文件开始监听事件 初始化server结构体 服务器的运行ID…

linux中错误总结归纳

1.使用gcc编译C文件&#xff0c;C文件在for循环语句中出现变量定义 编译器提示错误&#xff1a;“for”loop initial declarations are only allowed in C99 mode. note:use option -stdc99or-stdgnu99 to compile; 原因&#xff1a;gcc的标准是基于c89的&#xff0c;c89不能在…