《EffectiveC++》读书笔记(一)条款1-3

本文探讨了C++作为一门融合多种编程范式的语言的特点,并强调了在C++中使用const、枚举和内联函数代替宏定义的重要性。此外,还讨论了const在提升代码质量和维护性方面的作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

想一想C++也学了大半年但博客没怎么写过C++方面的。一是当初自己看《C++ Primer》很懵,写博客也只是抄书,二是后来代码量上去了,踩了各种坑之后也收获的很多,更多的都是要牢牢记住的基础用法,没啥整理的必要。。。

对于C++的学习,《Effective C++》绝对是一本必看的书,但又不是和《C++ Primer》同时看,而是在有了一定的C++基础和代码量之后,对于基础语法,知识点都有所掌握,但是不知道如何正确使用C++时,再深入阅读,这时收获更多。

同时,这本书所讲述的并不是一些规定,而是有效运用C++的方法,所以需要仔细的理解和体会,因此,希望通过整理读书笔记深入理解其内容。

正文

Item 1: View C++ as a federation of Languages

这里侯捷老师翻译为“视C++为一个语言联邦”,我觉得确实很正规,也很准确,但是对于我们理解上可能并不容易(联邦。。。)。

C++,有很多人爱它,但好像更多人Diss它,在国内遍地Java培训机构,但却少有C++的培训班,我想有很大一部分原因就是C++的内容太多了,不仅向现代高级语言看齐,又要兼容古老的C。

我们可以把它分为四大部分:
1。 C,C++的设计就是要和C兼容,以至于很多地方做的很XX,不如Java等方便,但是好处就是和OS更加亲近了。
2。OO 也就是从C到C++最大的不同吧,有class,有继承,多 态,封装。
3。泛型,主要就是模板(template),对于写库来说是个重头戏。
4。STL,一门编程语言想要流行开来必须要有一套高效,便利的轮子(库),当然对学习者来说也是优秀代码的宝库,STL符合这两点,(iostream可能。。。)。

在我们实际使用C++时往往只会用其中的一部分,比如ACMer可能就是C部分加上STL,而在实际工程中可能主要是OO,库的作者则主要是template,所以C++就像一个大工具箱,选好你适合的工具,当然,还要遵从工具的使用规约。。。

Item 2: Prefer consts,enums,inlines to #defines

记得C语言课上,讲到#define时老师往往会说把多次出现的常量用#define定义(比如PI),那么为什么不用const呢(黑线)?

使用const的好处如下:
1.调试更加方便,由于宏是在预处理阶段被CPP展开,所以也许编译器并不知道,如果你在使用这个常量的过程中遇到问题,你完全是蒙蔽的,尤其是使用到别人的代码时。
一个形如9.8的常量你可能并不知道有什么特殊意义,但是一个const 常量是由编译器处理的,将被记录到symbol table里,方便我们之后的调试(能看到变量名可以定位bug发生点)。

2.减少目标代码长度,盲目地将宏替换可能导致目标代码会出现多份,而一个const常量则不会。(这里不是很懂为什么宏替换会出现多份)

同时,对于C++里的常量,#define有其无法完成之事,即限定作用域(类作用域)的常量,也就是说对于类内的专属常量,#define并不能限定其作用域。
我们当然可以通过#undef这种机制来限制宏的作用域,但是对于一个类内的常量确无法做到。
而const则可以。

当我们需要用到这个这个常量时必须要加上作用域运算符,这就限制类常量的作用域。

值得一提的是,作为一个static成员变量,我们知道其需要在类内声明,在类外定义,这样才保证同一个类的多个对象共享一个static成员变量。
然而对于static const int/char/bool 类型的常量可以直接在类内定义(书上说是较新的编译器,但本书成于2005年。。。)

class A{
public:
  static const int size = 2; //正确
  static const float Pi = 2.3;//错误
};

但如果你需要的是类内常量是在类内使用或者你的编译器真的不支持这种in class定义,就需要enum出场了。

class A{
public:
  //static const int size = 2; 
  enum{ 
      size = 2//通过enum来设置类内使用的类内常量
  };
  char myarray[size]
};

enum所定义的常量名和const一样,也进入symbol table,方便我们调试。

说起enum,还有一个优点,不过这也是针对旧或者“不够优秀”的编译器,便是enum中的常量不能被&或者被引用绑定,const则可能,但我在自己的g++测试时,const常量也无法被引用绑定或者&,所以感觉enum的好处,enmmmm基本被const替代。

而老师讲到#define时,可能还会说到另一种用法

#define CALL_WITH_MAX(a, b) f((a) > (b) ? (a) : (b)) // 调用函数f,将ab之中的较大值作为参数调用f

小组的面试也出过这样的问题,当然,我们都知道这样的宏会带来怎样的副作用(传入一个i++)。

这里的#define是为了,呃,实现C语言中的泛型,and代码很短不用编写成函数,但是我们来到C++了,对于泛型,我们有template,而很短的代码则编写为就地展开的inline函数。

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

同样,当这样的函数变为类内专属时,#define一样无能为力。

但是CPP还有其不可替代的作用,包括#ifdef,包括与C兼容,包括看ACMer的代码总会一大堆宏。。。

Item 3: Use const whenever possible

const确实还是很好用的,当你需要规定一个东西为常量时,设置成const就让编译器替你做检查了。

对于重载的运算符,我们让其返回的对象为const可以避免一些无谓的错误。

const Object operator*(Obeject &a, Object &b)

// 一个手误的操作,将因为const被制止
if((a*b) = c)

C++的重载机制支持对于参数有无const的重载,和调用对象是否为const的重载。

int fun(char *a)
{
    return 2;
}
int fun(const char *a) //若两种都有,则能重载,若只有const 参数版本,则按照C风格可以传非const参数
{
    return 1;
}

class A{
    // 即按照调用对象是否为const重载
    const char &operator[](int b) const
    {
        return test[b];
    }
    char &operator[](string t)  
    {
        int a =2;
        return test[a];
    }
    string test;
};

需要注意的是,这里能调用const成员函数的const对象,指的是bitwise constness,也就是对于对象的每一个成员变量都无法改变其任何一个bit。

但若对象有一个指针变量呢?其指向的对象是否视为本对象的一部分呢?

所以const对象也可以有变化的成员变量,通过mutable关键字声明。

而const成员函数和non-const成员函数往往只有些许不同,所以我们可以只编写const成员函数,而对于non-const成员函数通过调用const成员函数+cast。

一个例子如下

const Object &operator[](std::size) const
{
    // 老实实现
}
Object &opeartor[](std::size pos)
{
    //static_case<const Object>(*this)[pos] 是将non-const对象转为const对象来调用const成员函数
    //const_cast将其返回的const Object &的const 移除,来符合函数的返回值类型
    return const_cast<Object &>(static_cast<const Object>(*this)[])
}

但是坚决不要通过const成员函数调用non-const成员函数(你懂的)。

资源下载链接为: https://pan.quark.cn/s/1bfadf00ae14 “STC单片机电压测量”是个以STC系列单片机为基础的电压检测应用案例,它涵盖了硬件电路设计、软件编程以及数据处理等核心知识点。STC单片机凭借其低功耗、高性价比和丰富的I/O接口,在电子工程领域得到了广泛应用。 STC是Specialized Technology Corporation的缩写,该公司的单片机基于8051内核,具备内部振荡器、高速运算能力、ISP(在系统编程)和IAP(在应用编程)功能,非常适合用于各种嵌入式控制系统。 在源代码方面,“浅雪”风格的代码通常简洁易懂,非常适合初学者学习。其中,“main.c”文件是程序的入口,包含了电压测量的核心逻辑;“STARTUP.A51”是启动代码,负责初始化单片机的硬件环境;“电压测量_uvopt.bak”和“电压测量_uvproj.bak”可能是Keil编译器的配置文件备份,用于设置编译选项和项目配置。 对于3S锂电池电压测量,3S锂电池由三节锂离子电池串联而成,标称电压为11.1V。测量时需要考虑电池的串联特性,通过分压电路将高电压转换为单片机可接受的范围,并实时监控,防止过充或过放,以确保电池的安全和寿命。 在电压测量电路设计中,“电压测量.lnp”文件可能包含电路布局信息,而“.hex”文件是编译后的机器码,用于烧录到单片机中。电路中通常会使用ADC(模拟数字转换器)将模拟电压信号转换为数字信号供单片机处理。 在软件编程方面,“StringData.h”文件可能包含程序中使用的字符串常量和数据结构定义。处理电压数据时,可能涉及浮点数运算,需要了解STC单片机对浮点数的支持情况,以及如何高效地存储和显示电压值。 用户界面方面,“电压测量.uvgui.kidd”可能是用户界面的配置文件,用于显示测量结果。在嵌入式系统中,用
资源下载链接为: https://pan.quark.cn/s/abbae039bf2a 在 Android 开发中,Fragment 是界面的个模块化组件,可用于在 Activity 中灵活地添加、删除或替换。将 ListView 集成到 Fragment 中,能够实现数据的动态加载与列表形式展示,对于构建复杂且交互丰富的界面非常有帮助。本文将详细介绍如何在 Fragment 中使用 ListView。 首先,需要在 Fragment 的布局文件中添加 ListView 的 XML 定义。个基本的 ListView 元素代码如下: 接着,创建适配器来填充 ListView 的数据。通常会使用 BaseAdapter 的子类,如 ArrayAdapter 或自定义适配器。例如,创建个简单的 MyListAdapter,继承自 ArrayAdapter,并在构造函数中传入数据集: 在 Fragment 的 onCreateView 或 onActivityCreated 方法中,实例化 ListView 和适配器,并将适配器设置到 ListView 上: 为了提升用户体验,可以为 ListView 设置点击事件监听器: 性能优化也是关键。设置 ListView 的 android:cacheColorHint 属性可提升滚动流畅度。在 getView 方法中复用 convertView,可减少视图创建,提升性能。对于复杂需求,如异步加载数据,可使用 LoaderManager 和 CursorLoader,这能更好地管理数据加载,避免内存泄漏,支持数据变更时自动刷新。 总结来说,Fragment 中的 ListView 使用涉及布局设计、适配器创建与定制、数据绑定及事件监听。掌握这些步骤,可构建功能强大的应用。实际开发中,还需优化 ListView 性能,确保应用流畅运
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值