《Effective C++中文版,第三版》读书笔记7

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

隐式接口:

​ 仅仅由一组有效表达式构成,表达式自身可能看起来很复杂,但它们要求的约束条件一般而言相当直接而明确。

显式接口:

​ 通常由函数的签名式(也就是函数名称、参数类型、返回类型)构成

​ 在源码中明确可见。

#include <iostream>// 个人感觉隐式和显式接口只是站在了不同的角度看一个类的接口。
// 站在类的角度:
// 类中定义的接口是显示的// 站在template 参数的这个角色的角度而言:接口是隐式的,就是个表达式。
// (换个说法:这个隐式接口其实就是隐藏条件的说法,如果某个类想要作为template的参数,它必须有满足template 表达式要求的接口)
class Base
{public:~Base() = default;virtual void myPrint() = 0;int size() {return 111;}
};class Derived: public Base
{public:Derived() {}~Derived() = default;virtual void myPrint(){std::cout << "Derived cout!!!" <<std::endl;}
};void myPrint1(Derived &dd)
{dd.myPrint();
}template<typename T>
void myPrint2(T t)
{t.myPrint();t.size();
}int main(int argc, char const *argv[])
{Derived dd;myPrint1(dd);myPrint2(dd);return 0;
}

编译期多态:

在编译时才能确定具体调用哪个函数

#include <iostream>
#include <string>// 编译期多态,个人理解就是:在编译时存在多个选择,根据参数类型的不同调用不同的函数。
// 对于重载: 在编译时根据参数的不同,选择不同的函数。(这些个函数已经存在,选一个心仪的)
// 对于function templates: 在编译期,根据不同的template参数具现出不同的函数(函数还没有,根据参数不同,现生成一个心仪的)
template <typename T>
T max1(const T &a, const T &b)
{return a > b ? a : b;
}int min1(const int &a, const int &b)
{std::cout << "int min" << std::endl;return a < b ? a : b;
}std::string min1(const std::string &a, const std::string &b)
{std::cout << "string min" << std::endl;return a < b ? a : b;
}int main(int argc, char const *argv[])
{int a = 11, b = 12;std::string a1("hello world"), b1("hello");std::cout << max1(a, b) << std::endl;std::cout << max1(a1, b1) << std::endl;std::cout << min1(a, b) << std::endl;std::cout << min1(a1, b1) << std::endl;return 0;
}

运行期多态:

在运行时才知道待用哪个函数。

#include <iostream>// 运行时多态演示代码
class Base
{public:~Base() = default;virtual void myPrint() = 0;
};class Derived: public Base
{public:Derived() {}~Derived() = default;virtual void myPrint(){std::cout << "Derived cout!!!" <<std::endl;}
};int main(int argc, char const *argv[])
{Base *pb = new Derived;// 程序运行的时候,才能明确调用的时Derived的myPrint()pb->myPrint();delete pb;return 0;
}

请记住:

classes 和template都支持接口和多态

对于classes而言接口是显式的,以函数签名为中心。多态则是通过virtual函数发生于运行期

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

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

请记住:

声明template参数时,前缀关键字class和typename可互换

请使用关键字表示嵌套从属类型名称;但不得在base class lists(基类列)或member initialization list(成员初值列)内以它作为base class修饰符。

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

请记住:

​ 可在derived class templates内通过“this->”指涉base class templates内的成员名称,或藉由一个明白写出的“base class 资格修饰符”完成

写一段:

#include <iostream>
#include <string>
class CompanyA
{
public:CompanyA() {}~CompanyA() = default;void sendClearText(const std::string &msg){std::cout << "company A sendEncrypted msg = " + msg << std::endl;}void sendEncrypted(const std::string &msg){std::cout << "company A sendEncrypted msg = " + msg << std::endl;}
};class CompanyB
{
public:CompanyB() {}~CompanyB() = default;void sendClearText(const std::string &msg){std::cout << "company B sendEncrypted msg = " + msg << std::endl;}void sendEncrypted(const std::string &msg){std::cout << "company B sendEncrypted msg = " + msg << std::endl;}
};class MsgInfo
{
public:MsgInfo(const std::string &inmsg) : msg(inmsg) {}~MsgInfo() = default;std::string getMsg() const{return msg;}private:std::string msg;
};template <typename Company>
class MsgSender
{
public:MsgSender() {}~MsgSender() = default;void sendClear(const MsgInfo &info){std::string msg = info.getMsg();Company c;c.sendClearText(msg);}void sendSecret(const MsgInfo &info){std::string msg = info.getMsg();Company c;c.sendEncrypted(msg);}
};// 以template为基类定义子类
template <typename Company>
class LoggingMsgSender : public MsgSender<Company>
{
public:LoggingMsgSender() {}~LoggingMsgSender() = default;// 解决方案3:使用using声明式,假设sendClear在基类// using MsgSender<Company>::sendClear;void sendClearMsg(const MsgInfo &info){std::cout << "adasd" << std::endl;// g++报错: there are no arguments to 'sendClear' that depend on a template parameter,// so a declaration of 'sendClear' must be available// 解决方案1:使用编译参数:“-fpermissive”,使用该参数后从报错变成了告警。// 解决方案2:使用this// this->sendClear(info);sendClear(info); //LoggingMsgSender在被实例化之前不知道sendClear在哪里。编译器也不会到base classes内查找。// 解决方案4: 明确指出被调用函数位于基类中(这个方案不推荐使用),如果sendClear是一个virtual,这样会关闭“virtual 绑定行为”// MsgSender<Company>::sendClear(info);std::cout << "adasdfffff" << std::endl;}
};class CompanyZ
{
public:CompanyZ() {}~CompanyZ() = default;void sendEncrypted(const std::string &msg){std::cout << "company Z sendEncrypted msg = " + msg << std::endl;}
};// template<>这个不是template也不是标准class,而是个特化版的MsgSender template,在template实参是CompanyZ时候使用。
// 这就是模板全特化
template <>
class MsgSender<CompanyZ>
{
public:MsgSender() {}~MsgSender() = default;void sendSecret(const MsgInfo &info){std::string msg = info.getMsg();CompanyZ c;c.sendEncrypted(msg);}
};int main(int argc, char const *argv[])
{LoggingMsgSender<CompanyA> mca;mca.sendClear(std::string("hello"));mca.sendSecret(std::string("hello"));LoggingMsgSender<CompanyB> mcb;mcb.sendClear(std::string("hello"));mcb.sendSecret(std::string("hello"));// 模板全特化:针对某一个类型的全面特化。LoggingMsgSender<CompanyZ> mcz;// 特化版的MsgSender template中没有提供sendClear// mcz.sendClear(std::string("hello")); // 编译报错mcz.sendSecret(std::string("world"));return 0;
}

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

请记住:

​ template生成多个 classes和多个函数,所以任何template代码都不应该于某个造成膨胀的template参数产生相依关系。

​ 因非模板参数而造成的代码膨胀,往往可以消除。做法是以函数参数或class成员变量替换template参数。

​ 因类型参数(type parameters)造成的代码膨胀,往往可以降低,做法是让带有完全相同二进制表述的具象类型实现共享代码。

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

请记住:

​ 请使用member function templates 生成“可接受所有兼容类型”的函数

​ 如果你声明member templates 用于“泛化构造”或“泛化assignment”,你还需要声明正常的copy构造函数和copy assignment函数。

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

请记住:

​ 当我们编写一个class template,而它所提供的“与此template相关”函数支持“所有参数之隐式转换”时,请将那些函数定义为“class template内部的friend函数”。

条款47:请使用traits class 表现类型信息

请记住:

​ Traits classes使得“类型相关信息”在编译期可用。它们以templates和“”完成实现

​ 整合重载技术后,traits classes有可能在编译期对类型执行if…else测试。

条款48:认识template元编程

这个可能太高阶了,目前知道有这么个东西就行。光靠这里去理解可能有点难。

什么是模板元编程(Template metaprogramming- TMP)?

编写template-base C++程序并执行于编译期的过程

TMP是被发现而不是发明出来的。

使用TMP的好处?

1.它让事情更容易

2.某些错误可以在编译期就找出来

3.可能在每一个方面都高效:较小的可执行文件、较短的运行期、较少的内存需求

缺点:

​ 编译时间变长了

难点:

​ 语法不直观,支持工具不充分。

请记住:

​ TMP可将工作由运行期迁移到编译期,从而实现早期错误侦测和更高的执行效率

​ TMP可被用来生成“基于政策选择组合”(based on combinations of policy choices)的客户定制代码,也可用来避免生成对某些特殊类型并不适合的代码。

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

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

相关文章

【模方ModelFun】实景三维建模和修模4.0.7最新版安装包以及图文安装教程

模方ModelFun 具有多种功能&#xff0c;旨在帮助用户进行实景三维建模和修模。以下是一些主要功能的简要介绍&#xff1a; 实景三维建模&#xff1a;【模方ModelFun】提供了自动化的实景三维重建功能&#xff0c;可以从实景图像中提取几何形状和纹理信息&#xff0c;生成高质量…

golang 多个struct 转换融合为一个json,平级融合或者多级融合

1. 平级融合&#xff0c;就是下面例子&#xff0c;虽然A在B里面&#xff0c;但是A在struct B里面没有名字&#xff0c;输出结果就是A 和B在同一级的json下面。 package mainimport ("encoding/json""fmt" )type A struct {Name string json:"…

【Vue-Element-Admin】导出el-table全部数据

背景 因为el-table实现了分页查询&#xff0c;所以想要实现el-table需要重新编写一个查询全部数据的方法 查询全部数据 listQuery: export default{return{listQuery:{//page:1,//limit:20,//如果想兼容按条件导出&#xff0c;可以定义查询条件age:undefined,sex:undefined…

Java 加了@PreAuthorize注解的接口在Postman中访问

1. 首先&#xff0c;你需要获取一个有效的用户token&#xff0c;该token应包含了相应的接口权限。你可以通过登录或其他身份验证方式来获取token。2. 打开Postman&#xff0c;并确保已选择正确的HTTP方法&#xff08;GET、POST等&#xff09;。3. 在请求的Headers部分&#xff…

【python爬虫】12.建立你的爬虫大军

文章目录 前言协程是什么多协程的用法gevent库queue模块 拓展复习复习 前言 照旧来回顾上一关的知识点&#xff01;上一关我们学习如何将爬虫的结果发送邮件&#xff0c;和定时执行爬虫。 关于邮件&#xff0c;它是这样一种流程&#xff1a; 我们要用到的模块是smtplib和emai…

解决npm run build 打包出现XXXX.js as it exceeds the max of 500KB.

问题描述&#xff1a; npm run build 时出现下面的问题&#xff1a; Note: The code generator has deoptimised the styling of D:\base\node_modules\_element-ui2.15.12element-ui\lib\element-ui.common.js as it exceeds the max of 500KB.在项目的根目录加粗样式下找到 …

论文笔记: 深度学习速度模型构建的层次迁移学习方法 (未完)

摘要: 分享对论文的理解, 原文见 Jrome Simon, Gabriel Fabien-Ouellet, Erwan Gloaguen, and Ishan Khurjekar, Hierarchical transfer learning for deep learning velocity model building, Geophysics, 2003, R79–R93. 这次的层次迁移应该指从 1D 到 2D 再到 3D. 摘要 深…

程序产生自我意识,创造人工生命

偶然发现一个大佬研究了一个很有意思的项目&#xff0c;研究了好几年&#xff0c;让程序产生自我意识诞生人工生命&#xff0c;感觉10年后肯定是继Chart GPT之后的又一个风口&#xff0c;在这里记录一下分享给大家&#xff0c;这个项目在git上有开源&#xff0c;开源地址&#…

@Controller和@RestController注解区别

&#x1f61c;作 者&#xff1a;是江迪呀✒️本文关键词&#xff1a;SpringBoot、Spring、注解、Controller、RestController☀️每日 一言&#xff1a;弗雷尔卓德是个好地方&#xff0c;可以造东西、打架、大吃一顿&#xff0c;啊~~ 甜蜜的家园呐 ——《英雄联盟》…

Linux 权限

什么是权限 在Linux下有两种用户&#xff0c;分别是超级用户&#xff08;root&#xff09;和普通用户。超级用户可以在Linux下做任何事情&#xff0c;几乎不受限制&#xff0c;而普通用户一般只能在自己的工作目录下&#xff08;/home/xxx&#xff09;工作&#xff0c;以及在系…

HTML5-3-表格

文章目录 属性边框属性标题跨行和跨列单元格边距 HTML 表格由 <table> 标签来定义。 tr&#xff1a;tr 是 table row 的缩写&#xff0c;表示表格的一行。td&#xff1a;td 是 table data 的缩写&#xff0c;表示表格的数据单元格。th&#xff1a;th 是 table header的缩…

flask使用Flask-Mail实现邮件发送

Flask-Mail可以实现邮件的发送&#xff0c;并且可以和 Flask 集成&#xff0c;让我们更方便地实现此功能。 1、安装 使用pip安装&#xff1a; $ pip install Flask-Mail或下载源码安装&#xff1a; $ git clone https://github.com/mattupstate/flask-mail.git $ cd flask-…

Java到底是值传递还是引用传递【通俗易懂】

我相信很多刚学Java的小伙伴都很难理解Java到底是值传递还是引用传递的问题&#xff0c;但肯定背过这道面试题。确实&#xff0c;Java就是值传递&#xff0c;那什么原理呢&#xff1f;请往下看。 我们先看一段代码&#xff1a;分析一下这两句打印的结果分别是什么。 public c…

R3LIVE源码解析(9) — R3LIVE中r3live_lio.cpp文件

目录 1 r3live_lio.cpp文件简介 2 r3live_lio.cpp源码解析 1 r3live_lio.cpp文件简介 在r3live.cpp文件中创建LIO线程后&#xff0c;R3LIVE中的LIO线程本质上整体流程和FAST-LIO2基本一致。 2 r3live_lio.cpp源码解析 函数最开始会进行一系列的声明和定义&#xff0c;发布的…

前端面试基础面试题——5

1.react 和 vue 的区别是什么&#xff1f; 2.虚拟DOM的原理&#xff1f;优点与缺点&#xff1f; 3.类组件和函数组件之间的区别是&#xff1f; 4.state 和 props 区别是什么&#xff1f; 5.React 中 refs 是做什么的&#xff1f; 6.什么是高阶组件&#xff1f; 7.讲讲什么…

重装系统后,MySQL install错误,找不到dll文件,或者应用程序错误

文章目录 1.找不到某某dll文件2.mysqld.exe - 应用程序错误使用DX工具直接修复 1.找不到某某dll文件 由于找不到VCRUNTIME140_1.dll或者MSVCP120.dll&#xff0c;无法继续执行代码&#xff0c;重新安装程序可能会解决此问题。 在使用一台重装系统过的电脑&#xff0c;再次重新…

ModStartCMS v7.2.0 匿名点赞功能,注册站内信

ModStart 是一个基于 Laravel 模块化极速开发框架。模块市场拥有丰富的功能应用&#xff0c;支持后台一键快速安装&#xff0c;让开发者能快的实现业务功能开发。 系统完全开源&#xff0c;基于 Apache 2.0 开源协议&#xff0c;免费且不限制商业使用。 功能特性 丰富的模块市…

elementUI时间选择器

<template>//月选择器//:clearable"false" 去掉<div class"monthCard"><el-date-picker:clearable"false"v-model"monthValue"type"month"placeholder"选择月"change"handleChangeMonth($eve…

音频——硬件拓扑

文章目录 硬件拓扑I2S 数据通路五线模式四线模式两线 TX两线 RX 典型应用硬件连接数据流 硬件拓扑 控制路径&#xff1a;UART/I2C/SPI数据路径&#xff1a;I2S 简略图如下 I2S 数据通路 五线模式 四线模式 两线 TX 两线 RX 典型应用 硬件连接 控制信号&#xff1a;SPI 用…

Flask狼书笔记 | 05_数据库

文章目录 5 数据库5.1 数据库的分类5.2 ORM5.3 使用Flask_SQLAlchemy5.4 数据库操作5.5 定义关系5.6 更新数据库表5.7 数据库进阶小结 5 数据库 这一章学习如何在Python中使用DBMS&#xff08;数据库管理系统&#xff09;&#xff0c;来对数据库进行管理和操作。本书使用SQLit…