基础知识

函数必须先被声明,然后才能被调用(被使用)。函数的声明让编译器得以检查后续出现的使用方式是否正确——是否有足够的参数、参数类型是否正确,等等。函数声明不必提供函数体,但必须指明返回类型、函数名,以及参数列表。此即所谓的函数原型(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. linux系统iot平台编程阶段总结

    1.inline内联函数 在C语言中,如果一些函数被频繁调用,不断地有函数入栈,即函数栈,会造成栈空间或栈内存的大量消耗. 为了解决这个问题,特别的引入了inline修饰符,表示为内联函数. 在使用循 ...

  2. 微软CEO:我们员工以及他们家人的健康与安全是我们最高的优先级!

    在肺炎疫情开始之初,微软中国各个部门就立即采取了各种的防控和关怀措施. 在昨天,微软 CEO Satya Nadella 给微软中国全体员工写了一封邮件. 其中,最让我们感动的就是这句话:" ...

  3. ios启动流程

    1.创建UIApplication (1.打开网页,发短信,打电话 . 2.设置应用程序提醒数字 . 3.设置联网状态 . 4.设置状态栏) 2.创建AppDelegate代理对象,并且成为UIApp ...

  4. HDU 3839 Ancient Messages(DFS)

    In order to understand early civilizations, archaeologists often study texts written in ancient lang ...

  5. java加解密算法--DES

    ECB import sun.misc.BASE64Decoder; import javax.crypto.*; import javax.crypto.spec.DESKeySpec; impor ...

  6. python——3种字符串反转方法

    在学习过程中,总结了3种字符串反转方法: 1.切片法 这种方法最为简便 str='abad' print(str[::-1]) · 用切片操作,将字符串以步长-1重新整理,即 'str[-1],str ...

  7. 三、 TCP(传输控制协议)

    它建立在网际层协议(IP)提供的数据包传输技术之上,.TCP使应用程序可使用连续的数据进行通信.除非由于网络故障导致连接中断或冻结,TCP都能保证数据流完好地传输.而不会发生丢包 ,重包或是乱序的问题 ...

  8. Codeforces_798

    A.暴力把每个位置的字符改成另外25个字符,判断是否回文. #include<bits/stdc++.h> using namespace std; string s; int main( ...

  9. 【原创】(二)Linux进程调度器-CPU负载

    背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...

  10. Go语言实现:【剑指offer】旋转数组的最小数字

    该题目来源于牛客网<剑指offer>专题. 把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非递减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3, ...