zoukankan      html  css  js  c++  java
  • 第19章 特殊工具与技术

    这些特性在一些特殊应用中非常重要,而在另外一些情况没有什么用,这里介绍这些非广泛使用的特征

    19.1控制内存分配

    标准库自定义了如下内存分配工具

    // theseversions might throw an exception
    void *operator new(size_t);  // allocate an object
    void *operator new[](size_t);  // allocate an array
    void operator delete(void*) noexcept;  // free an object
    void operator delete[](void*) noexcept; // free an array
    // versions that promise not to throw;
    void *operator new(size_t, nothrow_t&) noexcept;
    void *operator new[](size_t, nothrow_t&) noexcept;
    void operator delete(void*, nothrow_t&) noexcept;
    void operator delete[](void*, nothrow_t&) noexcept;

     

    1. new表达式的过程有两个步骤,1调用new操作分配内存,2在内存中执行构造函数
    2. delete表达式也会有两个步骤,1调用对象的析构函数,2调用delete操作收回分配的内润。
    3. 自定义的重载只能够针对new操作和delete操作这个步骤。
    4. 自定义重载可以增加操作符的参数个数,在使用时,必须使用定位形式,而不是在括号中写入增加的参数。
    5. 在重载中需要使用malloc和free进行内容的控制
    void *operatornew(size_t size) {
        if (void *mem = malloc(size))
            return mem;
        else
            throw bad_alloc();
    }
    void operator delete(void *mem) noexcept 
    {
        free(mem); 
    }

    19.1.2定位new表达式

    当通过地址值调用时定位new表达式,将会使用operator new(size_t, void*)分配内存,然后在这个地址上进行初始化。

    我们可以将内存分配分解为:

    //使用operator new分配内存
    auto p = operator new(sizeof(string));
    //使用new定位形式初始化对象
    auto pp = new(p) string("123");
    //析构对象
    pp->~string();
    //使用operator delete收回内存
    operator delete(pp);
    定位new的形式有如下几种:
    new (place_address) type
    new (place_address) type(initializers)
    new (place_address) type[size]
    new (place_address) type[size]{ braced initializer list }

    如果在place_address中不使用地址值,则次定位形式将会调用operator new的其他自定义形式分配内存,然后初始化对象。

    19.2运行时类型识别

    Run-time type identification(RTTI) is provided through two operator:

    1. The typeid operator, which returns the type of a given expression
    2. The dynamic_cast operator, which safely converts a pointer or reference to a base type into a pointer or reference to a derived type

    19.2.1运算符dynamic_cast

    Dynamic_cast有三种形式

    dynamic_cast<type*>(e)
    dynamic_cast<type&>(e)
    dynamic_cast<type&&>(e)

    需要保证被转换的类型能够转换成type类型。

    1. 如果转换目标是指针类型,失败返回0;
    2. 如果是引用类型,失败就会抛出bad_cast异常。

    19.2.2运算符typeid

    typeid(e)中,e可以是任意表达式或类型的名字。typeid操作的结果是一个常量对象的引用,该对象的类型是标准库类型type_info或type_info的公有派生类型<typeinfo>。

    当运算对象不属于类类型或者一个不包含任何虚函数的类时,typeid运算符指示的是运算对象的静态类型。唯有当运算对象是定义了至少一个虚函数的类的左值时,才会知道运行时才会求值。

    class Base 
    {
        virtual void Foo();
    };
    class Derived :public Base
    {
        void Foo() override;
    };
    int main()
    {
        Derived *d = new Derived;
        Base *b = d;
        //true,都是Derived类型
        if (typeid(*b) == typeid(*d))
        {
            cout << "same" << endl;
        }
        //false,是Derived类型
        if (typeid(*b) == typeid(Base))
        {
            cout << "Base" << endl;
        }
    }

    注意:没有虚函数的类型,会被当做静态类型使用。

    19.2.4类type_info

    type_info的操作

    t1==t2

    t1!=t2

    比较t1和t2是否是同一种类型

    t.name()

    返回C字符串,表示类型名字

    t.1.before(t2)

    返回bool,表示t1是否位于t2前,编译器依赖

    19.3枚举类型enumeration

    限定作用域的枚举类型是C++11引入的

    enum class open_modes { input, output, append };
    enum struct open_modes { input, output, append };

    不限定作用域的

    // unscoped enumeration
    enum color { red, yellow, green };  
    // unnamed, unscoped enum
    enum { floatPrec = 6, doublePrec = 10, double_doublePrec = 10 };

    作用域跟没作用域的区别

    enum color { red, yellow, green };  // unscoped enumeration
    enum stoplight { red, yellow, green };  // error: redefines enumerators
    enum class peppers { red, yellow, green }; // ok: enumerators are hidden
    color eyes = green; // ok: enumerators are in scope for an unscoped enumeration
    peppers p = green;  // error: enumerators from peppers are not in scope
    //  color::greenis in scope but has the wrong type
    color hair = color::red;  // ok: we can explicitly access the enumerators
    peppers p2 = peppers::red; // ok: using red from peppers

    默认,枚举值从0开始,依次加1,当然也可以指定专门的值

    enum class intTypes {
        charTyp = 8, shortTyp = 16, intTyp = 16,
        longTyp = 32, long_longTyp = 64
    };

    枚举类型的值是常量表达式

    constexpr intTypes charbits = intTypes::charTyp;

    前置声明,无作用域必须有类型

    // forward declaration of unscoped enum named intValues
    enum intValues : unsigned long long; // unscoped, must specify a type
    enum class open_modes;  // scoped enums can use int by default

    形参匹配与枚举类型

    // unscope denumeration; the underlying type is machine dependent
    enum Tokens { INLINE = 128, VIRTUAL = 129 };
    void ff(Tokens);
    void ff(int);
    int main() {
        Tokens curTok = INLINE;
        ff(128);  // exactly matches ff(int)
        ff(INLINE);// exactly matches ff(Tokens)
        ff(curTok);// exactly matches ff(Tokens)
        return 0;
    }

    19.4类成员指针

    如下类示例

    class Screen {
    public:
        typedef std::string::size_type pos;
        char get_cursor() const { return contents[cursor]; }
        char get() const;
        char get(pos ht, pos wd) const;
    private:
        std::string contents;
        pos cursor;
        pos height, width;
    };

    19.4.1数据成员指针

    // pdata can point to a string member of a const (or non const) Screen object
    const string Screen::*pdata; 
    pdata = &Screen::contents;
    auto p= &Screen::contents;

    使用数据成员指针

    auto pdata = &Screen::contents;
    Screen myScreen, *pScreen = &myScreen;
    // .* dereferences pdata to fetch the contents member from the object myScreen
    auto s = myScreen.*pdata;
    // ->* dereferences pdata to fetch contents from the object to which pScreen points
    s = pScreen->*pdata;

    返回数据成员的指针

    class Screen {
    public:
        static const std::string Screen::*data()
        {
            return &Screen::contents;
        }
    };

    19.4.2成员函数指针

    char (Screen::*pmf2)(Screen::pos, Screen::pos) const;
    pmf2 = &Screen::get;
    auto pmf = &Screen::get_cursor;

    使用成员函数指针

    Screen myScreen, *pScreen = &myScreen;
    // call the function to which pmf points on the object to which pScreen points
    char c1 = (pScreen->*pmf)();
    // passes the arguments 0, 0 to the two-parameter version of get on the object myScreen
    char c2 = (myScreen.*pmf2)(0, 0);

    使用成员指针的类型别名

    // Action isa type that can point to a member function of Screen
    // that returns a char and takes two pos arguments
    using Action = char (Screen::*)(Screen::pos, Screen::pos) const;
    Action get = &Screen::get; // get points to the get member of Screen
    // action takesa reference to a Screen and a pointer to a Screen member function
    Screen& action(Screen&, Action = &Screen::get);
    
    Screen myScreen;
    // equivalent calls:
    action(myScreen);  // uses the default argument
    action(myScreen, get); // uses the variable get that we previously defined
    action(myScreen, &Screen::get); // passes the address explicitly

    成员指针函数表

    有类中多个相关函数,为了使用更方便,将使用成员指针函数表

    class Screen {
    public:
        // other interface and implementation members as before
        Screen&home();  // cursor movement functions
        Screen&forward();
        Screen&back();
        Screen&up();
        Screen&down();
    };

    加入函数表、枚举表,和一个对成员函数指针使用的别名

    class Screen {
    public:
        // other interface and implementation members as before
        // Action is a pointer that can be assigned any of the cursor movement members
        using Action = Screen& (Screen::*)();
        // specify which direction to move; enum see § 19.3 (p. 832)
        enum Directions{ HOME, FORWARD, BACK, UP, DOWN };
        Screen&move(Directions);
    private:
        static Action Menu[];  // function table
    };

    使用move

    Screen& Screen::move(Directions cm)
    {
        // run the element indexed by cm on this object
        return(this->*Menu[cm])(); // Menu[cm] points to a member function
    }
    Screen::Action Screen::Menu[] = {   &Screen::home,
                                        &Screen::forward,
                                        &Screen::back,
                                        &Screen::up,
                                        &Screen::down,
                                     };

    19.4.3将成员函数用作可调用对象

    类的成员函数指针必须通过->*或者.*绑定到对象的时候才能够进行,所以成员指针不是一个可调用对象,不能够直接讲一个指向成员函数的指针传递给一个算法:

    auto fp = &string::empty;  // fp points to the string empty function
    // error: must use .* or ->* to call a pointer to member
    find_if(svec.begin(), svec.end(), fp);

    其中有一段代码

    if(fp(*it))

    fp不是可调用对象,所以会错误

    使用function生成一个可调用对象

    function<bool(const string&)> fcn = &string::empty;
    find_if(svec.begin(), svec.end(), fcn);

    对于其中调用

    if (fcn(*it))

    function会转换为

    if (((*it).*p)())

    使用men_fn生成一个可调用对象

    auto f = mem_fn(&string::empty); // f takes a string or a string*
    f(*svec.begin()); // ok: passes a string object; f uses .* to call empty
    f(&svec[0]);  // ok: passes a pointer to string; f uses .-> to call empty

    men_fn定义在functional中,可以正确处理类成员函数指针的调用

    find_if(svec.begin(), svec.end(), mem_fn(&string::empty));

    使用bind生成可调用对象

    auto f = bind(&string::empty, _1);
    f(*svec.begin()); // ok: argument is a string f will use .* to call empty
    f(&svec[0]); // ok: argument is a pointer to string f will use .-> to call empty

    19.5嵌套类Nested Classes

    class TextQuery {
    public:
        class QueryResult; // nested class to be defined later
                          // other members as in § 12.3.2 (p. 487)
    };

    嵌套类只在作用域中可见,需要注意的是,两个类是相互独立的,一点关系都没有。

    19.6联合union

    union Token {
        // members are public by default
        char  cval;
        int  ival;
        double dval;
    };
    Anonymous unions
    union {  // anonymous union
        char  cval;
        int  ival;
        double dval;
    };  // defines an unnamed object, whose members we can access directly
    cval = 'c'; // assigns a new value to the unnamed, anonymous union object
    ival = 42;  // that object now holds the value 42

    C++11扩展了union,可以定义更多成员,但是其管理难度也有了进一步的增加。通常,会在一个类中管理union,在类其中,需要自定义各种构造函数,并且,需要保存一个判别式,指示当前union中存储的对象类型。

    19.7局部类

    在函数中定义的类叫做局部类

    int a, val;
    void foo(int val)
    {
        static int si;
        enum Loc{ a = 1024, b };
        // Bar is local to foo
        struct Bar {
            Loc locVal; // ok: uses a local type name
            int barVal;
            void fooBar(Loc l = a)  // ok: default argument is Loc::a
            {
                barVal = val;  // error: val is local to foo
                barVal = ::val;  // ok: uses a global object
                barVal = si;  // ok: uses a static local object
                locVal = b;  // ok: uses an enumerator
            }
        };
    }

    19.8固有不可移植特性Inherently Nonportable Features

    19.8.1位域

    将类的非静态成员定义成位域

    typedef unsigned int Bit;
    class File {
        Bit mode : 2;  // mode has 2 bits
        Bit modified : 1;  // modified has 1 bit
        Bit prot_owner : 3; // prot_owner has 3 bits
        Bit prot_group : 3; // prot_group has 3 bits
        Bit prot_world : 3; // prot_world has 3 bits
                          // operations and data members of File
    public:
        // file modes specified as octal literals; see § 2.1.3 (p. 38)
        enum modes { READ = 01, WRITE = 02, EXECUTE = 03 };
        File&open(modes m) {
            mode |= READ;  // set the READ bit by default
                           // other processing
            if (m & WRITE) // if opening READ and WRITE
                           // processing to open the file in read/write mode
                return*this;
        }
        void close() {
            if (modified)
                // . . . save contents
        }
        void write() {
            modified = 1;
            // . . .
        }
        bool isRead() const { return mode & READ; }
        void setWrite() { mode |= WRITE; }
    };

    19.8.2限定符volatile

    程序处理的对象中,其值非程序直接控制,比如系统时钟更新的变量,应给声明为volatile,告诉编译器不对此进行优化。

    需要注意的是,合成的拷贝/移动构造函数、赋值运算符对volatile对象是无效的.

    19.8.3链接提示extern“C”

    // illustrative linkage directives that might appear in the C++ header <cstring>
    // single-statement linkage directive
    extern "C" size_t strlen(const char *);
    // compound-statement linkage directive
    extern "C" {
        int strcmp(const char*, const char*);
        char*strcat(char*, const char*);
    }
    还可以包含头文件进去
    // compound-statement linkage directive
    extern "C" {
    #include <string.h>  // C functions that manipulate C-style strings
    }
  • 相关阅读:
    springmvc和struts2的区别(转)
    springmvc和struts2的区别
    sql优化(转)
    java反射机制
    阿里云产品
    vscode 插件推荐
    测试JS方法运行时间
    基于VUE2.0的分页插件(很好用,很简单)
    【软件测试】测试管理工具----禅道
    Vue.js中用webpack合并打包多个组件并实现按需加载
  • 原文地址:https://www.cnblogs.com/qiusuo/p/5160726.html
Copyright © 2011-2022 走看看