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

0
11

基础知识

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

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

<

发布回复

请输入评论!
请输入你的名字