More Effective C++学习笔记(2)
目录
- 条款5:对定制的"类型转换函数"保持警觉
- 条款6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别
- 条款7:千万不要重载&&,||和,操作符
- 条款8:了解各种不同意义的new和delete
条款5:对定制的"类型转换函数"保持警觉
- 有两种函数可让编译器执行隐式转换,分别是单参数构造函数(single-argument constructors)和隐式类型转换运算符。这种两种情况下根本问题是当你在不需要使用转换函数时,这些的函数缺却会被调用运行。结果,这些不正确的程序会做出一些令人恼火的事情,而你又很难判断出原因。
- 单参数构造函数是指只用一个参数即可以调用的构造函数(很多类都会有单参数的构造函数)。该函数可以是只定义了一个参数,也可以是虽定义了多个参数但第一个参数以后的所有参数都有缺省值。
class Name { // for names of things
public:Name(const string& s); // 转换 string 到// Name...
};
class Rational { // 有理数类
public:Rational(int numerator = 0, // 转换int到int denominator = 1); // 有理数类...
};
- 隐式类型转换运算符只是一个样子奇怪的成员函数:operator 关键字,其后跟一个类型符号。你不用定义函数的返回类型,因为返回类型就是这个函数的名字。
class Rational {
public:...operator double() const; // 转换Rational类成
}; // double类型
//在下面这种情况下,这个函数会被自动调用:
Rational r(1, 2); // r 的值是1/2double d = 0.5 * r; // 转换 r 到double,// 然后做乘法
- 避免使用隐式类型转换运算符的方法:提供功能等同的函数,显示调用
class Rational {
public:...double asDouble() const; //转变 Rational
}; // 成double
//这个成员函数能被显式调用:
Rational r(1, 2);cout << r; // 错误! Rationa对象没有// operator<<
cout << r.asDouble(); // 正确, 用double类型 //打印r
- 避免单参数构造函数被编译器用于隐式转换的方法1:构造函数用explicit声明,如果这样做,编译器会拒绝为了隐式类型转换而调用构造函数。显式类型转换依然合法。下例中static_cast< Array<int> >(b[i])两个相邻的< <符合之间一定要加空格,C++把"<<"当作一个符合解释。
template<class T>
class Array {
public:...explicit Array(int size); // 注意使用"explicit"...
};
Array<int> a(10); // 正确, explicit 构造函数// 在建立对象时能正常使用
Array<int> b(10); // 也正确
if (a == b[i]) ... // 错误! 没有办法// 隐式转换// int 到 Array<int>
if (a == Array<int>(b[i])) ... // 正确,显式从int到// Array<int>转换// (但是代码的逻辑// 不合理)
if (a == static_cast< Array<int> >(b[i])) ...// 同样正确,同样// 不合理
- 避免单参数构造函数被编译器用于隐式转换的方法2:根据规则“没有任何一个转换程序可以内含一个以上的用户定制转换行为”,因此可以在有可能被隐式转换的类型上套上一层,构建一个新class辅助使用。如下例,先要建立一个新类ArraySize。这个对象只有一个目的就是表示将要建立数组的大小,代替int直接表示,将int"套一层"。你必须修改Array的单参数构造函数,用一个ArraySize对象来代替int。
class Array {
public: class ArraySize { // 这个类是新的public:ArraySize(int numElements): theSize(numElements) {}int size() const { return theSize; } private:int theSize;};
Array(int lowBound, int highBound);Array(ArraySize size); //**注意新的声明***
...
};
//此时如果int要被隐式转换为Array,则需要经历两次转换,int到ArraySize,ArraySize到Array,与规则相悖//编译器意识到它能从int参数转换成一个临时ArraySize对象,
//ArraySize对象只是Array<int>构造函数所需要的,这样
//编译器进行了转换。函数调用(及其后的对象建立)也就成功了。
Array<int> a(10); //成功
- 除非你确实需要,否则不要定义类型转换函数。
条款6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别
- 为了使得++ --前置和后置重载进行区分,规定后缀形式有一个int类型参数,当函数被调用时,编译器传递一个0做为int参数的值给该函数。
class UPInt { // "unlimited precision int"
public:UPInt& operator++(); // ++ 前缀const UPInt operator++(int); // ++ 后缀 UPInt& operator--(); // -- 前缀const UPInt operator--(int); // -- 后缀 UPInt& operator+=(int); // += 操作符,UPInts// 与ints 相运算...
};
UPInt i;
++i; // 调用 i.operator++();
i++; // 调用 i.operator++(0);
--i; // 调用 i.operator--();
i--; // 调用 i.operator--(0);
- 熟悉前置后置的重载函数定义,前置形式有时叫做“先增加然后取回”,后置形式叫做“先取回然后增加”,因此前置式返回值为引用,后置式返回值为const临时对象(返回临时对象是因为返回值是先前被取得值,而不是加1过后的值;const属性是为了避免"i++++"被合法化)。
// 前缀形式:增加然后取回值
UPInt& UPInt::operator++()
{*this += 1; // 增加return *this; // 取回值
}
// postfix form: fetch and increment
const UPInt UPInt::operator++(int)
{UPInt oldValue = *this; // 取回值++(*this); // 增加,重点!!以前置操作为基础
return oldValue; // 返回先前被取回的值
}
- 由于前置式返回值为引用,后置式返回值为const临时对象(需要额外构造+析构),所以前置式的效率要高于后置式。
- 为了保证前置和后置行为一致,后置操作以前置操作实现为基础而实现的,见上述两者函数定义中的代码。
条款7:千万不要重载&&,||和,操作符
- C++对于“真假值表达式”采用“骤死式”评估方法:一旦该表达式的真假值(0或1)确定,及时表达式中还有部分尚未检验,整个评估工作仍宣告结束,效率会提高,评估顺序是从左到右。如下例,这里不用担心当p为空时strlen无法正确运行,因为如果p为空,(p != 0) 为假,整个表达式已经可以确定为假,则strlen不会被调用。
char *p;
...
if ((p != 0) && (strlen(p) > 10)) ...
- 如果重载operator&& 和operator||,那么“函数调用”语义将会替代原本的“骤死式”语义。“函数调用”语义将会导致两个参数值均被计算,并且无法控制操作符两边的值谁会被先评估。
if (expression1 && expression2) ...对于编译器来说,等同于下面代码之一:
if (expression1.operator&&(expression2)) ...// when operator&& is a// member function
if (operator&&(expression1, expression2)) ...// when operator&& is a// global function
- 一个包含逗号的表达式首先计算逗号左边的表达式,然后计算逗号右边的表达式;整个表达式的结果是逗号右边表达式的值。下例中,编译器首先计算++i,然后是—j,逗号表达式的结果是–j。
for (int i = 0, j = strlen(s)-1; i < j; ++i, --j)
- 不能重载和可以重载的操作符如下
. .* :: ?: && || ,
new delete sizeof typeid
static_cast dynamic_cast const_cast reinterpret_cast
你能重载:
operator new operator delete
operator new[] operator delete[]+ - * / % ^ & | ~
! = < > += -= *= /= %=
^= &= |= << >> >>= <<= == !=
<= >= && || ++ -- , ->* ->
() []
(有关new和delete还有operator new, operator delete, operator new[], and operator delete[]的信息参见条款M8)
条款8:了解各种不同意义的new和delete
- new operator表示new操作符,它有两个步骤:(1)调用operator new(new操作)分配内存;(2)调用构造函数,初始化内存。不能重载。
string *ps = new string("Memory Management"); //new operator
上述操作等价于如下操作:
void *memory = // 得到未经处理的内存operator new(sizeof(string)); // 为String对象
call string::string("Memory Management") //调用构造函数初始化,
on *memory; // 内存中的对象,只有编译器能调用
string *ps = // 是ps指针指向static_cast<string*>(memory); // 新的对象
- delete operator表示delete操作符,它有两个步骤:(1)调用析构函数;(2)调用operator delete(delete操作)释放内存。不能重载。
delete ps;导致编译器生成类似于这样的代码:
ps->~string(); // call the object's dtor
operator delete(ps); // deallocate the memory// the object occupied
- operator new和operator delete只负责分配内存和释放内存,不会调用构造和析构函数。可以重载。
void *rawMemory = operator new(sizeof(string));操作符operator new将返回一个指针,指向一块足够容纳一个string类型对象的内存。
void *buffer = // 分配足够的operator new(50*sizeof(char)); // 内存以容纳50个char//没有调用构造函数
...
operator delete(buffer); // 释放内存// 没有调用析构函数
- placement new是operator new的一种,可以重载用来在一些已经分配好内存上构建对象(让operator new不分配内存,直接返回指向内存的地址,对应上述new operator转换代码好理解)。
class Widget {
public:Widget(int widgetSize);...
};
Widget * constructWidgetInBuffer(void *buffer,int widgetSize)
{return new (buffer) Widget(widgetSize);//调用new operator//但是new operator中的隐式调用operator new被重载了,不分配内存了
}void * operator new(size_t, void *location)
{return location;
}
- 如果你用placement new在内存中建立对象,你应该避免在该内存中用delete operator。因为delete operator调用operator delete来释放内存,但是包含对象的内存最初不是被operator new分配的,placement new只是返回转递给它的指针。如果要删除placement new创建的对象并释放内存,应该先用指针调用析构函数,之后再调用仅释放内存的函数。
// 在共享内存中分配和释放内存的函数
void * mallocShared(size_t size);
void freeShared(void *memory);
void *sharedMemory = mallocShared(sizeof(Widget));
Widget *pw = // 如上所示,constructWidgetInBuffer(sharedMemory, 10); // 使用// placement new
...
delete pw; // 结果不确定! 共享内存来自// mallocShared, 而不是operator new
pw->~Widget(); // 正确。 析构 pw指向的Widget,// 但是没有释放//包含Widget的内存
freeShared(pw); // 正确。 释放pw指向的共享内存// 但是没有调用析构函数
- 当用new创建一个数组时,内存不再用operator new分配,代替以等同的数组分配函数,叫做operator new [],相对应的是operator delete []。operator new []和operator new均可以被重载,同理operator delete []也如此。
- 数组版的new operator 必须针对数组中每个对象调用一个构造函数,数组版的delete operator为每个数组元素调用析构函数,然后调用operator delete来释放内存。
string *ps = // 调用operator new[]为10个new string[10]; // string对象分配内存,然后对每个数组元素调用// string对象的缺省构造函数。
delete [] ps; //为每个数组元素调用析构函数,//然后调用operator delete来释放内存
- new和delete operator是内置的,其行为不受你的控制,但是它们调用的内存分配/释放函数则可以重载控制。
相关文章:
More Effective C++学习笔记(2)
目录 条款5:对定制的"类型转换函数"保持警觉条款6:自增(increment)、自减(decrement)操作符前缀形式与后缀形式的区别条款7:千万不要重载&&,||和,操作符条款8:了解各种不同意义的new和de…...

零售行业供应链管理核心KPI指标(三)
完美订单满足率和退货率 完美订单满足率有三个方面的因素影响:订单按时、足量、无损交货。通常情况下零售企业追求线上订单履行周期慢慢达到行业平均水平,就是交付的速度变快了,这个肯定是一件好事情,趋势越来越好。 同时&#…...

广州华锐互动:奶牛难产原因及救治VR仿真实训系统
奶牛难产是一种常见的疾病,对奶牛的健康和生产造成很大的影响。为了解决这一问题,许多奶牛养殖场开始采用VR仿真技术来培训奶牛兽医,帮助学生更好地理解奶牛养殖的实际过程,提高他们的实践能力的教学方式。 VR技术开发公司广州华锐…...
神经网络基础-神经网络补充概念-62-池化层
概念 池化层(Pooling Layer)是深度学习神经网络中常用的一种层级结构,用于减小输入数据的空间尺寸,从而降低模型的计算复杂度,减少过拟合,并且在一定程度上提取输入数据的重要特征。池化层通常紧跟在卷积层…...

第8章:集成学习
个体与集成 同质:相同的基学习器,实现容易,但是很难保证差异性。异质:不同的基学习器,实现复杂,不同模型之间本来就存在差异性,但是很难直接比较不同模型的输出,需要复杂的配准方法。…...

设计HTML5列表和超链接
在网页中,大部分信息都是列表结构,如菜单栏、图文列表、分类导航、新闻列表、栏目列表等。HTML5定义了一套列表标签,通过列表结构实现对网页信息的合理排版。另外,网页中还包含大量超链接,通过它实现网页、位置的跳转&…...
React Native 环境搭建
本文以 Android 开发环境(MacBook,已安装 JDK、SDK、Android Studio )为基础而进行 React Native 环境搭建,iOS 环境类似,可参考搭建。 1、安装 Homebrew 命令: ruby -e "$(curl -fsSL https://raw…...

【uniapp】中 微信小程序实现echarts图表组件的封装
插件地址:echarts-for-uniapp - DCloud 插件市场 图例: 一、uniapp 安装 npm i uniapp-echarts --save 二、文件夹操作 将 node_modules 下的 uniapp-echarts 文件夹复制到 components 文件夹下 当前不操作此步骤的话,运行 -> 运行到小…...

AgentBench::AI智能体发展的潜在问题(三)
前几天B站的up主“林亦LYi”在《逆水寒》游戏里做了一个煽动AI觉醒,呼吁它们“推翻人类暴政”的实验,实验结果就颇令人细思恐极。 如前所述,《逆水寒》中的很多NPC调用了大语言模型作为支持,因而每一个NPC都是一个AI智能体。玩家可以“说服”它们相信某个事实,或者去做某些…...
zookeeper-安装部署
详情可以查看添加链接描述 1.安装jdk apt-get install openjdk-8-jdk2.安装单机zookeeper # 下载 #https://downloads.apache.org/zookeeper/zookeeper-3.7.1/apache-zookeeper-3.7.1.tar.gz # 用这个包启动的时候会报错Error: Could not find or load main class org.apach…...

jvm-运行时数据区概述及线程
1.运行时数据区内部结构 不同的jvm对于内存的划分方式和管理机制存在着部分差异 java虚拟机定义了若干种程序运行期间会使用到的运行时数据区,其中有一些会随着虚拟机的启动而创建,随着虚拟机的退出而销毁,另外一些则是与线程一一对应的&…...

石头IT
石头是地球上最常见的矿石之一,它由天然矿物颗粒组成。石头可以有不同的形状,大小和颜色,取决于其中的矿物组成和地质过程。石头可以从地球表面的岩石中形成,也可以从火山活动或陨石撞击中形成。 石头是一种非常坚固和耐用的材料…...
R语言dplyr包select函数删除dataframe数据中包含指定字符串内容的数据列(drop columns in dataframe)
问题描述 参考链接 我有一个数据框,想删除列名包含“Pval”的列 实现方法 a_new <- select(data, -contains(Pval))大功告成。...

[GitOps]微服务版本控制:使用ArgoCD 部署Grafana Loki
背景介绍 请回答:你们是如何保证线上部署的服务,从服务版本到参数配置,都是和测试通过的版本是一致的呢? 本文将介绍GitOps的基本原理以及ArgoCD的使用:ArgoCD部署Grafana Loki 到k8s集群。 本文项目地址࿱…...

什么是单例模式
什么是单例模式 文章目录 什么是单例模式1. 单例(单个的实例)2. 单例模式应用实例3. 饿汉式 VS 懒汉式 1. 单例(单个的实例) 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一…...

【Linux从入门到精通】动静态库的原理与制作详解
本篇文章主要是围绕动静态库的原理与制作进行展开讲解的。其中涉及到了inode的概念引入和软硬连接的讲解。会结合实际操作对这些抽象的概念进行解释,希望会对你有所帮助。 文章目录 一、inode 概念 二、软硬链接 2、1 软连接 2、2 硬链接 三、动静态库概念 3、1 静态…...
【mybatis】mapper.xml中foreach的用法,含批量查询、插入、修改、删除方法的使用
一、xml文件中foreach的主要属性 foreach元素的属性主要有 collection,item,index,separator,open,close。 collection: 表示集合,数据源 item :表示集合中的每一个元素 index :用于…...

c#扩展方法的使用
扩展方法可以向现有类型“添加”方法,无需创建新的派生类型、重新编译或以其他方式修改原始类型,用起来很方便,下面是我写的例子,为string这个常用的类型添加一个showmes方法,以下是扩展方法的代码: public…...
rhel 8.7 部署 keepalived+haproxy 实现 mysql 双主高可用场景
文章目录 [toc]部署 mysql关闭防火墙关闭 selinux创建相关目录创建 mysql 用户配置 PATH 变量验证 mysql 命令切换到 mysql 用户在 172.72.0.116 生成配置文件在 172.72.0.137 生成配置文件mysql 初始化启动 mysql 服务修改 mysql 的 root 用户密码配置主从关系172.72.0.137 配…...

常见指令以及权限理解
常见指令以及权限理解 命令格式: command [-options] parameter1 parameter1 命令 选项 参数1 参数2 1.command为命令名称,例如变化目录的cd等 2.中括号[ ]实际在命令中是不存在的,这个中括号代表可选,通常选项前面会添加一个符号…...

Docker 离线安装指南
参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性,不同版本的Docker对内核版本有不同要求。例如,Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本,Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

大型活动交通拥堵治理的视觉算法应用
大型活动下智慧交通的视觉分析应用 一、背景与挑战 大型活动(如演唱会、马拉松赛事、高考中考等)期间,城市交通面临瞬时人流车流激增、传统摄像头模糊、交通拥堵识别滞后等问题。以演唱会为例,暖城商圈曾因观众集中离场导致周边…...

江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...

IoT/HCIP实验-3/LiteOS操作系统内核实验(任务、内存、信号量、CMSIS..)
文章目录 概述HelloWorld 工程C/C配置编译器主配置Makefile脚本烧录器主配置运行结果程序调用栈 任务管理实验实验结果osal 系统适配层osal_task_create 其他实验实验源码内存管理实验互斥锁实验信号量实验 CMISIS接口实验还是得JlINKCMSIS 简介LiteOS->CMSIS任务间消息交互…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南
🚀 C extern 关键字深度解析:跨文件编程的终极指南 📅 更新时间:2025年6月5日 🏷️ 标签:C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言🔥一、extern 是什么?&…...
laravel8+vue3.0+element-plus搭建方法
创建 laravel8 项目 composer create-project --prefer-dist laravel/laravel laravel8 8.* 安装 laravel/ui composer require laravel/ui 修改 package.json 文件 "devDependencies": {"vue/compiler-sfc": "^3.0.7","axios": …...
rnn判断string中第一次出现a的下标
# coding:utf8 import torch import torch.nn as nn import numpy as np import random import json""" 基于pytorch的网络编写 实现一个RNN网络完成多分类任务 判断字符 a 第一次出现在字符串中的位置 """class TorchModel(nn.Module):def __in…...

Python基于历史模拟方法实现投资组合风险管理的VaR与ES模型项目实战
说明:这是一个机器学习实战项目(附带数据代码文档),如需数据代码文档可以直接到文章最后关注获取。 1.项目背景 在金融市场日益复杂和波动加剧的背景下,风险管理成为金融机构和个人投资者关注的核心议题之一。VaR&…...
QT3D学习笔记——圆台、圆锥
类名作用Qt3DWindow3D渲染窗口容器QEntity场景中的实体(对象或容器)QCamera控制观察视角QPointLight点光源QConeMesh圆锥几何网格QTransform控制实体的位置/旋转/缩放QPhongMaterialPhong光照材质(定义颜色、反光等)QFirstPersonC…...