Effective C++中文版学习记录(一)

ops/2024/9/22 16:52:27/

Effective C++中文版学习记录(一)

章节一:让自己习惯C++

进度:4/55


文章目录

  • Effective C++中文版学习记录(一)
  • 条款01、视C++为一个语言联邦
  • 条款02、尽量以const,enum,inline替换#define
  • 条款03、尽可能使用const
  • 条款04、对象使用前应该被初始化


条款01、视C++为一个语言联邦

C++三特性:封装、继承、多态

同时C++具有模板化的思路、并且具有STL库可以使用

所以C++可以被称为一种语言的联邦,由C、Object-Oriented C++、Template C++、 STL四个次语言组成

条款02、尽量以const,enum,inline替换#define

在使用define时,如:

#define ASPECTED 0.642

如果在运行该变量时代码报错,报错信息会直接输出0.642相关,而不是ASPECTED,这会导致代码维护性降低

所以解法是:

const double ASPECTED = 0.642;

而enum在这里引入是为了介绍一个方法:enum hack

考虑这样一个类,类内定义了一个变量,而另一个变量需要以该变量为基础来构建

class Game {
private:static const int GameTurn = 10;int scores[GameTurn];
};

明显看出GameTurn在类内就初始化为10,但是有些C++并不能编译这个代码,所以要用enum来解决

class Game {
private:enum {GameTurn = 10};int scores[GameTurn];
};

这里enum相当于做了一个define,将GameTurn定义为10,但是#define并不能对类内的元素定义,所以需要enum来完成

而inline代替define的原因是考虑到这样的一个情况:

#define CALL_WITH_MAX(a, b) f( (a) > (b) ? (a) : (b) )

这样一个像函数的宏定义看着都很难受,而且受限,比如无法写成类内的private函数

所以引入inline的概念,定义inline函数为:

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

这样定义更为清晰,规范,而且真正的是一个函数,符合很多代码的需求

条款03、尽可能使用const

const的意义是说:这个变量不可以修改

常问的问题是:

char * p
const char * p
char * const p
const char * const p

这四种指针有什么区别,这个分析也简单

只需要看const修饰的是哪个东西就可以了

char *p                                 // 无const,所以data和pointer都可改变
const char * p                          // const修饰的是char,所以data不能变,但是pointer可变
char * const p                          // const修饰的是p,所以pointer不能变,但是data可以变
const char * const p                    // 第一个const修饰char,第二个修饰p,所以data和pointer都可以变

不过要注意,const char *pchar const * p是一样的,所以是需要*来判断const修饰谁

const *那就不是修饰指针,* const就是在修饰指针

所以const常常在函数的参数定义上作修饰,告诉函数,哪些参数是不允许修改的,能大大保证程序安全

同时也可以用于修饰函数的返回值,防止返回值被修改

OK,那么考虑这样一个情况,定义的一个类,有一个函数是operator[]

const char & operator[] ...
char & operator[] ...

这两个函数实现的功能是一样的,那为什么要写两个?因为定义的类是可以为const和non-const两种类型

那么对应的函数也需要对这两种进行匹配,不然non-const型的这个类是无法调用const型的operator[]的

所以这里有什么问题?两个函数实现功能一样,难道要复制一遍吗

当然可以,实现上完全没区别,但是更好的做法是令non-const调用const函数,防止代码重复,只需要对传入的参数进行转型

条款04、对象使用前应该被初始化

这个要牢牢记住,比如

int i;
vector<int> test(i, 0);

i没有初始化就用去创建vector,那后果是完全没法预测的,内置型对象一定要手工初始化,在类内也应当初始化再使用

在C++ 11中,可以直接在初始化时定义了

class MyClass {
public:int uninitializedInt = 0;      // 类内默认初始化double uninitializedDouble = 0.0; // 类内默认初始化std::string initializedString = "Hello"; // 类内默认初始化// 其他成员函数...
};

在class的使用中,需要介绍一下non-local static和local static,最好是只有后者,而不要有任何前者。举例:

// file1.cpp
#include <iostream>class Logger {
public:Logger() { std::cout << "Logger Initialized\n"; }void log(const std::string& message) { std::cout << "Log: " << message << std::endl; }
};Logger globalLogger; // non-local static对象void logMessage(const std::string& message) {globalLogger.log(message);  // 会依赖globalLogger对象实现
}// file2.cpp
#include "file1.cpp"class Application {
public:Application() { logMessage("Application Initialized"); }
};Application globalApp; // 另一个non-local static对象

这里globalApp的初始化会调用logMessage,而logMessage的运行依赖globalLogger

然而,globalLogger可能在此时并未初始化,那么logMessage就会报错,那就完了

解决方法是:

// file1.cpp
#include <iostream>class Logger {
public:Logger() { std::cout << "Logger Initialized\n"; }void log(const std::string& message) { std::cout << "Log: " << message << std::endl; }
};Logger& getLogger() {static Logger logger; // local static对象return logger;
}void logMessage(const std::string& message) {getLogger().log(message);
}// file2.cpp
#include "file1.cpp"class Application {
public:Application() { logMessage("Application Initialized"); }
};Application globalApp; // non-local static对象,但不再依赖于globalLogger

把globalLogger放在函数中,利用函数来初始化这个变量,这样就能保证它一定存在

但是注意,globalApp的存在其实还是不太合规,这种non-local static对象最好就不要出现


http://www.ppmy.cn/ops/94105.html

相关文章

MySQL中的GROUP BY子句如何使用?

MySQL中的GROUP BY子句用于将结果集按照一个或多个列进行分组。它通常与聚合函数&#xff08;如COUNT、SUM、AVG、MAX、MIN等&#xff09;一起使用&#xff0c;以对每个组执行计算。 下面是GROUP BY子句的基本用法&#xff1a; 1. 语法&#xff1a; sql SELECT 列列表, COUNT…

浅谈C语言位段

1、位段的定义 百度百科中是这样解释位段的: 位段&#xff0c;C语言允许在一个结构体中以位为单位来指定其成员所占内存长度&#xff0c;这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。 以下&#xff0c;我们均在VS2022的…

【大数据】6:MapReduce YARN 初体验

目录 MapReduce & YARN 初体验 集群启停命令 一键启动脚本&#xff1a; 单进程启停 提交MapReduce任务到YARN执行 提交MapReduce程序至YARN运行 提交wordcount示例程序 提交求圆周率示例程序 拓展&#xff1a;蒙特卡罗算法求PI的基础原理 onte Carlo蒙特卡罗算法…

调试和优化大型深度学习模型 - 0 技术介绍

调试和优化大型深度学习模型 - 0 技术介绍 flyfish LLaMA Factory LLaMA Factory 是一个简单易用且高效的大型语言模型&#xff08;Large Language Model&#xff09;训练与微调平台。通过 LLaMA Factory&#xff0c;可以在无需编写任何代码的前提下&#xff0c;在本地完成上…

Redis:缓存雪崩

缓存雪崩 ​ 缓存雪崩是指当缓存中有大量的key在同一时刻过期&#xff0c;或者Redis直接宕机了&#xff0c;导致大量的查询请求全部到达数据库&#xff0c;造成数据库查询压力骤增&#xff0c;甚至直接挂掉。 ​ 和缓存击穿不同的是&#xff0c;缓存击穿指并发查同一条数据&a…

django学习入门系列之第七点《案例 添加页面》

文章目录 7.6 前端整合标准引入格式案例 添加页面 往期回顾 7.6 前端整合 HTMLCSSjavaScript、jQueryBootStrap&#xff08;动态效果依赖于jQuery&#xff09; 标准引入格式 css在上面js动态效果放下面bootstrap依赖于jQuery&#xff0c;所以先要有jQuery&#xff0c;再有bo…

Mybatis-springBoot

MyBatis 是一个流行的 Java 持久层框架&#xff0c;它简化了与关系型数据库的交互。通过将 SQL 语句与 Java 代码进行映射&#xff0c;MyBatis 提供了一种方便、灵活的方式来执行数据库操作。它支持动态SQL、缓存机制和插件扩展&#xff0c;使得开发人员能够更高效地编写和管理…

vue计算白屏时间;vue计算首屏时间

文章目录 参考一、四个时间计算&#xff1f;1. 白屏时间2.首屏时间3. 页面可交互时间:即DOM完全渲染时间4. 加载完成时间 二、使用1.创建公共方法和在路由使用 参考 API&#xff1a;performance.timing 提供了从页面开始加载到页面完全加载的各种时间戳。 链接1 链接2 一、四…