Effective C++(1)

文章目录

  • 1. 让自己习惯C++
    • 条款1:视C++为一个语言联邦
    • 条款2:尽量以const、enum、inline替换#define
    • 条款03:尽可能使用 const
    • 条款4:确保对象在使用之前被初始化


1. 让自己习惯C++

条款1:视C++为一个语言联邦

今天的C++已经是多个多重范型编程语言,一个同时支持过程形式、面向对象形式、函数形式、泛型形式、元编程形式的语言。

条款2:尽量以const、enum、inline替换#define

1)#define 修饰的记号,在预处理的时候,已经全部被替换成了某个数值,如果出错,错误信息可能会提到这个数值,而不会提到这个记号。在纠错方面很花时间,因为其他程序员不知道这个数值代表什么。我们可以用 const 和 enum 解决这个问题。

#define ASPECT_RATION 1.653 --> const double Aspect_ration = 1.653
作为一个常量语言,AspectRation肯定会被编译器看到

2)无法利用#define创建一个class专属常量,因为#define并不注重作用域。一旦宏被定义,它在其后的编译过程中有效。这意味着#define不仅不能用来定义class专属常量,也不能够提供任何封装性。

2)通过使用内联函数模板,可以获得宏的所有效率,以及普通函数的所有可预测行为和类型安全。


#define MY_COMPARE(a, b) f((a) > (b) ? (a) : (b))
//这是一个三目运算符,如果 a > b,则返回 a,否则返回 b
//宏中的每一个实参都必须加上小括号//调用:
int a = 5, b = 0;
MY_COMPARE(++a, b);//1
MY_COMPARE(++a, b + 10);//2/*
1式中,++a => a = 6 => 6 > b = 0 => return ++a;
a 的值竟然增加了两次!
*///定义 inline:
#define MY_MAX(a, b) (a) > (b) ? (a) : (b)template<class T>
inline int MY_COMPARE(const T&a, const T&b)
{a > b ? a : b;
} 
//inline 将函数调用变成函数本体
//传入的是 ++a 的值
  • 对于简单常量,首选const对象或枚举,而不是#define
  • 对于类似函数的宏,优先选择内联函数

条款03:尽可能使用 const

const对指针的使用
const出现在 * 左侧:表示被指物是常量(所指向的内容不能改变)
const出现在 * 右侧:指针本身是常量 (指针不能再重新指向其他内容)char greeting[] = "hello";
char * p =greeting; //non-const指针、non-const数据
const char* p = greeting;||char const* p = greeting; //non-const指针,const数据
char* const p = greeting; //const指针,non-const数据 
const char* const p =greeting; //const指针,const数据
const对迭代器的使用
const vector<int>::iterator iter = vec.begin();
*iter = 10;  //正确
++iter; //错误,iter本身是constvector<int>:: const_iterator iter=vec.begin();
*iter=10; //错误
++iter;//正确

在成员函数上使用const

  • 使类的接口意图更明确。知道哪些函数可以修改一个对象,哪些不能。
  • 使得使用const对象成为可能

两个成员函数如果只是常量性不同,可以被重载。

class TextBlock {
public:TextBlock(string s) {text = s;}const char& operator[](size_t position) const {cout << "const版本:";return text[position];}char& operator[](size_t position) {return text[position];}private:string text;
};int main() {TextBlock tb("hello");cout << tb[0] << endl;const TextBlock ctb("world");cout << ctb[0] << endl;
}结果:
h
const版本:w-----------------------------------------------------------------
void print(const TextBlock& ctb){cout<<ctb[0]<<endl; //不论传入的实参是否为const,最终都会调用const
}
int main() {TextBlock tb("hello");const TextBlock ctb("world");print(tb);print(ctb);}
结果:
const版本:h
const版本:w--------------------------------------------------------------------
tb[0] = 'x'; //正确,写一个non-const TextBlock
ctb[0] = 'x'; //错误,写一个const TextBlock

编译器对待const对象的态度通常是 bitwise constness,而我们在编写程序时通常采用 logical constness,这就意味着,在确保客户端不会察觉的情况下,我们认为const对象中的某些成员变量应当是允许被改变的,使用关键字mutable来标记这些成员变量:

C++中mutable是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
C++中如果类的成语函数不会改变对象的状态,那么这个成员函数一般会声明成const的。但是有些时候,我们需要在const的函数里面修改一些跟类状态无关的数据成员,那么这个数据成员应该被mutable来修饰。

class CTextBlock {
public:std::size_t Length() const;private:char* pText;mutable std::size_t textLength;mutable bool lengthIsValid;
};std::size_t CTextBlock::Length() const {if (!lengthIsValid) {textLength = std::strlen(pText);    // 可以修改mutable成员变量lengthIsValid = true;               // 可以修改mutable成员变量}return textLength;
}

在重载const和non-const成员函数时,需要尽可能避免书写重复的内容,这促使我们去进行常量性转除。在大部分情况下,我们应当避免转型的出现,但在此处为了减少重复代码,转型是适当的:

class TextBlock {
public:const char& operator[](std::size_t position) const {// 假设这里有非常多的代码return text[position];}char& operator[](std::size_t position) {return const_cast<char&>(static_cast<const TextBlock&>(*this)[position]);}private:std::string text;
};

需要注意的是,反向做法:令const版本调用non-const版本以避免重复——并不被建议,一般而言const版本的限制比non-const版本的限制更多,因此这样做会带来风险。

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

条款4:确保对象在使用之前被初始化

永远在使用对象之前将其初始化。对于无任何成员内的内置类型,必须手工完成

int x = 0;
const char* text = "A C-style string";double d;
cin>>d;

对于内置类型以外的任何其他东西,初始化则由构造函数完成。确保每一个构造函数都将对象的每一个成员初始化。

构造函数比较佳的写法是,使用所谓的member initialization list替换赋值动作

class ABEntry {
public:ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones);private:string theName;string theAddress;list< PhoneNumber> thePhones;int numTimesConsulted;
};这个版本的构造函数首先调用default构造函数设初值,然后立即再对它们赋予新值。default构造函数的一切因此浪费了
ABEntry::ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones) {//这些都是赋值而非初始化theName = name;theAddress = address;thePhones = phones;numTimesConsulted = 0;
}最好写成以下形式即member initialization list替换赋值动作,效率更高。
ABEntry::ABEntry(const string& name, const string& address, const list<PhoneNumber>& phones):theName(name),theAddress(address),thePhones(phones),numTimesConsulted(0){}成员初始化列表也可以留空用来执行默认构造函数:
ABEntry::ABEntry(): theName(),theAddress(),thePhones(),numTimesConsulted(0) {}

静态对象的初始化
函数内的static对象称为local static对象,其他static对象称为non-local static对象。static对象的析构函数会在main()结束时自动调用。

C++ 对于定义于不同编译单元内的全局静态对象的初始化相对次序并无明确定义,因此,以下代码可能会出现使用未初始化静态对象的情况:

// File 1
extern FileSystem tfs;// File 2
class Directory {
public:Directory() {FileSystem disk = tfs;}
};Directory tempDir;

在上面这个例子中,你无法确保位于不同编译单元内的tfs一定在tempDir之前初始化完成。
这个问题的一个有效解决方案是采用 Meyers’ singleton,将全局静态对象转化为局部静态对象:

FileSystem& tfs() {static FileSystem fs;return fs;
}Directory& tempDir() {static Directory td;return td;
}
  • 为内置型对象进行手工初始化,因为C++不保证初始化它们
  • 构造函数最好使用成员初始列,而不要在构造函数本体内使用赋值操作。初值列列出的成员变量,其排列次序应该和他们在class中的声明次序相同。
  • 为免除“跨编译单元值初始化次序”问题,就以local static 替换 non-local static对象

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

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

相关文章

原生html和js实现瀑布流布局(macyjs插件,不依赖于jquery,纯原生)

官网地址 方式一&#xff1a;在github上找到项目&#xff0c;复制demo/assets/css/macy.css&#xff0c;以及/dist/macy.js 直接引入项目 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv&qu…

如何理解类的符号引用?类的直接引用?

在Java中&#xff0c;符号引用&#xff08;symbolic reference&#xff09;和直接引用&#xff08;direct reference&#xff09;是理解Java类加载和内存管理的重要概念。它们涉及到JVM如何在运行时处理类、方法、字段等的引用。下面是对这两个概念的详细解释&#xff1a; 符号…

junit-platform-engine旧版本无法更新问题

现象&#xff1a; 运行groovy测试类&#xff0c;一直使用的是低版本的junit-platform-engine-1.5.2.jar。即使在最外层强制升级版本也没有用 解决&#xff1a; 在最外层pom.xml引入高版本的父pom即可 <dependencyManagement><dependencies><dependency>&…

满帮集团 Eureka 和 ZooKeeper 的上云实践

作者&#xff1a;胡安祥 满帮集团&#xff0c;作为“互联网物流”的平台型企业&#xff0c;一端承接托运人运货需求&#xff0c;另一端对接货车司机&#xff0c;提升货运物流效率。2021 年美股上市&#xff0c;成为数字货运平台上市第一股。根据公司年报&#xff0c;2021 年&a…

网络协议——FTP(简介、搭建FTP服务端)

一、简介 1、什么是FTP&#xff1f; FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09; TCP/IP 协议组的协议之一。常用20&#xff08;数据&#xff09;、21&#xff08;命令&#xff09;端口作为通讯端口。&#xff08;22为SSH端口&#xff09;F…

C++ 指针占用的大小是多少

作为一个C程序员&#xff0c;我们可以详细探讨指针在C中的大小&#xff0c;以及通过代码示例来演示这一点。 C 指针占用的大小是多少 指针的大小代码示例代码解释运行结果结论 指针的大小 指针的大小主要取决于系统的架构&#xff08;如32位或64位&#xff09;和编译器的实现…

就业班 第三阶段(ELK) 2401--5.22 day3 filebeat+elk云部署

kafka集群 Windterm同步输入&#xff0c;多台机子可以同时输入同步输入 启动kafka需要启动两个 第一个 [rootkafka1 ~]# cd /usr/local/kafka_2.11-2.0.0/ [rootkafka1 ~]# nohup bin/zookeeper-server-start.sh config/zookeeper.properties &第二个 [rootkafka1 ~]#…

apache BeanUtils

一、populate 1、介绍 BeanUtils.populate(Object bean, Map properties) 方法实在org.apache.commons.beanutils.BeanUtils包下的一个一个方法。 该方法的方法头 此方法中&#xff0c;有两个参数&#xff0c;Object bean 为一个实体类&#xff0c;Map properties为一个map集…

20232810 肖峰 2023-2024-2 《网络攻防实践》实验十一

一、实践内容 &#xff08;1&#xff09;web浏览器渗透攻击 任务&#xff1a;使用攻击机和Windows靶机进行浏览器渗透攻击实验&#xff0c;体验网页木马构造及实施浏览器攻击的实际过程。 实验步骤&#xff1a; ①选择使用Metasploit中的MS06-014渗透攻击模块 ②选择PAYLOAD为任…

Linux 36.3@Jetson Orin Nano之系统安装

Linux 36.3Jetson Orin Nano之系统安装 1. 源由2. 命令行烧录Step 1&#xff1a;下载Linux 36.3安装程序Step 2&#xff1a;下载Linux 36.3根文件系统Step 3&#xff1a;解压Linux 36.3安装程序Step 4&#xff1a;解压Linux 36.3根文件系统Step 5&#xff1a;安装应用程序Step …

# Mybatis 高级用法和tk.mybatis使用

Mybatis 高级用法和tk.mybatis使用 文章目录 Mybatis 高级用法和tk.mybatis使用使用SelectProvider、InsertProvider、UpdateProvider、DeleteProviderSelectProvider使用例子 tk.mybatis引入依赖查询实现实体映射类实体类规范 dao层调用dao 使用SelectProvider、InsertProvide…

eBay运营账号防关联成功的关键因素是什么?

一、什么是ebay eBay如今的发展现状呈现出积极且充满活力的态势。作为全球知名的在线拍卖和购物平台&#xff0c;随着全球消费者对线上购物的需求不断增长&#xff0c;这为卖家提供了广阔的市场空间和盈利机会&#xff0c;但多账号的运营若处理不好容易引起账号被关联&#xf…

基于生命周期评价法的农田温室气体排放估算;农田CH4和N2O排放模拟;农田碳库模型和土壤呼吸等

目录 专题一 温室气体排放模拟研究 专题二 农田CH4和N2O排放模拟 专题三 农田碳库模型和土壤呼吸 专题四 基于生命周期评价法的农田温室气体排放估算 专题五-六 基于过程模型的温室气体排放模拟 专题七 案例模拟与疑难解答 更多应用 农业是甲烷&#xff08;CH4&#xff…

前端面试题日常练-day37 【面试题】

题目 希望这些选择题能够帮助您进行前端面试的准备&#xff0c;答案在文末。 1. 在jQuery中&#xff0c;以下哪个方法用于隐藏一个元素&#xff1f; a) .hide() b) .remove() c) .toggle() d) .fadeIn() 2. 哪个jQuery方法用于在元素的前面插入新的HTML内容&#xff1f; a…

全球前五!ATFX 2024年Q1业绩狂飙,6240亿美元交易量彰显实力

5月&#xff0c;密集发布的报告显示&#xff0c;强者恒强是差价合约行业不变的竞争逻辑。而ATFX最新展现的业绩无疑是这一逻辑的有力例证。依照惯例&#xff0c;知名行业媒体Finance Magnates日前公布了全球经纪商最为关注的2024年第一季度行业报告。报告数据显示&#xff0c;A…

如何使用Java中的PreparedStatement来防止SQL注入

SQL注入的原理 SQL注入是一种常见的网络攻击方式&#xff0c;攻击者通过在应用程序的输入字段中插入恶意的SQL代码&#xff0c;利用程序对用户输入数据的合法性没有判断或过滤不严的漏洞&#xff0c;欺骗数据库执行非授权的任意查询&#xff0c;从而获取、修改、删除或添加数据…

数据结构算法-堆(Heap)和优先队列

堆的概念 堆&#xff08;heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树的数组对象。堆总是满足下列性质&#xff1a; always greater than its child node/s and the key of the root node is the largest among all other nodes. This property…

第53期|GPTSecurity周报

GPTSecurity是一个涵盖了前沿学术研究和实践经验分享的社区&#xff0c;集成了生成预训练Transformer&#xff08;GPT&#xff09;、人工智能生成内容&#xff08;AIGC&#xff09;以及大语言模型&#xff08;LLM&#xff09;等安全领域应用的知识。在这里&#xff0c;您可以找…

Python 全栈体系【四阶】(五十三)

第五章 深度学习 十二、光学字符识别&#xff08;OCR&#xff09; 2. 文字检测技术 2.3 DB&#xff08;2020&#xff09; DB全称是Differentiable Binarization&#xff08;可微分二值化&#xff09;&#xff0c;是近年提出的利用图像分割方法进行文字检测的模型。前文所提…

Git原理及常用命令小结——实用版(ing......)、Git设置用户名邮箱

Git基本认识 Git把数据看作是对小型文件系统的一组快照&#xff0c;每次提交更新&#xff0c;或在Git中保存项目状态时&#xff0c;Git主要对当时的全部文件制作一个快照并保存这个快照的索引。同时&#xff0c;为了提高效率&#xff0c;如果文件没有被修改&#xff0c;Git不再…