• 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;
      }   
      

  • 相关阅读:
    返回一个随机数组中的子数组中的数相加最大的和
    四则运算二之结果
    四则运算二
    UVA 11741 Ignore the Blocks
    UVA 1408 Flight Control
    UVA 10572 Black & White
    CF1138D(545,div2) Camp Schedule
    UVA 1214 Manhattan Wiring
    UVA 11270 Tiling Dominoes
    BZOJ 3261 最大异或和
  • 原文地址:https://www.cnblogs.com/Jawen/p/11151046.html
Copyright © 2020-2023  润新知