C++ 高级篇(一)—— 模板(Templates)



模板(Templates)是ANSI-C++ 标准中新引入的概念。如果你使用的 C++ 编译器不符合这个标准,则你很可能不能使用模板。

 

 

函数模板( Function templates)

模板(Templates)使得我们可以生成通用的函数,这些函数能够接受任意数据类型的参数,可返回任意类型的值,而不需要对所有可能的数据类型进行函数重载。这在一定程度上实现了宏(macro)的作用。它们的原型定义可以是下面两种中的任何一个:

template <class identifier> function_declaration;
template <typename identifier> function_declaration;

上面两种原型定义的不同之处在关键字class 或 typename的使用。它们实际是完全等价的,因为两种表达的意思和执行都一模一样。

例如,要生成一个模板,返回两个对象中较大的一个,我们可以这样写:

template <class GenericType>

GenericType GetMax (GenericType a, GenericType b) { return (a>b?a:b); }

在第一行声明中,我们已经生成了一个通用数据类型的模板,叫做GenericType。因此在其后面的函数中,GenericType 成为一个有效的数据类型,它被用来定义了两个参数a和 b ,并被用作了函数GetMax的返回值类型。

GenericType 仍没有代表任何具体的数据类型;当函数 GetMax 被调用的时候,我们可以使用任何有效的数据类型来调用它。这个数据类型将被作为pattern来代替函数中GenericType 出现的地方。用一个类型pattern来调用一个模板的方法如下:

function <type> (parameters);

例如,要调用GetMax 来比较两个int类型的整数可以这样写:

int x,y;
GetMax <int> (x,y);

因此,GetMax 的调用就好像所有的GenericType 出现的地方都用int 来代替一样。

这里是一个例子:

// function template

#include <iostream.h>

template <class T> T GetMax (T a, T b) {

    T result;

    result = (a>b)? a : b;

    return (result);

}

int main () {

    int i=5, j=6, k;

    long l=10, m=5, n;

    k=GetMax(i,j);

    n=GetMax(l,m);

    cout << k << endl;

    cout << n << endl;

    return 0;
}

6
10

(在这个例子中,我们将通用数据类型命名为T 而不是 GenericType ,因为T短一些,并且它是模板更为通用的标示之一,虽然使用任何有效的标示符都是可以的。)

在上面的例子中,我们对同样的函数GetMax()使用了两种参数类型:int 和 long,而只写了一种函数的实现,也就是说我们写了一个函数的模板,用了两种不同的pattern来调用它。

如你所见,在我们的模板函数 GetMax() 里,类型 T 可以被用来声明新的对象

T result;

result 是一个T类型的对象, 就像a 和 b一样,也就是说,它们都是同一类型的,这种类型就是当我们调用模板函数时写在尖括号<> 中的类型。

在这个具体的例子中,通用类型 T 被用作函数GetMax 的参数,不需要说明<int>或 <long>,编译器也可以自动检测到传入的数据类型,因此,我们也可以这样写这个例子:

int i,j;
GetMax (i,j);

因为i 和j 都是int 类型,编译器会自动假设我们想要函数按照int进行调用。这种暗示的方法更为有用,并产生同样的结果:

// function template II

#include <iostream.h>

template <class T> T GetMax (T a, T b) {

    return (a>b?a:b);

}

int main () {

    int i=5, j=6, k;

    long l=10, m=5, n;

    k=GetMax(i,j);

    n=GetMax(l,m);

    cout << k << endl;

    cout << n << endl;

    return 0;

}

6
10

注意在这个例子的main() 中我们如何调用模板函数GetMax() 而没有在括号<>中指明具体数据类型的。编译器自动决定每一个调用需要什么数据类型。

因为我们的模板函数只包括一种数据类型 (class T), 而且它的两个参数都是同一种类型,我们不能够用两个不同类型的参数来调用它:

int i;
long l;
k = GetMax (i,l);

上面的调用就是不对的,因为我们的函数等待的是两个同种类型的参数。

我们也可以使得模板函数接受两种或两种以上类型的数据,例如:

template <class T>

T GetMin (T a, U b) { return (a<b?a:b); }

在这个例子中,我们的模板函数 GetMin() 接受两个不同类型的参数,并返回一个与第一个参数同类型的对象。在这种定义下,我们可以这样调用该函数:

int i,j;
long l;
i = GetMin <int, long> (j,l);

或者,简单的用

i = GetMin (j,l);

虽然 j 和 l 是不同的类型。

 

类模板(Class templates)

我们也可以定义类模板(class templates),使得一个类可以有基于通用类型的成员,而不需要在类生成的时候定义具体的数据类型,例如:

template <class T>

class pair {

    T values [2];

public:

    pair (T first, T second) {

        values[0]=first;

        values[1]=second;

    }

};

上面我们定义的类可以用来存储两个任意类型的元素。例如,如果我们想要定义该类的一个对象,用来存储两个整型数据115 和 36 ,我们可以这样写:

pair<int> myobject (115, 36);

我们同时可以用这个类来生成另一个对象用来存储任何其他类型数据,例如:

pair<float> myfloats (3.0, 2.18);

在上面的例子中,类的唯一一个成员函数已经被inline 定义。如果我们要在类之外定义它的一个成员函数,我们必须在每一函数前面加template <... >。

// class templates

#include <iostream.h>

template <class T> class pair {
     T value1, value2;
public:
    pair (T first, T second) {

        value1=first;

        value2=second;

    }

    T getmax ();

};

template <class T>

T pair::getmax (){

    T retval;

    retval = value1>value2? value1 : value2;

    return retval;

}

int main () {

    pair myobject (100, 75);

    cout << myobject.getmax();

    return 0;

}

100

注意成员函数getmax 是怎样开始定义的:

template <class T>
T pair::getmax ()

所有写 T 的地方都是必需的,每次你定义模板类的成员函数的时候都需要遵循类似的格式(这里第二个T表示函数返回值的类型,这个根据需要可能会有变化)。

 

模板特殊化(Template specialization)

模板的特殊化是当模板中的pattern有确定的类型时,模板有一个具体的实现。例如假设我们的类模板pair 包含一个取模计算(module operation)的函数,而我们希望这个函数只有当对象中存储的数据为整型(int)的时候才能工作,其他时候,我们需要这个函数总是返回0。这可以通过下面的代码来实现:

// Template specialization

#include <iostream.h>

template <class T> class pair {

    T value1, value2;

public:

    pair (T first, T second){

        value1=first;

        value2=second;

    }

    T module () {return 0;}

};

template <>

class pair <int> {

    int value1, value2;

public:

    pair (int first, int second){

        value1=first;

        value2=second;

    }

    int module ();

};

template <>

int pair<int>::module() {

    return value1%value2;

}

int main () {

    pair <int> myints (100,75);

    pair <float> myfloats (100.0,75.0);

    cout << myints.module() << '\n';

    cout << myfloats.module() << '\n';

    return 0;

}

25
0

由上面的代码可以看到,模板特殊化由以下格式定义:

template <> class class_name <type>

这个特殊化本身也是模板定义的一部分,因此,我们必须在该定义开头写template <>。而且因为它确实为一个具体类型的特殊定义,通用数据类型在这里不能够使用,所以第一对尖括号<> 内必须为空。在类名称后面,我们必须将这个特殊化中使用的具体数据类型写在尖括号<>中。

当我们特殊化模板的一个数据类型的时候,同时还必须重新定义类的所有成员的特殊化实现(如果你仔细看上面的例子,会发现我们不得不在特殊化的定义中包含它自己的构造函数 constructor,虽然它与通用模板中的构造函数是一样的)。这样做的原因就是特殊化不会继承通用模板的任何一个成员。

 

模板的参数值(Parameter values for templates)

除了模板参数前面跟关键字class 或 typename 表示一个通用类型外,函数模板和类模板还可以包含其它不是代表一个类型的参数,例如代表一个常数,这些通常是基本数据类型的。例如,下面的例子定义了一个用来存储数组的类模板:

// array template

#include <iostream.h>

template <class T, int N>

class array {

    T memblock [N];

public:

    void setmember (int x, T value);

    T getmember (int x);

};

template <class T, int N>

void array<T,N>::setmember (int x, T value) {

    memblock[x]=value;

}

template <class T, int N>

T array<T,N>::getmember (int x) {

    return memblock[x];

}

int main () {

    array <int,5> myints;

    array <float,5> myfloats;

    myints.setmember (0,100);

    myfloats.setmember (3,3.1416);

    cout << myints.getmember(0) << '\n';

    cout << myfloats.getmember(3) << '\n';

    return 0;

}

100
3.1416

我们也可以为模板参数设置默认值,就像为函数参数设置默认值一样。

下面是一些模板定义的例子:

template <class T> // 最常用的:一个class 参数。

template <class T, class U> // 两个class 参数。

template <class T, int N> // 一个class 和一个整数。

template <class T = char> // 有一个默认值。

template <int Tfunc (int)> // 参数为一个函数。

 

模板与多文件工程 (Templates and multiple-file projects)

从编译器的角度来看,模板不同于一般的函数或类。它们在需要时才被编译(compiled on demand),也就是说一个模板的代码直到需要生成一个对象的时候(instantiation)才被编译。当需要instantiation的时候,编译器根据模板为特定的调用数据类型生成一个特殊的函数。

当工程变得越来越大的时候,程序代码通常会被分割为多个源程序文件。在这种情况下,通常接口(interface)和实现(implementation)是分开的。用一个函数库做例子,接口通常包括所有能被调用的函数的原型定义。它们通常被定义在以.h 为扩展名的头文件 (header file) 中;而实现 (函数的定义) 则在独立的C++代码文件中。

模板这种类似宏(macro-like) 的功能,对多文件工程有一定的限制:函数或类模板的实现 (定义) 必须与原型声明在同一个文件中。也就是说我们不能再 将接口(interface)存储在单独的头文件中,而必须将接口和实现放在使用模板的同一个文件中。

回到函数库的例子,如果我们想要建立一个函数模板的库,我们不能再使用头文件(.h) ,取而代之,我们应该生成一个模板文件(template file),将函数模板的接口和实现 都放在这个文件中 (这种文件没有惯用扩展名,除了不要使用.h扩展名或不要不加任何扩展名)。在一个工程中多次包含同时具有声明和实现的模板文件并不会产生链接错误 (linkage errors),因为它们只有在需要时才被编译,而兼容模板的编译器应该已经考虑到这种情况,不会生成重复的代码。

模板(Templates)是ANSI-C++ 标准中新引入的概念。如果你使用的 C++ 编译器不符合这个标准,则你很可能不能使用模板。

 

 

函数模板( Function templates)

模板(Templates)使得我们可以生成通用的函数,这些函数能够接受任意数据类型的参数,可返回任意类型的值,而不需要对所有可能的数据类型进行函数重载。这在一定程度上实现了宏(macro)的作用。它们的原型定义可以是下面两种中的任何一个:

template <class identifier> function_declaration;
template <typename identifier> function_declaration;

上面两种原型定义的不同之处在关键字class 或 typename的使用。它们实际是完全等价的,因为两种表达的意思和执行都一模一样。

例如,要生成一个模板,返回两个对象中较大的一个,我们可以这样写:

template <class GenericType>

GenericType GetMax (GenericType a, GenericType b) { return (a>b?a:b); }

在第一行声明中,我们已经生成了一个通用数据类型的模板,叫做GenericType。因此在其后面的函数中,GenericType 成为一个有效的数据类型,它被用来定义了两个参数a和 b ,并被用作了函数GetMax的返回值类型。

GenericType 仍没有代表任何具体的数据类型;当函数 GetMax 被调用的时候,我们可以使用任何有效的数据类型来调用它。这个数据类型将被作为pattern来代替函数中GenericType 出现的地方。用一个类型pattern来调用一个模板的方法如下:

function <type> (parameters);

例如,要调用GetMax 来比较两个int类型的整数可以这样写:

int x,y;
GetMax <int> (x,y);

因此,GetMax 的调用就好像所有的GenericType 出现的地方都用int 来代替一样。

这里是一个例子:

// function template

#include <iostream.h>

template <class T> T GetMax (T a, T b) {

    T result;

    result = (a>b)? a : b;

    return (result);

}

int main () {

    int i=5, j=6, k;

    long l=10, m=5, n;

    k=GetMax(i,j);

    n=GetMax(l,m);

    cout << k << endl;

    cout << n << endl;

    return 0;
}

6
10

(在这个例子中,我们将通用数据类型命名为T 而不是 GenericType ,因为T短一些,并且它是模板更为通用的标示之一,虽然使用任何有效的标示符都是可以的。)

在上面的例子中,我们对同样的函数GetMax()使用了两种参数类型:int 和 long,而只写了一种函数的实现,也就是说我们写了一个函数的模板,用了两种不同的pattern来调用它。

如你所见,在我们的模板函数 GetMax() 里,类型 T 可以被用来声明新的对象

T result;

result 是一个T类型的对象, 就像a 和 b一样,也就是说,它们都是同一类型的,这种类型就是当我们调用模板函数时写在尖括号<> 中的类型。

在这个具体的例子中,通用类型 T 被用作函数GetMax 的参数,不需要说明<int>或 <long>,编译器也可以自动检测到传入的数据类型,因此,我们也可以这样写这个例子:

int i,j;
GetMax (i,j);

因为i 和j 都是int 类型,编译器会自动假设我们想要函数按照int进行调用。这种暗示的方法更为有用,并产生同样的结果:

// function template II

#include <iostream.h>

template <class T> T GetMax (T a, T b) {

    return (a>b?a:b);

}

int main () {

    int i=5, j=6, k;

    long l=10, m=5, n;

    k=GetMax(i,j);

    n=GetMax(l,m);

    cout << k << endl;

    cout << n << endl;

    return 0;

}

6
10

注意在这个例子的main() 中我们如何调用模板函数GetMax() 而没有在括号<>中指明具体数据类型的。编译器自动决定每一个调用需要什么数据类型。

因为我们的模板函数只包括一种数据类型 (class T), 而且它的两个参数都是同一种类型,我们不能够用两个不同类型的参数来调用它:

int i;
long l;
k = GetMax (i,l);

上面的调用就是不对的,因为我们的函数等待的是两个同种类型的参数。

我们也可以使得模板函数接受两种或两种以上类型的数据,例如:

template <class T>

T GetMin (T a, U b) { return (a<b?a:b); }

在这个例子中,我们的模板函数 GetMin() 接受两个不同类型的参数,并返回一个与第一个参数同类型的对象。在这种定义下,我们可以这样调用该函数:

int i,j;
long l;
i = GetMin <int, long> (j,l);

或者,简单的用

i = GetMin (j,l);

虽然 j 和 l 是不同的类型。

 

类模板(Class templates)

我们也可以定义类模板(class templates),使得一个类可以有基于通用类型的成员,而不需要在类生成的时候定义具体的数据类型,例如:

template <class T>

class pair {

    T values [2];

public:

    pair (T first, T second) {

        values[0]=first;

        values[1]=second;

    }

};

上面我们定义的类可以用来存储两个任意类型的元素。例如,如果我们想要定义该类的一个对象,用来存储两个整型数据115 和 36 ,我们可以这样写:

pair<int> myobject (115, 36);

我们同时可以用这个类来生成另一个对象用来存储任何其他类型数据,例如:

pair<float> myfloats (3.0, 2.18);

在上面的例子中,类的唯一一个成员函数已经被inline 定义。如果我们要在类之外定义它的一个成员函数,我们必须在每一函数前面加template <... >。

// class templates

#include <iostream.h>

template <class T> class pair {
     T value1, value2;
public:
    pair (T first, T second) {

        value1=first;

        value2=second;

    }

    T getmax ();

};

template <class T>

T pair::getmax (){

    T retval;

    retval = value1>value2? value1 : value2;

    return retval;

}

int main () {

    pair myobject (100, 75);

    cout << myobject.getmax();

    return 0;

}

100

注意成员函数getmax 是怎样开始定义的:

template <class T>
T pair::getmax ()

所有写 T 的地方都是必需的,每次你定义模板类的成员函数的时候都需要遵循类似的格式(这里第二个T表示函数返回值的类型,这个根据需要可能会有变化)。

 

模板特殊化(Template specialization)

模板的特殊化是当模板中的pattern有确定的类型时,模板有一个具体的实现。例如假设我们的类模板pair 包含一个取模计算(module operation)的函数,而我们希望这个函数只有当对象中存储的数据为整型(int)的时候才能工作,其他时候,我们需要这个函数总是返回0。这可以通过下面的代码来实现:

// Template specialization

#include <iostream.h>

template <class T> class pair {

    T value1, value2;

public:

    pair (T first, T second){

        value1=first;

        value2=second;

    }

    T module () {return 0;}

};

template <>

class pair <int> {

    int value1, value2;

public:

    pair (int first, int second){

        value1=first;

        value2=second;

    }

    int module ();

};

template <>

int pair<int>::module() {

    return value1%value2;

}

int main () {

    pair <int> myints (100,75);

    pair <float> myfloats (100.0,75.0);

    cout << myints.module() << '\n';

    cout << myfloats.module() << '\n';

    return 0;

}

25
0

由上面的代码可以看到,模板特殊化由以下格式定义:

template <> class class_name <type>

这个特殊化本身也是模板定义的一部分,因此,我们必须在该定义开头写template <>。而且因为它确实为一个具体类型的特殊定义,通用数据类型在这里不能够使用,所以第一对尖括号<> 内必须为空。在类名称后面,我们必须将这个特殊化中使用的具体数据类型写在尖括号<>中。

当我们特殊化模板的一个数据类型的时候,同时还必须重新定义类的所有成员的特殊化实现(如果你仔细看上面的例子,会发现我们不得不在特殊化的定义中包含它自己的构造函数 constructor,虽然它与通用模板中的构造函数是一样的)。这样做的原因就是特殊化不会继承通用模板的任何一个成员。

 

模板的参数值(Parameter values for templates)

除了模板参数前面跟关键字class 或 typename 表示一个通用类型外,函数模板和类模板还可以包含其它不是代表一个类型的参数,例如代表一个常数,这些通常是基本数据类型的。例如,下面的例子定义了一个用来存储数组的类模板:

// array template

#include <iostream.h>

template <class T, int N>

class array {

    T memblock [N];

public:

    void setmember (int x, T value);

    T getmember (int x);

};

template <class T, int N>

void array<T,N>::setmember (int x, T value) {

    memblock[x]=value;

}

template <class T, int N>

T array<T,N>::getmember (int x) {

    return memblock[x];

}

int main () {

    array <int,5> myints;

    array <float,5> myfloats;

    myints.setmember (0,100);

    myfloats.setmember (3,3.1416);

    cout << myints.getmember(0) << '\n';

    cout << myfloats.getmember(3) << '\n';

    return 0;

}

100
3.1416

我们也可以为模板参数设置默认值,就像为函数参数设置默认值一样。

下面是一些模板定义的例子:

template <class T> // 最常用的:一个class 参数。

template <class T, class U> // 两个class 参数。

template <class T, int N> // 一个class 和一个整数。

template <class T = char> // 有一个默认值。

template <int Tfunc (int)> // 参数为一个函数。

 

模板与多文件工程 (Templates and multiple-file projects)

从编译器的角度来看,模板不同于一般的函数或类。它们在需要时才被编译(compiled on demand),也就是说一个模板的代码直到需要生成一个对象的时候(instantiation)才被编译。当需要instantiation的时候,编译器根据模板为特定的调用数据类型生成一个特殊的函数。

当工程变得越来越大的时候,程序代码通常会被分割为多个源程序文件。在这种情况下,通常接口(interface)和实现(implementation)是分开的。用一个函数库做例子,接口通常包括所有能被调用的函数的原型定义。它们通常被定义在以.h 为扩展名的头文件 (header file) 中;而实现 (函数的定义) 则在独立的C++代码文件中。

模板这种类似宏(macro-like) 的功能,对多文件工程有一定的限制:函数或类模板的实现 (定义) 必须与原型声明在同一个文件中。也就是说我们不能再 将接口(interface)存储在单独的头文件中,而必须将接口和实现放在使用模板的同一个文件中。

回到函数库的例子,如果我们想要建立一个函数模板的库,我们不能再使用头文件(.h) ,取而代之,我们应该生成一个模板文件(template file),将函数模板的接口和实现 都放在这个文件中 (这种文件没有惯用扩展名,除了不要使用.h扩展名或不要不加任何扩展名)。在一个工程中多次包含同时具有声明和实现的模板文件并不会产生链接错误 (linkage errors),因为它们只有在需要时才被编译,而兼容模板的编译器应该已经考虑到这种情况,不会生成重复的代码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/401813.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PPT到底是天使还是魔鬼?

说老实话&#xff0c;我非常不喜欢那些PPT的培训者一味地鼓吹PPT的重要性&#xff0c;视觉化思维的重要性&#xff0c;PPT在演讲中的重要性等等&#xff0c;我很清楚他们这么说是因为他们必须这么说&#xff0c;因为如果不把PPT的重要性强调一下&#xff0c;谁来上他的课呢&…

ultraedit正则表达式

一般使用ultraedit中的Perl风格的正则表达式&#xff0c;下面是perl正则的基本语法 perl中的元字符如下&#xff1a; ^ 表示一行的开头&#xff1b; $ 表示一行的结尾&#xff1b; ( ) 表示一个匹配块的&#xff0c;可以对匹配上的块通过$1,$2...进行读取&#xff0c;…

微软云介绍

微软云介绍 http://msdn.microsoft.com/zh-cn/ff380142 什么是云开发&#xff1f;&#xff08;概述&#xff09; 云计算是指远程运行并通过 Internet 访问的计算机和应用程序。在云计算中&#xff0c;虚拟机在大型数据中心中运行&#xff0c;并取代了物理 PC 和服务器。通过将许…

IO端口和IO内存的区别及分别使用的函数接口

IO端口和IO内存的区别及分别使用的函数接口 每个外设都是通过读写其寄存器来控制的。外设寄存器也称为I/O端口&#xff0c;通常包括&#xff1a;控制寄存器、状态寄存器和数据寄存器三大类。根据访问外设寄存器的不同方式&#xff0c;可以把CPU分成两大类。一类CPU&#xff08…

Win8 HTML5与JS编程学习笔记(二)

近期一直受到win8应用的Grid布局困扰&#xff0c;经过了半下午加半个晚上的奋斗&#xff0c;终于是弄明白了Grid布局方法的规则。之前我是阅读的微软官方的开发教程&#xff0c;书中没有详细说明CSS3的布局规则&#xff0c;自己鼓捣了半天也是一头雾水&#xff0c;于是又找到了…

Windows下的Qt Creator的安装

采用Qt和Qt creator分别下载和安装的方式&#xff1a;&#xff08;需要手动设置关联Qt和Qt Creator&#xff09; 一、软件下载 从http://qt-project.org/downloads分别下载Qt和Qt Creator&#xff1a; Qt使用4.7.2版本&#xff1a;qt-win-opensource-4.7.2-mingw.exe Qt Creato…

进程 、进程组、会话、控制终端之间的关系

一个进程组可以包含多个进程 进程组中的这些进程之间不是孤立的&#xff0c;他们彼此之间或者存在者父子、兄弟关系&#xff0c;或者在功能有相近的联系。 那linux为什么要有进程组呢&#xff1f;其实提供进程组就是方便管理这些进程。假设要完成一个任务&#xff0c;需要同时并…

Angularjs 通过asp.net web api认证登录

Angularjs 通过asp.net web api认证登录 Angularjs利用asp.net mvc提供的asp.net identity&#xff0c;membership实现居于数据库的用户名/密码的认证登录 环境 Vs.net 2013 Asp.net mvc web api Individual user accounts Angularjs Underscore 新建一个asp.net mvc web api …

PANIC: Unreachable code reached.

为什么80%的码农都做不了架构师&#xff1f;>>> Caused by: java.lang.RuntimeException: PANIC: Unreachable code reached.at cryptix.jce.provider.cipher.BlockCipher.engineGetParameters(BlockCipher.java:244)at javax.crypto.Cipher.checkCryptoPerm(Ciphe…

[LeetCode Online Judge]系列-求二维平面内在一条直线上的最大点数

2019独角兽企业重金招聘Python工程师标准>>> Max Points on a Line Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. 题目是:在2D平面内给N个点,求最多多少个点在一个直线上. 以下是AC的解决方式: /*** Defi…

【思科】GNS3模拟静态NAT/动态NAT

实验拓扑&#xff1a;实验目的&#xff1a;利用NAT技术&#xff0c;使C1,C2能与R2连通。实验步骤&#xff1a;如图配置好IP地址&#xff0c;为C1,C2指定网关192.168.1.1。2.在R1上配置静态NAT.假定为C1配置公网地址12.0.0.3&#xff0c;来实现与R2的连通。这样&#xff0c;静态…

xml 解析

2019独角兽企业重金招聘Python工程师标准>>> 各大数据接口大全&#xff1a;http://blog.sina.com.cn/s/articlelist_2127818045_10_1.html package com.test.junit; import static org.junit.Assert.*; import java.io.ByteArrayInputStream;import java.io.InputSt…

理解Lucene/Solr的缓存

缓存对于提高搜索引擎的吞吐量&#xff0c;降低CPU占用率极为重要。Lucene/Solr在这块做了很多的工作。Lucene/Solr中默认提供了5种缓存&#xff0c;同时solr还提供扩展缓存接口&#xff0c;允许开发者自定义缓存。1 缓存的基本原理Solr实现了两种策略的缓存&#xff1a;LRU(Le…

深入了解tcmalloc(一):windows环境下无缝拦截技术初探

2019独角兽企业重金招聘Python工程师标准>>> 概述&#xff1a; 又到了一个总结提炼的阶段&#xff0c;这次想具体聊聊游戏引擎中使用的内存管理模块tcmalloc组件的使用心得。项目的前期曾经遇到过内存瓶颈&#xff0c;特别是windows系统下的客户端程序在经历长时间运…

删除数据库日志文件的方法

原文:删除数据库日志文件的方法你曾经有在执行SQL的时候&#xff0c;数据库报事务日志已满&#xff0c;然后执行报错。然后纠结于怎么删除数据库日志&#xff0c;捣鼓半天吗&#xff0c;现在就提供两种删除日志文件的方法&#xff0c;希望能够帮到你&#xff01; 阅读目录 方法…

Android屏幕大小适配问题解决

&#xfeff;一、一些基本概念 1、长度&#xff08;真实长度&#xff09;&#xff1a;英寸、inch 2、分辨率&#xff1a;density 每英寸像素数 dpi&#xff08;密度&#xff09; 3、像素&#xff1a;px 4、dip的公式&#xff1a;px /dipdpi/160 所以 dip 类似于英寸、长度&am…

开源 免费 java CMS - FreeCMS1.9 会员组管理

2019独角兽企业重金招聘Python工程师标准>>> 项目地址&#xff1a;http://www.freeteam.cn/ 会员组管理 会员组分为两种&#xff0c;一级是经验会员组&#xff0c;一种是特殊会员组。 经验会员组的会员会根据经验自动变更&#xff0c;特殊会员组不会自动变更&…

蓝桥杯历年预赛习题

标题&#xff1a;啤酒和饮料 啤酒每罐2.3元&#xff0c;饮料每罐1.9元。小明买了若干啤酒和饮料&#xff0c;一共花了82.3元。 我们还知道他买的啤酒比饮料的数量少&#xff0c;请你计算他买了几罐啤酒。 注意&#xff1a;答案是一个整数。请通过浏览器提交答案。 不要书…

产品经理,你来自江湖

2019独角兽企业重金招聘Python工程师标准>>> 产品经理&#xff0c;你来自江湖 ----论产品经理的发展中的技能与技能图书 导读&#xff1a; 过去的15年中&#xff0c;在互联网行业&#xff0c;我认为没有一个职位比产品经理对行业的推动更大&#xff0c;这些年里&…

小谈深度优先搜索

最近读了一本算法书&#xff0c;书中提到了深度优先算法&#xff0c;于是我就整理了一下。 引入小题&#xff1a; 解决方案&#xff1a;这里先使用最简单最常用的穷举法时行求解。&#xff08;此代码中的book数组起到了标记的作用&#xff0c;可以参考桶装法排序了解标记的好处…