essential c++ 读书笔记

essential c++

这是第一次使用这个软件做笔记,希望好用。打开了我许久未使用的visual studio。

第一章

  1. 第一节helloworld程序,很适合新手入门,对我来说像是看幼儿故事。
  2. 用一个更复杂的需求,引出了对象的定义和初始化。这里面提到的内容加了一点难度,比如构造函数初始化(constructor initialization),模板类(template class),转义字符,const常量。
  3. 介绍常用运算符。逻辑运算”||”和“&&”的短路求值法是个小技巧。对于运算优先级加括号可能是最好的方式。
  4. 介绍条件和循环语句。我认为学一个语法最重要就是搞懂这两种语句的写法就好了。条件语句的嵌套与逻辑组织是一个难点,需要仔细梳理。同样的,循环语句中break和continue的循环控制也要小心。
  5. 简单介绍数组array和vector。补充了前面没说的for循环。
  6. 介绍了指针概念。指针数组,随机函数。
  7. 介绍文件读写。文件操作一直是我不熟悉的内容,有复习到。同时读写文件需要使用到函数还是比较复杂的,书中没有详细介绍。
  8. 练习题部分。1.5对比了string类对象和c-style字符串,这里提到的cin循环输入有个bug,需要参考下面链接。(28条消息) C++ while(cin>>a) cin输入直到回车结束_子木呀的博客-CSDN博客_while(cin>>)什么时候结束。1.6对比了array和vector。1.7提到了泛型算法sort,一个好的习惯是先把输入输出文件打开后,再进行后面的计算。1.8强调了保证程序健壮性的重要。

第二章

  1. 介绍了函数编写的基础知识。着重强调了程序健壮性,可以采取的方法包括:1)终止程序、2)抛出异常、3)信任程序员(“信任”不是良好的工程原则)。一种编写函数的方式是利用返回值表示是否正确执行函数,参数列表做输入和输出。为了健壮性需要考虑的问题很多,比如输入参数是否合理、是否会数值溢出。
  2. 介绍调用函数。传值(by value)传址(by reference),我觉得应该是叫传引用,还有一个传地址。然后介绍了reference引用的语法,与指针point的对比。小技巧,利用const修饰函数入口参数,防止被更改;传址方式一般用于对象类型,而不是内置类型参数。然后介绍了作用域概念。
  3. 介绍默认参数值。一个良好的编程习惯是尽量不要用全局变量。然后介绍了关于默认值设置的一些语法和规则,并不是全部都有。注意默认值应该放在声明处而不是定义处。
  4. 介绍局部静态变量,强调了尽量不使用全局变量。
  5. 介绍了inline内联函数,定义放在头文件中。需要注意是否内联与编译器有关。
  6. 介绍了重载函数。让用户更加方便,避免记住太多名字。
  7. 介绍了函数模板,避免编写太多功能类似参数类型不同的函数。使用上和一般函数一致。之后对比了函数重载和函数模板。
  8. 介绍了函数指针。使用指针前对其做检验是一个好习惯,包括函数指针。还顺便介绍了枚举类型。
  9. 介绍了头文件写法。包括函数的声明(内联函数需要有定义),全局变量的声明(通过加extern,const变量是个例外)。说明了<>和“”头文件的区别。
  10. 习题就是对内容的复习。

第三章

本章以STL库为主要内容,介绍泛型编程。

  1. 用一个十分精妙的例子,说明了迭代器的作用,统一了泛型算法在vector,list,数组上的操作。例子只说了这顺序容器与数组,至于关联容器没提。(我猜关联容器和顺序容器使用场景差别大,可能用到的算法也不一样)

  2. 介绍iterator class的使用方法,实现原理在下一章。需要牢记定义与生产的方法。vector::iterator iter=vec.begin();

    对于常量容器,应该使用下面的定义防止写入。

    vector::const_iterator cs_iter=cs_vec.begin();

    利用模板函数、迭代器,实现了一个功能强大的find函数,可以对多种类型的容器或数字,以及多种内置类型数据进行查找操作。

    为了进一步扩充功能,实现对自定义类的相等equality判断,可以使用函数指针或是function object(没听说过)。这样下来就可以称之为泛型算法了。

    最后总结了常用的泛型算法包括那些。

  3. 介绍所有容器的共通操作。

    (28条消息) vector插入和删除_实力比头发更重要的博客-CSDN博客

    (28条消息) vector的remove和erase函数的区别_代码码xzy的博客-CSDN博客_qvector remove

  4. 介绍如何使用顺序性容器,包括vector,list,deque。vector使用连续内存存储,随机访问效率高,随机插入删除(非队尾)效率低。list使用双向链表,随机插入删除效率高,随机访问效率低。两者适合不同的使用场景!deque(double ended queue)在两端的插入删除都很快,queue类型都是在此基础上实现的。然后介绍了常用的操作函数,包括如何定义,对尾端(首端)元素的处理,插入和删除操作详解。

    迭代器也有类型(28条消息) C++的五类迭代器_algsup的博客-CSDN博客_c++ 迭代器类型

  5. 介绍如何使用泛型算法,需要包含头文件。搜索算法介绍了四种,find()、binary_search()、count()、search()。简单说了复制函数copy(),需要程序员确保目的地空间足够,排序函数sort()。

  6. 介绍如何设计泛型算法。在第二小节中也提到,为了使一个函数功能更加强大,可以考虑用函数指针做参数,这里介绍了一个叫做function object的东西,它是某种类的实例对象。相比于前种方法实现了效率上的提升,避免了函数调用。进一步又介绍了function object adapter适配器,通过绑定适配器可以将原来的二元运算符与一个用户的数值绑定,变成一个一元的运算符。最后综合了之前所说的全部技巧,包括函数模板,iterator迭代器,function object对象,适配器,形成了一个功能强大的filter函数。最后又补充了一个negator的适配器,一个新的排序后删除思路的filter。

    这一讲学习学到了很多东西,但也有很多还不清楚底层原理,只是知道了如何去使用。比如迭代器类的实现原理,function object的实现原理,适配器的实现原理。vector<>这样形式的语法原理。而且感觉讲的东西十分抽象,实际应用中这些东西在哪里能发挥作用还不清楚。

    [C++]STL 模板(一) - 哔哩哔哩 (bilibili.com)

    最后测试了编写新思路的filter,算是复习一下。

    #include <iostream>
    #include <vector>
    #include <functional>
    #include <algorithm>
    using namespace std;
    
    template<typename InputIterator, typename OutputIterator, typename ElemType, typename Comp>
    OutputIterator sub_vec(InputIterator first, InputIterator last, OutputIterator at, const ElemType& val, Comp pred) {
    	sort(first, last, pred);
    	InputIterator posIter = find_if(first, last, not1(bind2nd(pred, val)));
    	copy(first, posIter, at);
    	return at;
    	
    }
    template<typename IteratorType>
    void display(IteratorType first, IteratorType last) {
    	while (first!=last)
    	{
    		cout << *first << " ";
    		first++;
    	}
    	cout << endl;
    } 
    int main(void)
    {
    	const int elem_size = 8;
    	int ia[elem_size] = { 12,8,43,0,6,21,3,7 };
    	vector<int> ivec(ia, ia + elem_size);
    
    	int ia2[elem_size];
    	vector<int> ivec2(elem_size);
    	cout << "<8 " << endl;
    	sub_vec(ia, ia + elem_size, ia2, 8, less<int> ());
    	display(ia2, ia2 + elem_size);
    	cout << ">8" << endl;
    	sub_vec(ivec.begin(), ivec.end(), ivec2.begin(), 8, greater<int>());
    	display(ivec2.begin(), ivec2.end());
    
    	return 0;
    }
    
  7. 介绍如何使用map。直接使用不存在的键值对会直接初始化一个。map也存在迭代器。利用map的成员first可以获得键key,成员second可以获得值value。查找键值对可以使用成员函数find,注意返回值是一个指向pair class的迭代器。还有一种方式是利用count函数获取该key在map中的项数(这里提到了可以保存多个相同key值的multimap,但不是重点)。

  8. 介绍如何使用set。包括初始化,插入。c++set容器插入与删除 - 纸包鱼 - 博客园 (cnblogs.com)

  9. 为了解决之前程序中,要求目的端容器空间必须足够大的缺点。使用insertion adapter插入适配器,一共有三种。修改filter使用的是back_inserter(应该是一个对象,对原有的容器进行再包装,就实现了对赋值运算符=的重载,我的理解)。不理解实现的语法原理是什么,可能得留给下一章。

  10. 介绍了如何使用iostream iterator实现对标准输入输出的操作(我觉得cin流中没有eof,像我之前测试的那样,这种方式应该不好用c语言中eof怎么打出来,怎么输入eof_IT教学网 (itjxue.com))这个连接有可能解决,但我没试了。然后指出该方法也可以用于文件的读入读出,这部分可能有用。但是一些错误输入能够处理吗?比如需要int输入了字符串?

  11. 编程题目。3.1没啥好说的。3.2编写function object没搞懂,还有后面的stable_sort()函数也不懂,留给下一章。3.3的数据结构map<string,vector>有点优秀,是我没想到的,而且对文件和字符的处理操作有可借鉴的地方。3.4用到了一些新的泛型算法,同样对于类定义我都不太懂。

第四章

基于对象的编程风格,首先介绍了关于类对象class object的一些直观认识。

  1. 抽象(abstraction)。类的定义包括了public和private,public定义成员函数,private定义私有的成员函数和数据成员。例题4.2提到了全局作用域解析符::这个很重要。
  2. 介绍构造函数和析构函数。(提到了一个用于类间贡献数据的方法,再下一节介绍)构造函数没有返回类型,可以被重载。(不要相信“用户永远是对的”)。默认构造函数可以没有参数,或是全是提供了默认参数。成员初始化列表方式,有些成员必须用这种方式。析构函数没有参数不可以重载,没有返回值,用于处理善后。(有什么需要善后呢?)介绍了复制构造函数,赋值操作符重载。
  3. 介绍const成员函数。(28条消息) const参数,const返回值与const函数_函数返回值const_wfu的博客-CSDN博客我觉得博客中的第二种基本遇不到。(28条消息) 引用作为函数的返回值_引用作为函数返回值_Cherry_keven的博客-CSDN博客C++函数中返回引用和返回值的区别 - J.M.Liu - 博客园 (cnblogs.com)省流,可以避免函数返回值的临时变量。这导致了一个const 引用返回值卡bug的问题,书里提到了。(28条消息) C++的const类成员函数_haozlee的博客-CSDN博客。介绍了mutable关键词,机制还是很复杂的,可以简单理解为在const对象和非const对象间的一个折中的方案。
  4. 介绍this指针。(设计copy函数时可以用this检查是不是同一个对象)(虽然不清楚copy函数去返回值有什么意义?)
  5. 介绍静态类成员。C++ 类的静态成员 | 菜鸟教程 (runoob.com)注意初始化位置。介绍静态成员函数,不需要对象就可以调用,不过里面的变量也得是静态类成员。(28条消息) C++ const static成员的初始化方法总结_c++ const static 初始化_小拖漫漫技术路的博客-CSDN博客
  6. 介绍运算符重载,以iterator为例子。iterator类利用了friend机制去访问别的类的元素。运算符重载有一些规则。指向成员的指针运算符:“.”和“->” | Microsoft Learn。运算符重载可以是成员函数,也可以作为非成员函数,不过这个时候参数历表里面一定有一个类参数,相当于成员函数里面隐藏的this指针。递增和递减运算符参数不同是为了解决规则要求的变通技巧(编译器会处理),返回值不同是为了和函数内容配合解决先使用还是先增加的问题。为了联系起来iterator类和triangle类,需要嵌套类型。利用typedef可以在triangle类内定义一个好看的名字iterator。
  7. 利用friend机制实现普通函数访问类的私有变量和函数。包括friend单个函数,friend一个类。 这一机制是为了效率更高。
  8. 介绍重载等号赋值运算符。(提到了一个异常发生时的安全问题,不是很懂)
  9. 实现function object。(一个带有模板的类,利用inline函数返回比较结果。一些泛型算法会使用到这种类作为参数。)(我们的目标是实现一个同样可以被泛型算法接受的类吗?现在我只是知道用法而不清楚细节需要包括什么)。首先介绍了function call运算符(),这是我从来没想到过的。还是不懂实现原理是什么?(28条消息) find_if函数_没有金刚钻,也木有刷子的博客-CSDN博客 我懂了,相当于调用函数时给了一个函数指针一样,所以调用时候要一个()。
  10. 介绍重载输入输出运算符。注意是重载为非成员函数!这里没有考虑读入错误的问题。
  11. 最后介绍了指向类成员的指针,两个新的运算符。
  12. 习题4.5函数的声明很有技巧。

第五章

面对对象编程:封装、继承和多态是三个重要概念。前面提到了封装,现在开始最后两个。

  1. 介绍面向对象编程的概念。许多东西得看过后才有印象和体会,比如里面提到的继承体系,抽象基类,指向抽象基类的指针和引用,动态绑定等。注意,多态特性需要利用指针pointer和引用reference来实现。

  2. 介绍编程思维。实现多态需要虚函数,关键字virtual。C++构造函数和析构函数可以是虚函数吗 - 知乎 (zhihu.com)。在派生类被定义时,先执行基类构造函数,再执行派生类构造函数,析构式同理。本书只介绍public继承,说明其他的继承方式不重要。介绍了protected关键字。C++三种继承方式 (biancheng.net)(28条消息) C++学习笔记 —— 继承同名变量于多重继承_c++同名变量继承_Charles Ray的博客-CSDN博客。(多重继承?虚继承?是个大坑,不用看了)。

    为了搞清楚virtual关键字的用法,写了一个小程序测试。说明只要函数入口的类是virtual,就会一直使用最新的函数,实现多态。

    #include <iostream>
    using namespace std;
    class A{
    public:
    	void print() {
    		cout << "i am a" << endl;
    	}
    };
    class B :public A{
    public:
    	virtual void print() {
    		cout << "i am b" << endl;
    	}
    };
    class C :public B{
    public:
    	void print() {
    		cout << "i am c" << endl;
    	}
    };
    class D :public C{
    public:
    	void print() {
    		cout << "i am d" << endl;
    	}
    };
    void test(A& t) {
    	t.print();
    }
    int main(void)
    {
    	A aa; B bb; C cc; D dd;
    	test(aa); test(bb); test(cc); test(dd);
    	return 0;
    }
    
  3. 介绍了利用编程技巧实现多态。这是一种很麻烦的多态,利用一些flag表示对象当前是什么状态,表达的是一种并列类型的对象,没有层级关系。提到了static_cast.C++强制类型转换操作符 static_cast - melonstreet - 博客园 (cnblogs.com)

  4. 介绍如何去设计一个抽象基类。第一步:找出所有子类类的共同操作函数;第二步,思考那些操作函数是与子类类型相关,需要声明为虚函数的;第三步,确定成员的访问层级,是public,protected,private。介绍了纯虚函数,一个准则(只要类里面有虚函数,就要使用虚的析构函数),一个小tip(不要使用纯虚析构?)

  5. 介绍如何编写派生类。(28条消息) 静态(static)成员的继承_static成员的继承_theusProme的博客-CSDN博客。在设计的过程中,如果遇到不合适的地方,会需要回过头来重新设计基类的接口函数。!!!设计是一个长期迭代演进的过程!!!详细介绍了public继承的规则。介绍了如何利用域运算符::跳过虚函数的动态绑定机制。然后又讨论了基类函数应不应该声明为virtual(每太看懂,我感觉需要多态的都加吧)。

  6. 介绍如何使用设计好的两层继承体系。非常方便,难点在于设计。

  7. 介绍一个改版的继承体系,将一些共用代码放到了基类里面。(小细节,成员变量是引用的时候,必须在构造函数初始化)构造函数定义为private或者protected的好处_C 语言_脚本之家 (jb51.net)

  8. 在上一节的基础上,完善了初始化、析构、复制的功能。(复制是我没有想到需要去做的,还有重载赋值运算符)

  9. 介绍了派生类覆盖虚函数时的注意事项。虚函数的静态解析!有些时候虚函数不是按预期行为工作的。

  10. 介绍了一个新的概念,运行时的类型鉴定机制(以前没看过)。RTTI(run time type identification),tpyeid运算符(28条消息) C++ typeid关键字详解_CHENG Jian的博客-CSDN博客。type_info对象。介绍了static_cast和dynamic_cast。

  11. 习题。5.1提到了迭代器的类型C++迭代器(STL迭代器)iterator详解 (biancheng.net)。5.2提到具象基类,就不是纯虚的基类。5.3强调了继承是一种is-a的关系。5.4继承关系是需要自己组织,自己设计基类的。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值