c++ primer 19th 特殊工具与技术

一、控制内存分配
  1. new表达式与operator new函数

    string *sp = new string("hello world");//分配并初始化一个string对象
    string *arr = new string[10];//分配10个默认初始化的string对象
    delete sp;//销毁*sp,然后释放sp指向额内存空间
    delete[] arr;//销毁数组中的元素,然后释放对应的内存空间
    

    当我们使用一条new表达式时:

    实际执行了三步操作.第一步,new表达式调用一个operator new(或者operator new[])的标准库函数。该函数分配一块足够大的、原始的、未命名的内存空间以便存储特定类型的对象(或者对象的数组)。第二步,编译器运行相应的构造函数以构造这些对象,并为其传入初始值。第三步,对象被分配了空间并构造完成,返回一个指向该对象的指针。

    当我们使用一条delete表达式删除一个动态分配的对象时:

    实际执行了两步操作。第一步,对sp所指的对象或者arr所指的数组中的元素执行对应的析构函数。第二步,编译器调用名为 operator delete(或者operator delete[])的标准库函数释放内存空间。

    如果程序希望控制内存分配的过程, 则它们需要定义自己的operator new函数和operator delete函数。即使标准库中已经存在这两个函数的定义,我们依旧可以定义自己的版本。

    一条new表达式的执行过程总是先调用operator new函数以获取内存空间,然后在得到的内存空间中构造对象。与之相反,一个delete表达式的执行过程总是先销毁对象,然后调用operator delete函数释放对象所占的空间。

  2. malloc函数与free函数

    malloc函数接受一个表示待分配字节数的size_t,返回指向分配空间的指针或者返回0以表示分配失败。free函数接受一个void*,它是malloc返回的指针的副本,free将相关内存返回给系统。调用free(0)没有任何意义。

    编写operator new 和 operator delete的一种简单方式,其他版本与之类似:

    void *operator new(size_t size)
    {
    	if(void *mem = malloc(size))
    	{
    		return mem;
    	}
    	else
    	{
    		throw bad_alloc();
    	}
    }
    void operator delete(void *mem) noexcept
    {
    	free(mem);
    }
    
  3. 显示析构函数调用

    string *sp = new string("hello world");
    sp->~string();
    

    调用析构函数会销毁对象,但是不会释放内存。

二、运行时类型识别
  1. dynamic_cast运算符的使用形式:

    dynamic_cast<type*>(e)

    dynamic_cast<type&)(e)

    dynamic_cast<type&&)(e)

    第一种形式中,e必须是一个有效的指针;在第二种形式中,e必须是一个左值;在第三种形式中,e不能是左值。

    e的条件:必须是目标type的共有派生类、e的类型是目标type的共有基类或者e的类型就是目标type的类型。

    返回:如果符合,则转换成功。否则如果转换的是指针类型并且失败了,则结果为0,如果是引用类型并且失败了,则抛出一个bad_cast异常。

    1. 指针类型的dynamic_cast

      if(Derived *dp = dynamic_cast<Derived*>(bp))
      else
      

      我们可以对一个空指针执行dynamic_cast,结果是所需类型的空指针。

    2. 引用类型的dynamic_cast

      void f(const Base &b)
      {
      	try
      	{
      		const Derived &b = dynamic_cast<const Derived&>(b);
      	}
      	catch(bad_cast)
      	{
      	}
      }
      
  2. typeid运算符

    它允许程序向表达式提问:你的对象是什么类型?

    使用typeid运算符:

    通常情况下,我们使用typeid比较两条表达式的类型是否相同,或者比较一条表达式的类型是否与指定类型相同:

    Derived *dp = new Dericed;
    Base *bp = dp;
    if(typeid(*bp) == typeid(*dp))
    {
    	//指向同一类型的对象
    }
    if(typeid(*bp) == typeid(Derived))
    {
    	//bp实际指向Derived对象
    }	
    

    下面的检查永远不执行:bp的类型时指向Base的指针

    if(typeid(bp) == typeid(Derived))
    {
    }
    

    当typeid作用于指针(而非指针所指的对象),返回的结果是该指针的静态编译时类型。如果p是一个空指针,则typeid(*p)将抛出一个名为bad_typeid的异常。

  3. type_Info类

    1. t1 == t2如果对象t1和t2 表示相同类型,返回true,否则false
    2. t1 != t2 如果type_info对象t1和t2表示不同的类型,返回true,否则false
    3. t.name() 返回一个C风格字符串,表示类型名字的可打印形式。
    4. t1.before(t2) 返回一个bool值,表示t1是否位于t2之前。
    int arr[10];
    Derived d;
    Base *p = &d;
    cout << typeid(42).name() << endl;
    cout << typeid(arr).name() << endl;
    cout << typeid(Sales_data).name() << endl;
    cout << typeid(std::string).name() << endl;
    cout << typeid(p).name() << endl;
    cout << typeid(*p).name() << endl;
    

    结果如下:i,A10_i,10Sales_data,Ss,P4Base,7Derived

三、枚举类型
  1. (非)限定作用域枚举类型

    #include<bits/stdc++.h>
    using namespace std;
    enum class MyEnum
    {
        p1 = 1,
        p2
    };
    int main()
    {
        MyEnum myEnum = MyEnum::p1;
        cout << (int)myEnum << endl;//一定要加int
    }
    

    没有class就是不限定作用域

    #include<bits/stdc++.h>
    using namespace std;
    enum class MyEnum
    {
        p1 = 1,
        p2
    };
    int main()
    {
        enum color 
        {
            p1 = 1,
            p2
        };
        color p3 = p2;
        cout << p3 << endl;
    }
    
  2. 枚举类型的前置声明

    enum intV : unsigned long long;//不限定作用域的前置声明
    enum class open_modes; //默认int
    
四、类成员指针

首先定义一个Screen类

#include<bits/stdc++.h>
using namespace std;
class Screen
{
public:
    Screen(string str)
    {
        contents = str;
    }
    void f1()
    {
        cout << "1" << endl;
    }

    static const string Screen::*data()
    {
        return &Screen::contents;
    }
public:
    std::string contents;
};
  1. 数据成员指针

    Screen sc("hello world!");
    auto pdata = &Screen::contents;//pdata声明成一个"指向Screen类的const string成员的指针”只能读取它所指的成员,而不能向它写入内容
    cout << sc.*pdata << endl;
    
  2. 返回数据成员指针的函数

    Screen sc("hello world!");
    const string Screen::*d = Screen::data();
    auto s = sc.*d;//要想使用pdata,必须把它绑定到Screen类型的对象上
    cout << s << endl;
    
  3. 成员函数指针

    Screen sc("hello world!");
    auto pmf = &Screen::f1;
    (sc.*pmf)();//括号必不可少!
    
  4. 成员指针函数表

    #include<bits/stdc++.h>
    using namespace std;
    class Screen
    {
    public:
        Screen(string str)
        {
            contents = str;
        }
        void f1()
        {
            cout << "1" << endl;
        }
        static const string Screen::*data()
        {
            return &Screen::contents;
        }
        Screen& home();
        Screen& forward();
        Screen& back();
        Screen& up();
        Screen& down();
    public:
        using Action = Screen& (Screen::*)();
        enum Directions
        {
            Home,
            FORWARD,
            BACK,
            UP,
            DOWN
        };
        std::string contents;
        Screen& Screen::move(Directions cm)
        {
            return (this->*Menu[cm])();
        }
    public:
        static Action Menu[]; //函数表
    };
    int main()
    { 
        Screen myScreen("hello world!");
    
        myScreen.move(Screen::Home);
        myScreen.move(Screen::DOWN);
    }   
    

    初始化函数表:

    Screen::Action Screen::Menu[] = {
    	&Screen::home,
    	&Screen::forward,
    	&Screen::back,
    	&Screen::up,
    	&Screen::down
    }
    
  5. 将成员函数用作可调用对象

    1. 使用mem_fn生成一个可调用对象

      vector<string> vec;
      vec.push_back("");
      cout << count_if(vec.begin(),vec.end(),mem_fn(&string::empty)) << endl; //1
      
五、union
  1. union是一种特殊的类。一个union可以有多个数据成员,但是在任意时刻只有一个数据成员可以有值。当我们给union的某个成员赋值之后,该union的而其他成员就变成未定义的状态了。分配给一个union对象的存储空间至少要能容纳它的最大的数据成员。

    #include<bits/stdc++.h>
    using namespace std;
    union Token
    {
        char cval;
        int ival;
        double dval;
    };
    int main()
    { 
        Token fir = {'a'};
        // Token sec;
        // Token *pt = new Token;
        cout << fir.cval << endl;
        fir.ival = 10;
        cout << fir.ival << endl;
        cout << fir.cval << endl;//木有了
    }   		
    
  2. union可以为其成员指定public、protected和private等保护标记。默认情况下,union的成员都是共有的,这一点与struct相同。

  3. union可以定义包括构造函数和析构函数在内的成员函数。但是由于union既不能继承自其他类,也不能作为基类使用,所以在union中不能含有虚函数。

  4. 匿名union,可直接访问它的成员

    #include<bits/stdc++.h>
    using namespace std;
    
    int main()
    { 
        union 
        {
            char cval;
            int ival;
            double dval;
        };
        cval = 'c';
        ival = 42;
        
        
        cout << cval << endl;
        cout << ival << endl;
    }   
    

原文地址:https://www.cnblogs.com/Jawen/p/11151046.html