Effective C++ 改善程序与设计的55个具体做法笔记与心得 1

一. 让自己习惯C++

1. 视C++为一个语言联邦

2. 尽量以const, enum, inline替换#define


#define CALL_WITH_MAX(a,b) f((a) > (b) ? (a) : (b))int a = 5 , b = 0;CALL_WITH_MAX(++a,b); // a = 6, b = 0 CALL_WITH_MAX(++a,b+10); // a = 8, b = 0

你观察到的这个现象是由于宏替换(C preprocessor macro substitution)的工作方式引起的。

当你写CALL_WITH_MAX(++a,b)时,预处理器将整个CALL_WITH_MAX(++a,b)表达式替换为f((++a) > (b) ? (++a) : (b))。你可以看到,这里++a出现了两次。这意味着a的值实际上被增加了两次。

对于CALL_WITH_MAX(++a,b+10),在宏展开后的结果是f((++a) > (b+10) ? (++a) : (b+10))。在这种情况下,只有当++a小于b+10时,++a才会被调用第二次。由于++a(即6)大于b+10(即0+10),所以++a只会被调用一次。

这就是导致你观察到的现象的原因。为了防止这类问题的出现,尽量不要在宏的参数中使用有副作用(比如修改变量值)的表达式。


template<typename T>inline void callWithMax(const T& a, const T& b) {f(a > b ? a : b);}

这里你看到的是C++中的模板函数。模板函数允许我们为多种数据类型编写同一套代码。换句话说,它们允许我们实现所谓的"泛型编程"。

在你给出的函数callWithMax中,它接收两个参数ab,并调用函数f,传入的参数是ab中的较大值。这里的T是一个模板参数,它表示一个类型。这意味着你可以使用任何类型的数据来调用callWithMax,只要这个类型支持>运算符和可以作为f的参数。

例如,如果有一个函数f接收一个int参数,你可以这样使用这个模板函数:

int a = 5, b = 10;
callWithMax(a, b);  // 这里f(10)将被调用

同样,如果f接收一个double参数,你也可以传入两个double值:

double a = 1.2, b = 3.4;
callWithMax(a, b);  // 这里f(3.4)将被调用

总的来说,这个模板函数提供了一种通用的方式,让我们可以用同样的代码处理不同类型的数据。

请记住:

  1. 对于单纯常量,最好以const对象或enums替换#defines
  2. 对于形似函数的宏(macros),最好改用inline函数替换#defines

3. 尽可能使用const

如果const出现在星号左边,表示被指物是常量;如果出现在右边,表示指针自身是常量;如果出现在星号两边,表示被指物和指针两者都是常量。

  1. const在星号左边:你不能通过指针来更改其所指向对象的值,但可以使指针指向其他对象。
int x = 10, y = 20;
const int* ptr = &x;
// *ptr = 30; //错误,无法通过ptr改变x的值
ptr = &y; //正确,可以更改ptr指向的对象
  1. const在星号右边:你可以通过指针来更改其所指向对象的值,但不能使指针指向其他对象。
int x = 10, y = 20;
int* const ptr = &x;
*ptr = 30; //正确,可以通过ptr来改变x的值
// ptr = &y; //错误,无法更改ptr指向的对象
  1. const出现在星号两边:既不能通过指针来更改其所指向对象的值,也不能使指针指向其他对象。
int x = 10, y = 20;
const int* const ptr = &x;
// *ptr = 30; //错误,无法通过ptr改变x的值
// ptr = &y; //错误,无法更改ptr指向的对象

每种情况下,试图违反const约束的操作都会导致编译错误。这是因为const关键字的作用就是帮助开发者避免无意或错误的更改值。

‌‌‌‌  在C++中,如果一个对象被声明为const,那么我们只能调用它的const方法。因为const方法保证了它们不会修改对象的状态。mutable是C++中的一个存储类说明符,跟const关键字一起使用,可以使得const对象中的成员变量在const成员函数中可以被修改。
举例说明


#include<bits/stdc++.h>
using namespace std;class MyClass {
public:MyClass() : m_val(0) {}void inc(){m_val++;  }int getVal(){return m_val;}
private:mutable int m_val;
};
int main() {const MyClass obj;obj.inc();cout << obj.getVal() << endl;  // 输出: 1return 0;
}//上面的代码会报错,因为const对象只能调用他的const方法,正确的代码如下:#include<bits/stdc++.h>
using namespace std;class MyClass {
public:MyClass() : m_val(0) {}void inc() const{m_val++;  }int getVal() const{return m_val;}
private:mutable int m_val;
};
int main() {const MyClass obj;obj.inc();cout << obj.getVal() << endl;  // 输出: 1return 0;
}

请记住:

  1. 将某些东西声明为const可帮助编译器侦测出错误用法。const可被施加于任何作用域内的对象、函数参数、函数返回类型、成员函数本体。
  2. 编译器强制实施bitwise constness,但你编写程序时应该使用“概念上的常量性”。
  3. 当const和non-const成员函数有着是指等价的实现时,令non-const版本调用const版本可避免代码重复。

class MyClass { 
public: int getValue() const { return m_value; } int& getValue() { return const_cast<int&>(static_cast<const MyClass&>(*this).getValue()); } 
private:int m_value{}; 
};//在这个例子中,non-const的`getValue()`函数调用了const版本的`getValue()`函数。我们首先将当前对象(用`*this`表示)转化为`const`引用,之后调用的`getValue()`就是`const`版本的了。

4. 确定对象被使用前已先被初始化


class PhoneNumber{...};
class ABEntry{
public:ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones);
private:string theName;string theAddress;list<PhoneNumber> thePhones;int numTimesConsulted;
};ABEntry::ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones){theName = name;theAddress = address; // 这里的赋值操作是浅拷贝thePhones = phones;    //而非初始化numTimesConsulted = 0;
}

比较好的写法是下面这种,直接初始化:


ABEntry::ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones): theName(name), theAddress(address), thePhones(phones), numTimesConsulted(0){}

请记住:

  1. 为内置型对象进行手工初始化,因为C++不保证初始化他们。
  2. 构造函数最好使用成员初值列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列顺序应该和他们在class中声明次序相同。
  3. 为免除“跨编译单元值初始化次序”问题,请以local static对象替换non-local static对象。

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

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

相关文章

工程打包与运行

黑马程序员Spring Boot2 文章目录 先点击cean&#xff0c;确保打包之前是个干净的环境点击package进行打包&#xff0c;打包成功之后可以看到target文件夹下的文件 到项目目录下使用终端打开&#xff0c;并使用以下命令运行打包好的程序 如果遇到没有主清单属性的问题&#xff…

搜维尔科技邀您共赴2024第四届轨道车辆工业设计国际研讨会

会议内容 聚焦“创新、设计、突破”&#xff0c;围绕“面向生命健康、可持续发展的轨道交通系统” 为主题&#xff0c;从数字化、智能化、人性化、绿色发展等方面&#xff0c;探索轨道交通行业的设计新趋势及发展新机遇。 举办时间 2024年7月10日-12日 举办地点 星光岛-青岛融…

5.音视频基础 FLV

目录 简说FLV FLV Header FLV Body Tag Header ​编辑Tag Data Audio Data Video Data Script Data 简说FLV FLV格式可以包含音频、视频和文本数据&#xff0c;并且可以在网络上进行流媒体传输。优点是文件大小较小&#xff0c;压缩效率高&#xff0c;并且可以在较低…

ROS 激光雷达

ROS 激光雷达 基本工作原理 激光雷达&#xff08;LIDAR&#xff0c;Light Detection and Ranging&#xff09;是一种用于测量距离的远程感应技术。它通过向目标发射激光并分析反射回来的光来测量目标与激光发射源之间的距离。激光雷达广泛应用于多种领域&#xff0c;包括地理…

feign原理

Feign远程调用&#xff0c;核心就是通过一系列的封装和处理&#xff0c;将以JAVA注解的方式定义的远程调用API接口&#xff0c;最终转换成为HTTP的请求形式&#xff0c;然后将HTTP的请求的响应结果&#xff0c;解码成JAVA Bean&#xff0c;放回给调用者。 流程 FeignClient远程…

Vue3 之 Pinia 服务端渲染 (SSR) (九)

Pinia SSR概述 Pinia是一个用于Vue.js的状态管理库&#xff0c;它支持服务端渲染(SSR)。在SSR中&#xff0c;页面在服务器端渲染成HTML字符串&#xff0c;然后发送到客户端&#xff0c;从而提升首屏加载速度和SEO优化。Pinia通过其设计使得在SSR环境下也能轻松使用和管理状态。…

WiFi/BLE芯片(1):英飞凌

前言: 大部分客户很少直接接触到WiFi/Bluetooth的芯片,一般是直接封装到了模块中,隔了一层。具体到芯片而言,WiFi/BLE芯片,芯片厂家有:Qualcomm高通、broadcom博通、Infineon英飞凌、Nordic诺迪科、Espressof乐鑫等。而英飞凌这块产品也是很丰富的,低功耗、距离等性能指…

飞创直线模组厂家,为高精度运动系统提供直驱技术解决方案

飞创Faster motion是国内一家专业的直线模组厂家&#xff0c;致力于研发、设计、生产超长行程、超高速度、超高精度、超重负载以及速度平稳的单轴和多轴直线电机模组&#xff0c;为高速、高精度运动平台提供直驱技术解决方案。 飞创直线模组其生产的单轴、双轴、龙门、多轴模组…

Spring Boot究竟是如何进行自动配置的!

我们都知道 SpringBoot 是要比原始的 SpringMVC 这些好用的&#xff0c;毕竟如果经历过最早的 SSM 模式的开发的话&#xff0c;一定对那些大批量的繁琐的配置文件印象颇深&#xff0c;因为之前使用 SSM 框架来进行开发的时候&#xff0c;那配置文件多的让人心态都容易崩溃&…

化工电力系统RFID无线测温技术的重要性。

在现代工业体系中&#xff0c;化工电力系统的安全与效率对于整个生产链的顺畅运行至关重要。在日常工作中,由于设备制造的原因,设备受环境污染的原因、设备长期运行、严重超载运行、触点氧化、电弧冲击等原因造接触电阻增大,因此在运行时往往不断发热,温度不断上升,给设备安全运…

Leetcode Hot100之双指针

1. 移动零 题目描述 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。解题思路 双指针遍历一遍即可解决: 我们定义了两个指针 i 和 j&#xf…

C++ Windows Hook使用

GitHub - microsoft/Detours: Detours is a software package for monitoring and instrumenting API calls on Windows. It is distributed in source code form. /*挂载钩子 setdll /d:C:\Users\g\source\repos\LotTest\Release\lotDll.dll C:\Users\g\source\repos\LotTest…

Java特性之设计模式【访问者模式】

一、访问者模式 概述 在访问者模式&#xff08;Visitor Pattern&#xff09;中&#xff0c;我们使用了一个访问者类&#xff0c;它改变了元素类的执行算法。通过这种方式&#xff0c;元素的执行算法可以随着访问者改变而改变。这种类型的设计模式属于行为型模式。根据模式&…

WPF三方UI库全局应用MessageBox样式(.NET6版本)

一、问题场景 使用HandyControl简写HC 作为基础UI组件库时&#xff0c;希望系统中所有的MessageBox 样式都使用HC的MessageBox&#xff0c;常规操作如下&#xff1a; 在对应的xxxx.cs 顶部使用using 指定特定类的命名空间。 using MessageBox HandyControl.Controls.Message…

js语法---理解反射Reflect对象和代理Proxy对象

Reflect 基本要点 反射&#xff1a;reflect是一个内置的全局对象&#xff0c;它的作用就是提供了一些对象实例的拦截方法&#xff0c;它的用法和Math对象相似&#xff0c;都只有静态方法和属性&#xff0c;同时reflect也没有构造器&#xff0c;无法通过new运算符构建实例对象&…

xcode和iPhone真机或者watch真机连接问题

1.如果真机是第一次连接xocde&#xff0c;就需要开启真机上的开发者模式&#xff0c;开启开发者模式的方式&#xff1a; iphone/ipad开启方式: 设置 > 隐私与安全 > 开发者模式 > 开启&#xff0c;然后重启就可以了 watch设置&#xff1a;很麻烦&#xff0c;看文章…

数据库异常数据恢复(1)-快速恢复和镜像恢复

(一) 数据库服务器的崩溃和恢复 1. 服务器的修复机制 数据库因为某些原因导致数据库突然异常donw机&#xff0c;为了保证数据库的使用&#xff0c;提供了一些机制进行数据库的恢复 快速恢复&#xff1a;数据库异常down机后重启数据库自己的恢复方式&#xff0c;人工无法干预…

现代谱估计分析信号的功率谱(1)---AR 模型谱估计

本篇文章是博主在通信等领域学习时&#xff0c;用于个人学习、研究或者欣赏使用&#xff0c;并基于博主对通信等领域的一些理解而记录的学习摘录和笔记&#xff0c;若有不当和侵权之处&#xff0c;指出后将会立即改正&#xff0c;还望谅解。文章分类在通信领域笔记&#xff1a;…

Spring相关面试题(一)

Spring 1.谈谈你对Spring的理解 难度系数 Spring 是一个开源框架&#xff0c;为简化企业级应用开发而生。Spring 可以是使简单的JavaBean 实现以前只有EJB 才能实现的功能。Spring 是一个 IOC 和 AOP 容器框架。 Spring 容器的主要核心是&#xff1a; 控制反转&#xff08;…