美文网首页
The C++ standard library(侯捷/孟岩 译

The C++ standard library(侯捷/孟岩 译

作者: Ewitter | 来源:发表于2019-04-12 21:52 被阅读0次

4. numeric limits/辅助函数/comparison operator

一般而言,数值型别(numeric types)的极值是与平台有关的。

C++标准程序库通过template numberic_limits提供这些极值,
    取代传统C采用的预处理器常数(preprocessor constants)**,当然二者都可使用。

整数常数定义在<climits>和<limits.h>。
浮点数定义于<cfloat>和<float.h>

新的极值概念有两个优点:提供了更好的型别安全型;可借此写出一些template以核定(evaluate)这些极值。

note:C++ standard规定了各种型别必须保证的最小精度,如果能注意并运用这些价值就可写出与平台无关的程序。

p59_t4-1.png

4.1 class numeric_limits<>

使用template通常是为了对所有型别一次性的写出一个通用解决方案。但还可以在必要时以template为每个型别提供共同接口。
方法:不但提供通用性template,还提供其特化(specialization)版本,eg:numeric_limits

1. 通用性template,为所有型别提供缺省值:
namespace std
{
    /* general numeric limits as default for any type
    */
    template <class T>
    class numeric_limits
    {
    public:
        //no specialization for numeric limits exist
        static const bool is_specialized = false;
        //.. //other members that are meaningless for the general numeric limits
    };
}
这个通用性template将成员is_specialized设为false,意思是对于型别T,无所谓极值的存在。

2. 各个具体型别的极值,由特化版本(specialization)提供:
namespace std
{
    /* numeric limits for int
    * - implementation defined
    */
    template <> class numeric_limits<int>
    {
    public:
        //yes, a specialization for numeric limits of int does exist
        static const bool is_specialized = true;

        static T min() throw()
        {
            return -2147483648;
        }
        static T max() throw()
        {
            return 2147483647;
        }
        static const int digits = 31;
        //...
    };
}

通用性numeric_limits template及其特化版本都放在<limits>头文件中。
C++ standard所囊括的特化版本涵盖了所有数值基本型别:
bool/char/signed char/unsigned char/wchar_t、
short/unsigned short/int/unsigned int/long/ float/double/long double

class numeric_limits<>所有成员及其意义如下图


p62_t4-2-0.png p62_t4-2-1.png

4.2 float型别数值限定模板特殊化的示例(page62)

namespace std
{
    template<> class numeric)limits<float>
    {
    public:
        //yes,a specialization for numeric limits for limits of float does exist
        static const bool is_specialized = true;
    
        inline static float min() throw()
        {
            return 1.17549435E-38F;
        }
        inline static float max() throw()
        {
            return 3.40282347E+38F;
        }

        static const int digits = 24;
        static const int digits10 = 6;
    
        static const bool is_signed = true;
        static const bool is_integer = false;
        static const bool is_exact = false;
        static const bool is_bounded = true;
        static const bool is_modulo = false;
        static const bool is_iec559 = true;

        static const int radix = 2;

        inline static float epsilon() throw()
        {
            return 1.19209290E-07F;
        }

        static const float_round_style round_style = round_to_nearest;
        inline static float round_error() throw()
        {
            return 0.5F;
        }

        static const int min_exponent = -125;
        static const int max_exponent = +128;
        static const int min_exponent10 = -37;
        static const int max_exponent10 = +38;

        static const bool has_infinity = true;
        inline static float infinity() throw() {         return ...;}
        static const bool has_quiet_NaN = true;
        inline static float quiet_NaN() throw() {         return ...;}
        static const bool has_signaling_NaN = true;
        inline static float signaling_NaN() throw() {         return ...;}
        static const bool has_denorm_loss = false;
        inline static float infinity() throw() {         return ...;}

        static const bool traps = true;
        static const bool tinyness_before = true;
    };
}

note:数据成员是const或static,如此其值便在编译期确定;
      至于函数定义的成员,某些编译器无法在编译期间确定其值。
      故同一份代码在不同处理器上执行可能得出不同浮点数。
p64.png

C++ standard保证,若denorm_absent为0则为false,若denorm_present为1且denorm_indeterminate为-1则二者都为true。故可把has_denorm视为一个bool以判断某型别是否允许"denormalized values"。

4.3 numeric_limits<> 使用范例(page64)

/* 此例用于展示 某些型别极值的可能运用(eg了解某型别的最大值或确定char是否有符号)
*/
#include <iostream>
#include <limits>
#include <string>
using namespace std;

int main()
{
    // use textual representation for bool
    cout << boolalpha;

    // print maximum of integral type
    cout << "max(short): " << numeric_limits<short>::max() << endl;
    cout << "max(int): " << numeric_limits<int>::max() << endl;
    cout << "max(long): " << numeric_limits<long>::max() << endl;
    cout << endl;

    // print maximum of floating-point types
    cout << "max(float): " << numeric_limits<float>::max() << endl;
    cout << "max(double): " << numeric_limits<double>::max() << endl;
    cout << "max(long double): " << numeric_limits<long double>::max() <<endl;

    // print whether char is signed
    cout << "is_signed(char): " << numeric_limits<char>::is_signed << endl;
    cout << endl;

    // print whether numeric limits for type string exist
    cout << "is_specialized(string): " << 
            numeric_limits<string>::is_specialized << endl;
}

程序输出结果与执行平台有关,如下是一种可能:


limits.png

4.5 辅助函数/比较操作符(page66)

4.5.1 辅助函数

算法程序库(定义于头文件<algorithm>)内含3个辅助函数,一个是两值取大,一个是两值取小,第三个用于交换两值。

1. 挑选较小值和较大值
namespace std
{
    template <class T>
    inline const T& min(const T& a, const T& b)
    {
        return b < a ? b : a;
    }
    template <class T>
    inline const T& max(const T& a, const T& b)
    {
        return a < b ? b : a;
    }
}
如果两值相等,通常返回第一值(程序最好不要依赖这一点)。

另一版本:接受额外的template参数作为“比较准则”
namespace std
{
    template <class T , class Compare>
    inline const T& min (const T& a, const T& b,Compare comp)
    {
        return comp(b,a) ? b : a;
    }
    template <class T , class Compare>
    inline const T& max (const T& a, const T& b,Compare comp)
    {
        return comp(a,b) ? b : a;
    }
}
作为“比较准则”的参数应该是函数或仿函数,接受两参数并比较:
    在某指定规则下,判断第一个参数是否小于第二参数并返回判断结果。
eg,code:
#include <algorithm>
using namespace std;

/*function that compares two pointers by comparing the values to which they point
 */
bool int_ptr_less (int* a, int* b)
{
    return *a < *b;
}

int main()
{
    int x = 17;
    int y = 42;
    int* px = &x;
    int* py = &y;
    int* pmax;

    // call max() with sepcial comparison function
    pmax = max (px, py, int_ptr_lesss);
    //...
}

2. 两值互换
函数swap()用于交换两对象的值。其泛型版定义于<algorithm>。
namespace std
{
    template <class>
    inline void swap(T& a, T& b)
    {
        T tmp(a);
        a = b;
        b = tmp;
    }
}

当且仅当swap()所依赖的copy构造函数和assignment操作行为存在时,这个调用才可能有效。
swap()的最大优势:

透过template specialization或function overloading,可以为更复杂的型别提供特殊的版本:
    可交换对象内部成员而不是反复赋值,如此可节约时间。
    标准程序库中所有容器都用了这项技术。eg:
        一个容器仅含一个array和一个成员(用于指示元素数量),那么其特化版本的swap()可以是:
class MyContainer
{
private:
    int* elems;  // dynamic array of elements
    int* numElems;  // numbers of elements
public:
    //...
    // implementation of swap()
    void swap(MyContainer& x) 
    {  
        std::swap(elems, x.elems);
        std::swap(numElems, x.numElems);
    }
    //...
    
    //overloaded global swap() for this type
    inline void swap(MyContainer& c1, MyContainer& c2)
    {
        c1.swap(c2);  // calls implementation of swap()
    }
}; 
4.5.2 辅助操作符(Comparison Operators)(page69)

有四个template functions,分别定义了!= 、>、<=、>=比较操作符,它们都是利用操作符== 和 <完成的。四个函数定义于<utility>。

namespace std
{
    namespace rel_ops
    {
        template <class T>
        inline bool operator!= (const T& x, const T& y)
        {
            return !(x == y);
        }

        template <class T>
        inline bool operator> (const T& x, const T& y)
        {
            return y < x;
        }

        template <class T>
        inline bool operator<= (const T& x, const T& y)
        {
            return !(y < x);
        }

        template <class T>
        inline bool operator>= (const T& x, const T& y)
        {
            return !(x < y);
        }
    }
}

只要加上using namespace std::rel_pos上述四个比较操作符就自动获得了定义。

4.6 头文件<cstddef>和<cstdlib> (page71)

4.6.1 <cstddef>各种含义
p71_t4-6.png
NULL通常表一个不指向任何对象的指针,即0(型别是int或long),但C中NULL常定义为(void*)0。
C++ 中没定义从 void*到任何其他型别的自动转型操作。
NULL也定义于头文件<cstdio>/<cstdlib>/<cstring>/<ctime>/<cwchar>/<clocale>。
4.6.2 <cstdlib>内各种含义
p72_t4-7.png
常数EXIT_SUCCESS和EXIT_FAILURE用于作exit()的参数或main()的返回值。

经由atexit()注册的函数,在程序正常退出是会依注册的相反次序被一一调用,
    无论是通过exit()退出或main()尾部退出,都是如此,不传递任何参数。

exit()和abort()可在任意处终止程序运行,无需返回main()。

exit()会销毁所有static对象,将所有缓冲区buffer清空,关闭所有I/O通道channels,
    然后终止程序(之前会先调用由atexit()注册的函数),
      若atexit()注册的函数抛出异常则会调用terminate()。

abort()会立刻终止函数且不做任何清理clean up工作。

note:这两个函数都不会销毁局部对象local objects,
    因为堆栈 辗转开展动作stack inwinding 不会被执行。
    为确保所有局部对象的析构函数被调用,应用异常exceptions或正常返回机制,再由main()离开。

相关文章

网友评论

      本文标题:The C++ standard library(侯捷/孟岩 译

      本文链接:https://www.haomeiwen.com/subject/hstgiqtx.html