C++类型擦除

2023-11-07

转自:http://www.cnblogs.com/liyiwen/archive/2009/12/10/1621451.html

 

 

关于类型擦除,在网上搜出来的中文资料比较少,而且一提到类型擦除,检索结果里就跑出很多 Java 和 C# 相关的文章来(它们实现“泛型”的方式)。所以,这一篇我打算写得稍微详细一点。 注意,这是一篇读书笔记(《C++ template metaprogramming》第9.7小节和《C++ テンプレートテクニック》第七章),里面的例子都来自原书。


在 C++ 中,编译器在编译期进行的静态类型检查是比较严格的,但有时候我们却希望能“避过”这样的类型检查,以实现更灵活的功能,同时又尽量地保持类型安全。听起来很矛盾,而且貌似很难办到。但其实 C++ 的库里已经有很多这样的应用了。比如,著名的 boost::function 和 boost::any 。当我们定义一个 function<void(int)> fun 对象,则 fun 即可以存储函数指针,又可以存储函数对象,注意,这两者是不同“类型”的,而且函数对象可以是无限种类型的,但这些不同类型的“东西”都可以存在同一类型的对象 fun 中,对 fun 来说,它关心的只是存储的“对象”是不是“可以按某种形式(如void(int))来调用”,而不关心这个“对象”是什么样的类型。有了 function 这样的库,在使用回调和保存可调用“对象”的时候,我们就可以写出更简单且更好用的代码来。再举一个例子,boost::any 库。any 可以存储任何类型的“对象”,比如 int ,或是你自己定义的类 MyCla 的对象。这样我们就可以使一个容器(比如 vector<boost::any> )来存储不同类型的对象了。

这些库所表现出来的行为,就是这篇文章中要提到的类型擦除,类型擦除可以达到下面两个目的:

  • 用类型 S 的接口代表一系列类型 的的共性。
  • 如果 s 是 S 类型的变量,那么,任何 T 类型的的对象都可以赋值给s。

好了,下面我们具体地看看类型擦除是怎么回事,在这个过程中,我们先以 any 这个类为依托来解释(因为它比较“简单”,要解释的额外的东西比较少)。

any 这个类需要完成的主要任务是:1. 存储任何类型的变量 2. 可以相互拷贝 3. 可以查询所存变量的类型信息 4. 可以转化回原来的类型(any_cast<>)

对于其中,只要说明1和2 ,就能把类型擦除的做法展示出来了,所以,我们这里只实现一个简单的,有1、2、3功能的any类(3是为了验证)。

首先,写个最简单的“架子”出来:

 

<span style="color:#333333"><span style="color:blue">class </span><span style="color:#010001">my_any </span>{ 
    ?? <span style="color:#010001">content_obj</span>; 
<span style="color:blue">public</span>: 
    <span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">T</span>> 
    <span style="color:#010001">my_any</span>(<span style="color:#010001">T </span><span style="color:blue">const</span>& <span style="color:#010001">a_right</span>); 
}; </span>

这里,由于 my_any 的拷贝构造函数使用的是模板函数,因此,我们可以任何类型的对象来初始化,并把该对象的复本保存在 content_obj 这个数据成员中。那么,问题是,content_obj 用什么类型好呢?

首先我们会想到,给 class 加个模板参数 T ,然后……,不用然后了,这样的话,使用者需要写这样的代码:

<span style="color:#333333"><span style="color:#010001">my_any</span><<span style="color:#010001">someType</span>> <span style="color:#010001">x </span>= <span style="color:#010001">y</span>;</span>

不同的 y 会创造出不同类型的 x 对象,完全不符合我们要将不同类型对象赋给同一类型对象的初衷。接着,我们会想到用 void *(C 式的泛型手法啊……),但这样的话,我们就会完全地丢失原对象的信息,使得后面一些操作(拷贝、还原等)变得很困难,那么,再配合着加入一些变量用于保存原对象信息?你是说用类似“反射”的能力?好吧,我只好说,我以为 C++ 不存在原生的反射能力,以我浅薄的认识,我只知道像 MFC 式的侵入式手法……,嗯,此路不通。

这个困境的原因在于,在C++ 的类中,除了类模板参数之外,无法在不同的成员(函数、数据成员)之间共享类型信息。在这个例子中,content_obj 无法得知构造函数中的 T 是什么类型。所以类型无法确定。

为了妥善保存原对象复本,我们定义两个辅助类,先上代码(来自 boost::any 的原码):

<span style="color:#333333"><span style="color:blue">class </span><span style="color:#010001">placeholder 
</span>{ 
<span style="color:blue">public</span>: <span style="color:green">// structors 
    </span><span style="color:blue">virtual </span>~<span style="color:#010001">placeholder</span>()      { 
    } 
<span style="color:blue">public</span>: <span style="color:green">// queries 
    </span><span style="color:blue">virtual const </span><span style="color:#010001">std</span>::<span style="color:#010001">type_info </span>& <span style="color:#010001">type</span>() <span style="color:blue">const </span>= 0; 
    <span style="color:blue">virtual </span><span style="color:#010001">placeholder </span>* <span style="color:#010001">clone</span>() <span style="color:blue">const </span>= 0; 
}; 

<span style="color:blue">template</span><<span style="color:blue">typename </span><span style="color:#010001">ValueType</span>> 
<span style="color:blue">class </span><span style="color:#010001">holder </span>: <span style="color:blue">public </span><span style="color:#010001">placeholder 
</span>{ 
<span style="color:blue">public</span>: <span style="color:green">// structors 
    </span><span style="color:#010001">holder</span>(<span style="color:blue">const </span><span style="color:#010001">ValueType </span>& <span style="color:#010001">value</span>): <span style="color:#010001">held</span>(<span style="color:#010001">value</span>) 
    { 
    } 
<span style="color:blue">public</span>: <span style="color:green">// queries 
    </span><span style="color:blue">virtual const </span><span style="color:#010001">std</span>::<span style="color:#010001">type_info </span>& <span style="color:#010001">type</span>() <span style="color:blue">const </span>{ 
        <span style="color:blue">return typeid</span>(<span style="color:#010001">ValueType</span>); 
    } 
    <span style="color:blue">virtual </span><span style="color:#010001">placeholder </span>* <span style="color:#010001">clone</span>() <span style="color:blue">const </span>{ 
        <span style="color:blue">return new </span><span style="color:#010001">holder</span>(<span style="color:#010001">held</span>); 
    } 
<span style="color:blue">public</span>: <span style="color:green">// representation 
    </span><span style="color:#010001">ValueType held</span>; 
}; </span>

首先,定义了一个基类 placeholder ,它是一个非模板的抽象类,这个抽象类的两个接口是用来抽取对保存在 my_any 中的各种类型对象的共性的,也就是,我们需要对被保存在 my_any 中的数据进行拷贝和类型查询。

然后用一个模板类 holder 类继承 placeholder 类,这个(类)派生类实现了基类的虚函数,并保存了相关的数据。注意,派生类的数据成员的类型是 ValueType,也就是完整的原对象类型,由于它是个模板类,各个类成员之间可以共享类模板参数的信息,所以,可以方便地用原数据类型来进行各种操作。

有了这两个辅助类,我们就可以这样写 my_any 了:

<span style="color:#333333"><span style="color:blue">class </span><span style="color:#010001">My_any
</span>{
    <span style="color:#010001">placeholder </span>* <span style="color:#010001">content_obj</span>;
<span style="color:blue">public</span>:
    <span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">T</span>>
    <span style="color:#010001">My_any</span>(<span style="color:#010001">T </span><span style="color:blue">const</span>& <span style="color:#010001">a_right</span>):<span style="color:#010001">content_obj</span>(<span style="color:blue">new </span><span style="color:#010001">T</span>(<span style="color:#010001">a_right</span>))
    {}

    <span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">T</span>>
    <span style="color:#010001">My_any </span>& <span style="color:blue">operator </span>= (<span style="color:#010001">T </span><span style="color:blue">const</span>& <span style="color:#010001">a_right</span>) {
        <span style="color:blue">delete </span><span style="color:#010001">content_obj</span>;
        <span style="color:#010001">content_obj </span>= <span style="color:blue">new </span><span style="color:#010001">T</span>(<span style="color:#010001">a_right</span>);
        <span style="color:blue">return </span>*<span style="color:blue">this</span>;
    }

    <span style="color:#010001">My_any</span>(<span style="color:#010001">My_any </span><span style="color:blue">const</span>& <span style="color:#010001">a_right</span>)
      : <span style="color:#010001">content_obj</span>(<span style="color:#010001">a_right</span>.<span style="color:#010001">content_obj </span>? 
          <span style="color:#010001">a_right</span>.<span style="color:#010001">content_obj</span>-><span style="color:#010001">clone</span>() : 0)
    {        
    }

    <span style="color:#010001">std</span>::<span style="color:#010001">type_info</span>& <span style="color:#010001">type</span>() <span style="color:blue">const </span>{
        <span style="color:blue">return </span><span style="color:#010001">content_obj </span>? <span style="color:#010001">content_obj</span>-><span style="color:#010001">type</span>() : <span style="color:blue">typeid</span>(<span style="color:blue">void</span>);
    }
};</span>

现在 my_any 类的 content_obj 的类型定义成 placeholder * ,在构造函数(和赋值运算符)中,我们使用 holder 类来生成真实的“备份”,由于 holder 是模板类,它可以根据赋值的对象相应地保存要我们需要的信息。这样,我们就完成了在赋值的时候的“类型擦除”啦。在 my_any 的 public 接口( type() )中,利用 placeholder 的虚函数,我们就可以进行子类提供的那些操作,而子类,已经完整地保存着我们需要的原对象的信息。

接着我们看下 boost::function 中的 Type Erasure。相比起 boost::any 来,function 库要复杂得多,因为这里只是想讲 boost::function 中的“类型擦除”,而不是 boost::function 源码剖析,所以,我们仍然本着简化简化再简化的目的,只挑着讨论一些“必要”的成分。

我们假设 function 不接受参数。为了更好的说明,我先帖代码,再一步一步解释,注意,下面是一片白花花的代码,几没有注释,千万别开骂,请跳过这段代码,后面会有分段的解释:

<span style="color:#333333"><span style="color:blue">#include </span><span style="color:#a31515"><iostream>
</span><span style="color:blue">#include </span><span style="color:#a31515"><boost/type_traits/is_pointer.hpp>
</span><span style="color:blue">#include </span><span style="color:#a31515"><boost/mpl/if.hpp>

</span><span style="color:blue">using namespace </span><span style="color:#010001">std</span>;

<span style="color:blue">union </span><span style="color:#010001">any_callable </span>{
    <span style="color:blue">void </span>(*<span style="color:#010001">fun_prt</span>) (); <span style="color:green">// 函数指针
    </span><span style="color:blue">void </span>* <span style="color:#010001">fun_obj</span>;     <span style="color:green">// 函数对象
</span>};

<span style="color:blue">template</span><<span style="color:blue">typename </span><span style="color:#010001">Func</span>, <span style="color:blue">typename </span><span style="color:#010001">R</span>>
<span style="color:blue">struct </span><span style="color:#010001">fun_prt_manager </span>{
    <span style="color:blue">static </span><span style="color:#010001">R invoke</span>(<span style="color:#010001">any_callable a_fp</span>) {
        <span style="color:blue">return reinterpret_cast</span><<span style="color:#010001">Func</span>>(<span style="color:#010001">a_fp</span>.<span style="color:#010001">fun_prt</span>)();
    }
    <span style="color:blue">static void </span><span style="color:#010001">destroy</span>(<span style="color:#010001">any_callable a_fp</span>) {}
};

<span style="color:blue">template</span><<span style="color:blue">typename </span><span style="color:#010001">Func</span>, <span style="color:blue">typename </span><span style="color:#010001">R</span>>
<span style="color:blue">struct </span><span style="color:#010001">fun_obj_manager </span>{
    <span style="color:blue">static </span><span style="color:#010001">R invoke</span>(<span style="color:#010001">any_callable a_fo</span>) {
        <span style="color:blue">return </span>(*<span style="color:blue">reinterpret_cast</span><<span style="color:#010001">Func</span>*>(<span style="color:#010001">a_fo</span>.<span style="color:#010001">fun_obj</span>))();
    }
    <span style="color:blue">static void </span><span style="color:#010001">destroy</span>(<span style="color:#010001">any_callable a_fo</span>) {
        <span style="color:blue">delete reinterpret_cast</span><<span style="color:#010001">Func</span>*>(<span style="color:#010001">a_fo</span>.<span style="color:#010001">fun_obj</span>);
    }
};

<span style="color:blue">struct </span><span style="color:#010001">funtion_ptr_tag </span>{};
<span style="color:blue">struct </span><span style="color:#010001">funtion_obj_tag </span>{};

<span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">Func</span>>
<span style="color:blue">struct </span><span style="color:#010001">get_function_tag </span>{
    <span style="color:blue">typedef typename </span><span style="color:#010001">boost</span>::<span style="color:#010001">mpl</span>::<span style="color:#010001">if_</span><
        <span style="color:#010001">boost</span>::<span style="color:#010001">is_pointer</span><<span style="color:#010001">Func</span>>, <span style="color:green">// 在VC10中标准库已经有它啦
        </span><span style="color:#010001">funtion_ptr_tag</span>,
        <span style="color:#010001">funtion_obj_tag
    </span>>::<span style="color:#010001">type FunType</span>;
};

<span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">Signature</span>>
<span style="color:blue">class </span><span style="color:#010001">My_function</span>;

<span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">R</span>>
<span style="color:blue">class </span><span style="color:#010001">My_function</span><<span style="color:#010001">R</span>()> {
    <span style="color:#010001">R </span>(*<span style="color:#010001">invoke</span>)(<span style="color:#010001">any_callable</span>);
    <span style="color:blue">void </span>(*<span style="color:#010001">destory</span>)(<span style="color:#010001">any_callable</span>);
    <span style="color:#010001">any_callable fun</span>;
<span style="color:blue">public</span>:
    ~<span style="color:#010001">My_function</span>() {
        <span style="color:#010001">clear</span>();
    }

    <span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">Func</span>>
    <span style="color:#010001">My_function</span>& <span style="color:blue">operator </span>= (<span style="color:#010001">Func a_fun</span>) {
        <span style="color:blue">typedef typename </span><span style="color:#010001">get_function_tag</span><<span style="color:#010001">Func</span>>::<span style="color:#010001">FunType fun_tag</span>;
        <span style="color:#010001">assign</span>(<span style="color:#010001">a_fun</span>, <span style="color:#010001">fun_tag</span>());
        <span style="color:blue">return </span>*<span style="color:blue">this</span>;
    }

    <span style="color:#010001">R </span><span style="color:blue">operator </span>() () <span style="color:blue">const </span>{
        <span style="color:blue">return </span><span style="color:#010001">invoke</span>(<span style="color:#010001">fun</span>);        
    }

    <span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">T</span>>
    <span style="color:blue">void </span><span style="color:#010001">assign </span>(<span style="color:#010001">T a_funPtr</span>, <span style="color:#010001">funtion_ptr_tag</span>) {
        <span style="color:#010001">clear</span>();
        <span style="color:#010001">invoke </span>= &<span style="color:#010001">fun_prt_manager</span><<span style="color:#010001">T</span>, <span style="color:#010001">R</span>>::<span style="color:#010001">invoke</span>;
        <span style="color:#010001">destory </span>= &<span style="color:#010001">fun_prt_manager</span><<span style="color:#010001">T</span>, <span style="color:#010001">R</span>>::<span style="color:#010001">destroy</span>;
        <span style="color:#010001">fun</span>.<span style="color:#010001">fun_prt </span>= <span style="color:blue">reinterpret_cast</span><<span style="color:blue">void</span>(*)()>(<span style="color:#010001">a_funPtr</span>);
    }

    <span style="color:blue">template </span><<span style="color:blue">typename </span><span style="color:#010001">T</span>>
    <span style="color:blue">void </span><span style="color:#010001">assign </span>(<span style="color:#010001">T a_funObj</span>, <span style="color:#010001">funtion_obj_tag</span>) {
        <span style="color:#010001">clear</span>();
        <span style="color:#010001">invoke </span>= &<span style="color:#010001">fun_obj_manager</span><<span style="color:#010001">T</span>, <span style="color:#010001">R</span>>::<span style="color:#010001">invoke</span>;
        <span style="color:#010001">destory </span>= &<span style="color:#010001">fun_obj_manager</span><<span style="color:#010001">T</span>, <span style="color:#010001">R</span>>::<span style="color:#010001">destroy</span>;
        <span style="color:#010001">fun</span>.<span style="color:#010001">fun_obj </span>= <span style="color:blue">reinterpret_cast</span><<span style="color:blue">void</span>*>(<span style="color:blue">new </span><span style="color:#010001">T</span>(<span style="color:#010001">a_funObj</span>));
    }

<span style="color:blue">private</span>:
    <span style="color:blue">void </span><span style="color:#010001">clear</span>() {
        <span style="color:blue">if </span>(!<span style="color:#010001">destory</span>) {
            <span style="color:#010001">destory</span>(<span style="color:#010001">fun</span>);
            <span style="color:#010001">destory </span>= 0;
        }
    }
};


<span style="color:blue">int </span><span style="color:#010001">TestFun</span>() {
    <span style="color:blue">return </span>0;
}

<span style="color:blue">class </span><span style="color:#010001">TestFunObj </span>{
<span style="color:blue">public</span>:
    <span style="color:blue">int operator</span>() () <span style="color:blue">const </span>{
        <span style="color:blue">return </span>1;
    }
};

<span style="color:blue">int </span><span style="color:#010001">main</span>(<span style="color:blue">int </span><span style="color:#010001">argc</span>, <span style="color:blue">char</span>* <span style="color:#010001">argv</span>[])
{
    <span style="color:#010001">My_function</span><<span style="color:blue">int </span>()> <span style="color:#010001">fun</span>;
    <span style="color:#010001">fun </span>= &<span style="color:#010001">TestFun</span>;
    <span style="color:#010001">cout</span><<<span style="color:#010001">fun</span>()<<<span style="color:#010001">endl</span>;
    <span style="color:#010001">fun </span>= <span style="color:#010001">TestFunObj</span>();
    <span style="color:#010001">cout</span><<<span style="color:#010001">fun</span>()<<<span style="color:#010001">endl</span>;    
}</span>

首先需要考虑的是,数据成员放什么?因为我们需要存储函数指针,也需要存储函数对象,所以,这里定义一个联合体:

<span style="color:#333333"><span style="color:blue">union </span><span style="color:#010001">any_callable </span>{
    <span style="color:blue">void </span>(*<span style="color:#010001">fun_prt</span>) (); <span style="color:green">// 函数指针
    </span><span style="color:blue">void </span>* <span style="color:#010001">fun_obj</span>;     <span style="color:green">// 函数对象
</span>};</span>

用来存放相应的“调用子”。另外两个数据成员(函数指针)是为了使用上的方便,用于存储分别针对函数指针和函数对象的相应的“操作方法”。对于函数指针和函数对象这两者,转型(cast)的动作都是不一样的,所以,我们定义了两个辅助类 fun_prt_manager 和 fun_obj_manager,它们分别定义了针对函数指针和函数对象进行类型转换,然后再引发相应的“调用”和“销毁”的动作。

接下来是类的两个 assign 函数,它们针对函数针指和函数对象,分别用不同的方法来初始化类的数据成员,你看:

<span style="color:#333333"><span style="color:#010001">invoke </span>= &<span style="color:#010001">fun_prt_manager</span><<span style="color:#010001">T</span>, <span style="color:#010001">R</span>>::<span style="color:#010001">invoke</span>;
<span style="color:#010001">destory </span>= &<span style="color:#010001">fun_prt_manager</span><<span style="color:#010001">T</span>, <span style="color:#010001">R</span>>::<span style="color:#010001">destroy</span>;
<span style="color:#010001">fun</span>.<span style="color:#010001">fun_prt </span>= <span style="color:blue">reinterpret_cast</span><<span style="color:blue">void</span>(*)()>(<span style="color:#010001">a_funPtr</span>);</span>

当 My_function 的对象是用函数指针赋值时,invoke 被 fun_prt_manager 的 static 来初始化,这样,在“调用”时就把数据成员转成函数指针。同理,可以知道函数对象时相应的做法(这里就不啰嗦了)。

但如何确定在进行赋值时,哪一个 assign 被调用呢?我想,熟悉 STL 的你,看到 funtion_ptr_tag 和 funtion_obj_tag 时就笑了,是的,这里的 get_function_tag 用了 type_traise 的技法,并且,配合了 boost::mpl 提供的静态 if_ 简化了代码。这样,我们就完成了赋值运算符的编写:

<span style="color:#333333"><span style="color:blue">    template </span><<span style="color:blue">typename </span><span style="color:#010001">Func</span>>
    <span style="color:#010001">My_function</span>& <span style="color:blue">operator </span>= (<span style="color:#010001">Func a_fun</span>) {
        <span style="color:blue">typedef typename </span><span style="color:#010001">get_function_tag</span><<span style="color:#010001">Func</span>>::<span style="color:#010001">FunType fun_tag</span>;
        <span style="color:#010001">assign</span>(<span style="color:#010001">a_fun</span>, <span style="color:#010001">fun_tag</span>());
        <span style="color:blue">return </span>*<span style="color:blue">this</span>;
    }</span>

有了这个函数,针对函数指针和函数对象,My_function 的数据成员都可以正确的初始化了。

如我们所见,在 My_function 中,使用了很多技巧和辅助类,以使得 My_funtion 可以获取在内部保存下函数指针或是函数对象,并在需要的时候,调用它们。函数指针或是函数对象,一旦赋值给 My_funtion,在外部看来,便失去了原来的“类型”信息,而只剩下一个共性——可以调用(callable)

这两个例子已经向你大概展示了 C++ 的“类型擦除”,最后,再补充一下我的理解:C++中所说的“类型擦除”不是有“标准实现”的一种“技术”(像 CRTP 或是 Trais 技术那样有明显的实现“规律”),而更像是从使用者角度而言的一种“行为模式”。比如对于一个 boost::function 对象来说,你可以用函数指针和函数对象来对它赋值,从使用者的角度看起来,就好像在赋值的过程中,funtion pointer 和 functor 自身的类型信息被抹去了一样,它们都被“剥离成”成了boost::function 对象的类型,只保留了“可以调用”这么一个共性,而 boost::any ,则只保留各种类型的“type查询”和“复制”能力这两个“共性”,其它类型信息一概抹掉。这种“类型擦除”并不是真正的语言层面擦除的,正如我们已经看到的,这一切仍然是在 C++ 的类型检查系统中工作,维持着类型安全上的优点。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++类型擦除 的相关文章

随机推荐

  • 合理设置的MTU值,解决“部分网站打不开”“上网速度慢”等问题,并且可以适当提升上网速度

    一般来讲 设计好本机的MTU值 可以解决 部分网站打不开 上网速度慢 的情况 但是如果你的共享主机或路由器的MTU设置有问题 有时问题仍然存或 或者出现网速过慢的情况 合理的设置路由器与本机的MTU值 就可以完全解决上述问题 使上网速度达到
  • AndroidJavaClass 和AndroidJavaClass

    很明显 AndroidJavaClass 就代表一个Java类 例如 com henry util 有一个静态方法 love 可以这样new AndroidJavaClass com henry util callstatic love 就
  • swagger mock文档服务器,通过 Swagger 定义自动生成 Mock 数据

    我最近的在做的项目是一个前后端分离的项目 前后端由不同的团队分别开发 并且前端的进度经常领先后端 这就意味着 当前端在开发一个新功能时 API 可能还没有准备好 不过 我们会先和后端先商议好 API Schema 然后使用 Mock 数据进
  • 使用PowerDNS实现内网DNS解析

    部署环境 公司内部安装powerdns实现局域网服务dns解析 避免通过ip访问 系统 CentOS 7 9 mysql版本 5 7 33 pdns版本 4 4 1 pdns recursor版本 4 4 2 PowerDNS admin版
  • ARTS挑战打卡的100天,我学到了这些

    前言 知道ARTS打卡计划是来源于陈皓的极客时间教程 在大学期间就知道了陈皓 左耳朵耗子 骨灰级程序员 差不多就是看着他的博客成长 后来在极客时间上发现了他的课程 就买下来了 现在学习了75 过程中发现了ARTS打卡计划 一直不敢尝试 一个
  • 第二课:变量和数据类型

    第二课 变量和数据类型 一 了解什么是变量 为什么需要它 1 计算机中的内存分类 1 RAM 运行时存储 我们的计算机程序在运行的时候 数据就会临时存储在RAM中 如果不持久化 或着突然断电 它的数据就丢失了 2 ROM 只读存储 持久化存
  • css伪元素实现方框上面打钩

    html p class skill three con item frame p css skill three con item frame width 36px height 36px background transparent b
  • 深入浅出SQL(7)-ALTER

    该系列文章系个人读书笔记及总结性内容 任何组织和个人不得转载进行商业活动 ALTER 改写历史 使用ALTER命令 可以修改表 对其套用新的设计方法 且不会影响现有数据 本章还会学到规范化的意义 我们要规范化我们的表 由于重新建了本地数据库
  • unity2d物理系统在安卓闪退的坑

    记录下2d物理系统安卓闪退的坑 之前的2d横版动作游戏和现在的幸存者游戏都出现过同样的问题 通过一步一步的排查 确定是Unity Project Setting Physics 2D Auto Sync Transfroms 这里勾选上的问
  • c盘清理

    https jingyan baidu com article ea24bc39ebefadda62b33180 html 转载于 https www cnblogs com zach0812 p 11557586 html
  • 神舟笔记本进入BIOS的方法

    最近整了一个i9 8950h的神舟笔记本 默认预装的是windows 10 总结一下进入BIOS的方法 方法一 重启电脑 黑屏的时候 不断按F2键 这个方法的优点是操作简单 缺点是有时候会进不去 直接进入桌面 方法二 系统设置 gt 更新和
  • IDEA工作常用快捷键

    ide快捷键 Intellij IDEA 移动光bai标du到行尾的快捷键是End Intellij IDEA 移动光标到行首的快捷键是Home Home End键的意思是开头 结尾 在记事dao本或word等其他文本工具中也有同样的效果
  • Java的Integer.valueOf()初窥

    前言 今天在做题时 碰到了一道选择题 就是关于Integer valueOf 的知识 题目如下 A System out println i01 i02 B System out println i01 i03 C System out p
  • js string转json有斜杠_详解json串反转义(消除反斜杠)

    JSon串在被串行化后保存在文件中 读取字符串时 是不能直接拿来用JSON parse 解析为JSON 对象的 因为它是一个字符串 不是一个合法的JSON对象格式 例如下面的JSON串保存在文件中 读出来不能直接解析 resourceId
  • C++类模板的特化(三)

    本文主要介绍类模板的特化 局部特化和缺省模板实参 1 类模板的特化 类模板的特化 Class Template Specialization 是指为特定的模板参数提供自定义实现的过程 通过特化 我们可以针对某些特定的类型或条件提供不同的行为
  • Java分页工具类

    通用分页工具类 import java io Serializable import java util List b 分页通用类 b author hcw param
  • 自定义同步器

    自定义同步器 假如你想要实现一个自定义同步器 官方推荐的做法是将继承了AQS类的子类作为自定义同步器的内部类 而自定义同步器中相关的操作只需代理成子类中对应的方法即可 往下用一个简单的例子看看如何实现自己的锁 由于同步器被分为两种模式 独占
  • [USACO

    网址链接或者是链接 题目描述 After spending so much time around his cows Farmer John has started to understand their language Moreover
  • 域用户登录的方式-使用登录主名和登录名登录

    1 1 1 域用户登录的方式 以下演示域用户使用登录名或登录主名在域中的计算机上登录 销售部的域用户账号 王瑞胜 在销售部的计算机Sales上登录 默认显示上次登录过的帐户 点击 切换用户 点击 其他用户 使用登录名登录 输入ESS wan
  • C++类型擦除

    转自 http www cnblogs com liyiwen archive 2009 12 10 1621451 html 关于类型擦除 在网上搜出来的中文资料比较少 而且一提到类型擦除 检索结果里就跑出很多 Java 和 C 相关的文