读书3步,先通读,然后读厚,最后读薄。
Item 1视c++为一个语言联盟
c++的发展历史
一开始c++,只是想在c的基础上引入对象概念,最初的名字是C with Classes
。
随着c++的成熟,it grew bolder and more adventurous
,他变得越来越大胆和冒险。引入了很多C with Classes
之外的理念。作者举了3个例子:
- 异常: Exceptions required different approaches to structuring functions(see Item 29).如何理解这句话呢,仔细阅读Item29之后回头过来理解,是在说,异常要求不同的实现方式去实现函数。因为异常会导致函数有时候执行到一半,就因为抛出异常而提前退出了函数,如果你用来释放资源的代码正好因为异常没有执行到,那就会导致内存泄露。这时候设计函数的时候,考虑资源释放时就要使用资源管理对象了。
- 模板:是一种可以忽略数据类型的设计方法,而且通过编译期间的模板具现化过程可以改善一些程序设计。这个是通常程序设计不会考虑到的思路。发展到极致就是模板的元编程。比如利用元编程在编译期间就可以把代码中要用到的所有求幂运算提前计算完毕。而不需要到了执行期在运算。(可以参看Item48)
- STL:
the STL defined an approach to extensibility unlike any most people had ever seen.
作者写书的时候是91年,编程语言发展到现在,估计这句话不一定合适了。不过STL是对模板的进一步升华。模板让函数或类数据无视数据类型,STL剥离数据和算法,进一步让算法无视数据类型。
C++支持的编程方式
- 基于过程编程, 典型的c语言应用
- 基于对象编程,典型的
C with Classes
应用 - 函数形式编程
函数形式编程不是新的概念,称神的编辑器之一emacs就是LISP语言用函数编程思路写的。不过这种编程思路我接触的不多。偶尔能见到函数式编程思路的一些应用,但全部应用函数式思路编写项目的情况没有出现过。函数式编程可以把函数当成变量传来传去,可以当高阶函数的入参,也可当高阶函数的返回。函数式编程要求程序是由一个个函数组成,每个函数都不允许修改外部变量。这一要求天生有利于多线程的开发。 - 泛型形式编程
利用模板可以泛化对入参的类型要求,编译过程中只要入参对象能够支持函数内的实现即可。如果是模板类,那编译过程中会根据不同的模板参数具现化出不同的模板类。 - 元编程形式
利用模板具现化的编译过程,完成功能实现。让有些功能在编译过程中实现的思路。
C++支持这么多的编程形式,不同的变成方式下,一些高效率的原则不同,这就使得c++很难掌握,至少在不同的编程理念下,要应用不同的设计原则。
“c++语言联盟”下主要的4种
不同的编程理念下,要应用不同的设计原则。C++又支持很多的编程理念,这对掌握C++提出了很高的要求。Scott Meyers提炼了4种主要的编程理念分类,相当于提炼简化了下C++的学习重点和难度。
- C
- Object-Oriented C++
- Template C++
- STL
在全书看完后会发现,这本书主要还是介绍后三者的技巧。而针对C的很少。大部分提及的时候是为了介绍C++的一些特殊规则由来(是因为兼容C的需要)。C++是在C的基础上发展起来的,这个是大前提。对C的优化一样适用于C++,但是如果使用C++有但C没有的特殊部分,就要按照C++的优化规则来。如果使用Template C++,就要按照Template C++的优化规则。一样的,使用STL的时候也有一些特殊的守则。
举几个例子来说明上面的描述:
预处理器是C上就有的概念,在C代码中我们习以为常的使用#define AAA ()
,然后让预处理器处理时进行全文替换。但到了C++领域,虽然依然可以使用#define AAA ()
但是却不推荐使用,原因之一是#define
是全局的在一个文件中写下,那么该文件位置以下的任何代码都可以引用AAA
。C++领域讲究数据的封装性,如果只是希望个别类能够使用AAA
,就不适合使用#define
方式了。
在Object-oriented C++领域上的类继承,与在Template C++领域上的类继承也是有区别的。在条款43上就介绍了这种情况。在Template C++领域,在继承类里面如果要调用基类的函数,还需要特殊处理。STL的核心是容器,迭代器和算法。模板技术让我们的函数和类可以忽略数据类型。STL则更进一步,让算法跟数据类型无关。STL中算法通过迭代器沟通容器。
Scott Meyers在书中以“传值好还是传引用好”为例告诉读者这取决于你所使用的C++语言子集。这个会在Item20中详细说明。
对Item1的引申
C++对函数式编程的支持,这里只局一个简单的例子:
C++的高阶函数for_each
transform
可以接受lambda表达式或者函数做入参。
void printItems(char x)
{
std::cout<<""<<x<<endl;
}
int plusOne(int x)
{
return (x+1);
}
int main()
{
std::list<int>alist = {1,2,3,4};
std:for_each(alist.begin(),alist.end(),printItems);
std::transform(alist.begin(),alist.end(),alist.begin(),plusOne);
}
通过查看for_each
的定义,可以知道他的第三个入参是函数。对这个入参函数的要求是只要这个函数接受一个入参,接受的这个入参类型和alist里面的成员类型并不要求一定要一样,只要能隐式转换成功就行。在for_each
执行过程中,它遍历到alist的某个元素时,会把这个元素当入参丢入printIItems,并执行一下printItems函数。
transform,他的定义时遍历到alist的某个元素时,把这个元素当入参丢入plusOne,执行的返回,丢入第三个入参定义的迭代器指向的位置,同时更新该迭代器+1。
网友评论