基础知识

函数必须先被声明,然后才能被调用(被使用)。函数的声明让编译器得以检查后续出现的使用方式是否正确——是否有足够的参数、参数类型是否正确,等等。函数声明不必提供函数体,但必须指明返回类型、函数名,以及参数列表。此即所谓的函数原型(function prototype)。

如果函数的返回类型不为void,那么它必须在每个可能退出点上将值返回。函数中的每条return语句都被用来明确表示该处就是函数的退出点。如果函数体最后一条语句不是return,那么最后一条语句之后便是该函数的隐式退出点。

当我们以by reference方式将对象作为函数参数传入时,对象本身不会复制出另一份——复制的是对象的地址。函数中对该对象进行的任何操作,都相对于是对传入的对象进行间接操作。将参数声明为reference的理由之一是,希望得以直接对所传入的对象进行修改;第二个理由是,降低复制大型对象的额外负担。

我们也可以使用pointer形式传递函数参数,这和以reference传递的效果相同:传递的是对象的地址,而不是整个对象的副本。但是两者用法不同。pointer参数和reference参数之间更重要的差异是,pointer可能(也可能不)指向某个实际对象。当我们提领pointer时,一定要先确定其值并非0。至于reference,则必定会代表某个对象,所以不需要做此检查。

但使用delete释放heap(堆,动态内存)分配而来的对象是,无需检验指针为零,编译器会自动进行这项检查。如果因为某种原因,程序员不想使用delete表达式,由heap分配而来的对象就永远不会被释放,这称之为memory leak(内存泄漏)。

在选择函数参数使用pointer还是reference时,我们要知道,pointer可以被设置为0,表示未指向任何一个对象,而reference一定得代表某个对象,无法被设置为0。

将函数声明为inline,表示要求编译器在每个函数调用点上,将函数的内容展开。面对一个inline函数,编译器可将该函数的调用操作改为以一份函数代码副本代替,这将使我们获得性能改善。将函数指定为inline,只是对编译器提出的一种要求,而没有强制性,编译器是否执行这项请求,需视编译器情况而定。

函数重载机制(function overloading),参数列表不相同(可能是参数类型不同,可能是参数个数不同)的两个或多个函数,可以拥有相同的函数名称。编译器会将调用者提供的实际参数拿来和每个重载函数的参数对比,找出其中最合适的。编译器无法根据函数返回类型来区分两个具有相同名称的函数,因为返回类型无法保证提供给我们一个足以区分不同重载函数的语境。

函数的定义只能有一份,可以有许多份声明。我们不把函数的定义放入头文件,因为同一个程序的多个代码文件可能都会包含这个头文件。“只定义一份”的规则例外:inline函数的定义,为了能扩展inline函数的内容,在每个调用点上,编译器都取得其定义,这意味着我们必须将inline函数的定义放在头文件,而不是把它放在各个不同的程序代码文件中;const object和inline函数一样,是“只定义一份”规则下的例外,const object的定义只要一出文件之外便不可见,这意味着我们可以在多个程序代码文件中加以定义,不会导致任何错误。(注意指向const object 的指针,其本身可能并不是const object)

在引用头文件时,如果头文件和包含此程序代码文件位于同一个磁盘目录下,我们便使用双引号,如果在不同的磁盘目录下,我们便使用尖括号。更有技术含量的回答是,如果此文件被认定为标准的或项目专属的头文件,我们便以尖括号将文件名括住,编译器搜索此文件时,会先在某些默认的磁盘目录中寻找;如果文件名由成对的双引号括住,此文件便被认为是一个用户提供的头文件,搜索此文件时,会由包含此文件的文件所在磁盘目录开始找起。

练习题答案

练习2.1 先前的main()只让用户输入一个位置值,然后便结束程序。如果用户想取得两个甚至更多元素值,它必须执行这个程序两次或多次。请改写main(),使它允许用户不断输入位置值,直到用户希望停止为止。

fibon_elem.h
bool fibon_elem(int pos, int& elem)
{
if (pos <= 0 || pos > 45)
{
elem = 0;
return false;
}
elem = 1;
int n_2 = 1, n_1 = 1;
for (int ix = 3;ix <= pos;++ix)
{
elem = n_2 + n_1;
n_2 = n_1;
n_1 = elem;
}
return true;
} #include <iostream>
#include "fibon_elem.h" using namespace std; extern bool fibon_elem(int, int&); int main()
{
int pos, elem;
char ch;
bool more = true;
while (more)
{
cout << "Please enter a position: ";
cin >> pos;
if (fibon_elem(pos, elem))
{
cout << "element # " << pos
<< " is " << elem << endl;
}
else
{
cout << "Sorry. Counld not calculate element # "
<< pos << endl;
}
cout << "would you like to try again? (y/n) ";
cin >> ch;
if (ch != 'y' && ch != 'Y')
{
more = false;
}
}
return 0;
}

练习2.2 Pentagonal数列的求值公式是P(n)=n(3n-1)/2,借此产生1,5,12,22,35等元素值。试定义一个函数,利用上述公式,将产生的元素放到用户传入的vector之中,元素个数由用户指定。请检查元素个数的有效性(太大可能引发overflow问题)。接下来编写第二个函数,能够将给定的vector的所有元素一一打印出来。此函数的第二参数接受一个字符串,表示存放在vector内的数列的类型。最后再写一个main(),测试上述两个函数。

#include <iostream>
#include <vector>
#include <string> using namespace std; bool calc_elements(vector<int>& vec, int pos);
void display_elems(vector<int>& vec, const string& title, ostream& os = cout); int main()
{
vector<int> pent;
int pos;
const string title("Pentagonal Numeric Series");
cout << "Please enter a position: ";
cin >> pos;
if (calc_elements(pent, pos))
{
display_elems(pent, title);
}
return 0;
} bool calc_elements(vector<int>& vec, int pos)
{
if (pos <= 0 || pos > 100)
{
cerr << "Sorry. Invaild position: " << pos << endl;
return false;
}
for (int ix = vec.size() + 1;ix <= pos;++ix)
{
vec.push_back(ix * (3 * ix - 1) / 2);
}
return true;
} void display_elems(vector<int>& vec, const string& title, ostream& os)
{
os << '\n' << title << "\n";
for (int ix = 0;ix < vec.size();++ix)
{
os << vec[ix] << '\t';
if ((ix + 1) % 10 == 0)
{
cout << endl;
}
}
os << endl;
}

练习2.3 将练习2.2的Pentagonal数列求值函数拆分为两个函数,其中之一为inline,用来检验元素个数是否合理。如果的确合理,而且尚未被计算,便执行第二个函数,执行实际的求值工作。

#include <iostream>
#include <vector>
#include <string> using namespace std; void really_calc_elems(vector<int>& , int );
inline bool calc_elems(vector<int>&, int);
void display_elems(vector<int>& vec, const string& title, int, ostream& os = cout); int main()
{
vector<int> pent;
int pos;
char ch;
bool more = true;
const string title("Pentagonal Numeric Series");
while (more)
{
cout << "Please enter a position: ";
cin >> pos;
if (calc_elems(pent, pos))
{
display_elems(pent, title, pos);
}
cout << "would you like to try again? (y/n) ";
cin >> ch;
if (ch != 'y' && ch != 'Y')
{
more = false;
}
}
return 0;
} inline bool calc_elems(vector<int>& vec, int pos)
{
if (pos <= 0 || pos > 100)
{
cerr << "Sorry. Invalid position: " << pos << endl;
return false;
}
if (vec.size() < pos)
{
really_calc_elems(vec, pos);
}
return true;
} void really_calc_elems(vector<int>& vec, int pos)
{
for (int ix = vec.size() + 1;ix <= pos;++ix)
{
vec.push_back((ix * (3 * ix - 1) / 2));
}
} void display_elems(vector<int>& vec, const string& title, int pos, ostream& os)
{
os << '\n' << title << "\n";
for (int ix = 0;ix < pos;++ix)
{
os << vec[ix] << '\t';
if ((ix + 1) % 10 == 0)
{
cout << endl;
}
}
os << endl;
}

练习2.4 写一个函数,以局部静态(local static)的vector储存Pentagonal数列元素。此函数返回一个const指针,指向该vector。如果vector的大小小于指定的元素个数,就扩充vector的大小。接下来再实现第二个函数,接受一个位置值,返回该位置上的元素。最后,编写main()测试这些函数。

#include <iostream>
#include <vector> using namespace std; inline bool check_validity(int pos);
const vector<int>* pentagonal_series(int pos);
bool pentagonal_elem(int pos, int& elem); int main()
{
int pos, elem;
char ch;
bool more = true;
while (more)
{
cout << "Please enter a position: ";
cin >> pos;
if (pentagonal_elem(pos, elem))
{
cout << "element " << pos << " is " << elem << endl;
}
cout << "would you like to continue? (y/n) ";
cin >> ch;
if (ch != 'y' && ch != 'Y')
{
more = false;
}
}
return 0;
} inline bool check_validity(int pos)
{
return (pos <= 0 || pos > 100) ? false : true;
} const vector<int>* pentagonal_series(int pos)
{
static vector<int> _elems;
if (check_validity(pos) && (pos > _elems.size()))
{
for (int ix = _elems.size() + 1;ix <= pos;++ix)
{
_elems.push_back((ix * (3 * ix - 1)) / 2);
}
}
return &_elems;
} bool pentagonal_elem(int pos, int& elem)
{
if (!check_validity(pos))
{
cout << "Sorry. Invalid position: " << pos << endl;
elem = 0;
return false;
}
const vector<int>* pent = pentagonal_series(pos);
elem = (*pent)[pos - 1];
return true;
}

练习2.5 实现一个重载的max()函数,让它接受以下参数:(a)两个整数,(b)两个浮点数,(c)两个字符串,(d)一个整数vector,(e)一个浮点数vector,(f)一个字符串vector,(g)一个整数组,以及一个表示数组大小的整数值,(h)一个浮点数组,以及一个表示数组大小的整数值,(i)一个字符串数组,以及一个表示数组大小的整数值。最后,编写main()测试这些函数。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm> using namespace std; inline int max(int t1, int t2)
{
return t1 > t2 ? t1 : t2;
} inline float max(float t1, float t2)
{
return t1 > t2 ? t1 : t2;
} inline string max(string t1, string t2)
{
return t1 > t2 ? t1 : t2;
} inline int max(const vector<int>& vec)
{
return *max_element(vec.cbegin(), vec.cend());
} inline float max(const vector<float>& vec)
{
return *max_element(vec.cbegin(), vec.cend());
} inline string max(const vector<string>& vec)
{
return *max_element(vec.cbegin(), vec.cend());
} inline int max(const int* parray, int size)
{
return *max_element(parray, parray + size);
} inline float max(const float* parray, int size)
{
return *max_element(parray, parray + size);
} inline string max(const string* parray, int size)
{
return *max_element(parray, parray + size);
} int main()
{
int size = 5;
int n = 1, n2 = 9;
float f = 0.34, f2 = 9.3;
string s = "dfsdg", s2 = "dafsdfsad";
vector<int> ivec = { 12,70,2,169,1,5,29 };
vector<float> fvec = { 2.5,24.8,18.7,4.1,23.9 };
vector<string> svec = { "we","were","her","pride","of","ten" };
int iarray[5] = { 1,2,3,4,5 };
float farray[5] = { 5.0,4.0,3.0,2.0,1.0 };
string sarray[5] = { "a","b","c","asfs","aaa" };
cout << "max(n,n2)=" << max(n, n2) << "\n"
<< "max(f,f2)=" << max(f, f2) << "\n"
<< "max(s,s2)=" << max(s, s2) << "\n"
<< "max(ivec)=" << max(ivec) << "\n"
<< "max(fvec)=" << max(fvec) << "\n"
<< "max(svec)=" << max(svec) << "\n"
<< "max(iarray,size)=" << max(iarray, size) << "\n"
<< "max(farray,size)=" << max(farray, size) << "\n"
<< "max(sarray,size)=" << max(sarray, size) << "\n";
return 0;
}

练习2.6 以template重新完成练习2.5,并对main()函数做适当的修改。

#include <iostream>
#include <string>
#include <vector>
#include <algorithm> using namespace std; template <typename Type>
inline Type new_max(Type t1, Type t2)
{
return t1 > t2 ? t1 : t2;
} template <typename elemType>
inline elemType new_max(const vector<elemType>& vec)
{
return *max_element(vec.cbegin(), vec.cend());
} template <typename arrayType>
inline arrayType new_max(const arrayType* parray, int size)
{
return *max_element(parray, parray + size);
} int main()
{
int size = 5;
int n = 1, n2 = 9;
float f = 0.34, f2 = 9.3;
string s = "dfsdg", s2 = "dafsdfsad";
vector<int> ivec = { 12,70,2,169,1,5,29 };
vector<float> fvec = { 2.5,24.8,18.7,4.1,23.9 };
vector<string> svec = { "we","were","her","pride","of","ten" };
int iarray[5] = { 1,2,3,4,5 };
float farray[5] = { 5.0,4.0,3.0,2.0,1.0 };
string sarray[5] = { "a","b","c","asfs","aaa" };
cout << "max(n,n2)=" << new_max(n, n2) << "\n"
<< "max(f,f2)=" << new_max(f, f2) << "\n"
<< "max(s,s2)=" << new_max(s, s2) << "\n"
<< "max(ivec)=" << new_max(ivec) << "\n"
<< "max(fvec)=" << new_max(fvec) << "\n"
<< "max(svec)=" << new_max(svec) << "\n"
<< "max(iarray,size)=" << new_max(iarray, size) << "\n"
<< "max(farray,size)=" << new_max(farray, size) << "\n"
<< "max(sarray,size)=" << new_max(sarray, size) << "\n";
return 0;
}

end。

“纸上得来终觉浅,绝知此事要躬行。”

#《Essential C++》读书笔记# 第二章 面向过程的编程风格的更多相关文章

  1. 【C++系列小结】面向过程的编程风格

    前言 编程语言有面向过程和面向对象之分,因此编程风格也有所谓的面向过程的编程和面向对象的编程,并且语言的性质不会限制编程的风格. 这里主要说一以下向过程的编程. "面向过程"(Pr ...

  2. STL源码分析读书笔记--第二章--空间配置器(allocator)

    声明:侯捷先生的STL源码剖析第二章个人感觉讲得蛮乱的,而且跟第三章有关,建议看完第三章再看第二章,网上有人上传了一篇读书笔记,觉得这个读书笔记的内容和编排还不错,我的这篇总结基本就延续了该读书笔记的 ...

  3. 《C++ Primer》读书笔记—第二章 变量和基本类型

    声明: 文中内容收集整理自<C++ Primer 中文版 (第5版)>,版权归原书所有. 学习一门程序设计语言最好的方法就是练习编程. 1.8比特的char类型计算机表示的实际范围是-12 ...

  4. Getting Started With Hazelcast 读书笔记(第二章、第三章)

    第二章 起步 本章就相当简单粗暴了,用一个个例子说明hazelcast怎么用. 1.map,set,list这些集合类都是开箱即用的,只要从Hazelcast的实例中获取一份就行. 2.增加了Mult ...

  5. Java Concurrency in Practice 读书笔记 第二章

    第二章的思维导图(代码迟点补上):

  6. Spring 3.x 实践 第一个例子(Spring 3.x 企业应用开发实战读书笔记第二章)

    前言:工作之后一直在搞android,现在需要更多和后台的人员交涉,技术栈不一样,难免鸡同鸭讲,所以稍稍学习下. 这个例子取自于<Spring 3.x 企业应用开发实战>一书中的第二章,I ...

  7. javascript 数据结构和算法读书笔记 > 第二章 数组

    这章主要讲解了数组的工作原理和其适用场景. 定义: 一个存储元素的线性集合,元素可以通过索引来任意存取,索引通常是数字,用来计算元素之间存储位置的偏移量. javascript数组的特殊之处: jav ...

  8. [Effective Java 读书笔记] 第二章 创建和销毁对象 第一条

    第二章  创建和销毁对象 第一条 使用静态工厂方法替代构造器,原因: 静态工厂方法可以有不同的名字,也就是说,构造器只能通过参数的不同来区分不同的目的,静态工厂在名字上就能表达不同的目的 静态工厂方法 ...

  9. 《Pointers On C》读书笔记(第二章 基本概念)

    1.从源代码到生成可执行程序的过程整体上可以分为两个阶段:编译和链接.其中,编译过程大致上又可分为:预处理.编译和汇编.预处理阶段主要对源代码中的预处理指令(包含宏定义指令<如 #define& ...

随机推荐

  1. 基于playcanvas的3d模型展示

    1.使用基于playcanvas的离线编辑器制作模型效果 2.使用基于playcanvas的开发包读取编辑好的3d模型进行在线3d展示 效果如下:

  2. applyColorMap 在OpenCV中对灰度图进行颜色映射,实现数据的色彩化

    什么是色彩映射: 说直白点就是将各种数据映射成颜色信息,例如:温度,高度,压力,密度,湿度,城市拥堵数据等等 色彩化后更加直观表达 在OpenCV里可以使用 Mat im_gray = imread( ...

  3. pycharm安装PIL失败

    搜索安装PIL后无法成功安装,在尝试各种版本后依旧无法解决 问题解决 安装Pillow-PIL,既可以成功执行代码 因为pil没有64位的版本,所以需要下载安装第三方支持64位系统的版本才可以使用.

  4. Spring注解开发系列VII --- Servlet3.0

    Servlet3.0简介 Servlet 3.0 作为 Java EE 6 规范体系中一员,随着 Java EE 6 规范一起发布.该版本在前一版本(Servlet 2.5)的基础上提供了若干新特性用 ...

  5. spring4.2.4整合ehcache

    最近工作中遇到个功能需要整合ehcache,由于spring版本用的是4.2.4,而在ehcache官网找到的集成配置文档是spring3.1的,因此配了几次都不成功,在历经一番波折后终于成功集成了s ...

  6. sys model 常见用法

    import sys #与python解释器 交互 print(sys.argv) #是一个列表 解释器执行文件名后面可以增加字符串 以列表元素形式添加进去def foo(): print('ok') ...

  7. js 字符串方法 和 数组方法总览

    字符串方法        search()             方法搜索特定值的字符串,并返回匹配的位置.         相比于indexOf(),search()可以设置更强大的搜索值(正则表 ...

  8. CentOS 7 上CNVnator安装

    1.到github上下载最新版本 https://github.com/abyzovlab/CNVnator/releases 2.先看INSTALL文件,要求以下依赖,我的机器上已经安装了前两个,所 ...

  9. Struts(四)

    1.Struts 2提供了非常强大的类型转换功能,提供了多种内置类型转换器,也支持开发自定义类型转换器2.Struts 2框架使用OGNL作为默认的表达式语言 ==================== ...

  10. Windows下SVN权限配置

    Windows下SVN权限配置          按照前面的教程装完1.6.1版以后,当svnadmin create D;\svn创建仓库后,应该在仓库目录下的config目录有3个文件— auth ...