精华内容
下载资源
问答
  • 泛型编程

    千次阅读 2018-09-03 16:39:02
    (2)泛型编程 概念(concepts) 模型化(modeling) 强化(refinement) 2.泛型编程概念  泛型编程(Generic Programming) 指在多种数据类型上皆可操作。和面向对象编程不同,它并不要求额...

    1.C++两种抽象方法

    (1)面向对象编程

    • 封装(Encapsulation)
    • 继承(Inheritance)
    • 多态(Polymorphism)

    (2)泛型编程

    • 概念(concepts)
    • 模型化(modeling)
    • 强化(refinement)

    2.泛型编程概念

        泛型编程(Generic Programming) 指在多种数据类型上皆可操作。和面向对象编程不同,它并不要求额外的间接层来调用函数,而是使用完全一般化并可重复使用的算算效率与针对某特定数据类型而设计的算法相同。

    (1)概念(concepts)

        类型必须满足的一组条件。基本的concepts中有赋值、默认构造、相等比较、小于判断等。

    (2)模型(modeling)

        当类型满足这个条件,即为该concepts的一个model。

        如果能够复制类型X的值,或者赋给X对象一个新值的话,则类型X是Assignable的一个model。

    (3)强化(refinement)

       如果concept  C2满足concept  C1的所有条件,再加上其他额外条件,则C2是C1的强化(refinement)。

         

    3.泛型编程实现

    (1)模板

    • 函数模板
    • 类模板

    (2)STL

        STL(Standard Template Library,标准模板库) 是泛型编程思想的实现,于1994年被纳入C++标准程序库。STL是一种高效、泛型、可交互操作的软件组件,巨大,而且可以扩充,它包含很多计算机基本算法和数据结构,而且将算法与数据结构完全分离,其中算法是泛型的,不与任何特定数据结构或对象类型系在一起。

        STL以迭代器(Iterators)和容器(Containers)为基础,是一种泛型算法(Generic Algorithms)库,容器的存在使这些算法有东西可以操作。STL包含泛型算法(algorithms)、泛型指针(iterators)、泛型容器(containers)、函数对象(function objects)。

        迭代器(Iterators)是STL的核心,它们是泛型指针,是一种指向其他对象(objects)的对象,迭代器能够遍历由对象所形成的区间(range)。

       迭代器一般分为五种:

    • Input Iterator     只读,单向移动,如STL中的istream_iterator。
    • Output Iterator   只写,单向移动,如STL中的ostream_iterator。
    • Forward Iterator   具有读、写性,单向移动。
    • Bidirections Iterator   ​​​​​​​具有读、写性,双向移动。
    • ​​​​​​​Random Access Iterator   具有读、写性,随机访问

           

     

    4.泛型编程优缺点

    1)通用性强

                泛型算法是建立在语法一致性上,运用到的类型集是无限的/非绑定的。

    2)效率

                编译期能确定静态类型信息,其效率与针对某特定数据类型而设计的算法相同。

    3)类型检查严

                静态类型信息被完整的保存在了编译期,编译期发觉更多潜在的错误。

    4)二进制复用性

               泛型算法是建立在语法一致性上,语法是代码层面的,语法上的约定无法体现在二进制层面。泛型算法实现的库,其源代码基本上是必须公开的。而传统的C库全是以二进制形式发布的。

    展开全文
  • 通过泛型编程可以使编写的代码被很多不同的类型所共享,大大提高了代码的重用性。下面给出一个自定义泛型类的例子:public class Pair{privateT first;privateT second;publicPair(T first,T second){this.first =...

    1. 泛型类

    泛型类就是具有一个或者多个类型变量的类,在Java集合框架中大量使用了泛型类。通过泛型编程可以使编写的代码被很多不同的类型所共享,大大提高了代码的重用性。

    下面给出一个自定义泛型类的例子:

    public class Pair{privateT first;privateT second;publicPair(T first,T second)

    {this.first =first;this.second =second;

    }publicT getFirst()

    {returnfirst;

    }publicT getSecond()

    {returnsecond;

    }public voidsetFirst(T first)

    {this.first =first;

    }public voidsetSecond(T second)

    {this.second =second;

    }

    }

    使用普通的类名替换类型变量T就可以实例化泛型类型,如:Pair,Java的泛型类类似于C++的模板类。

    2. 泛型方法

    Java还可以定义带有类型参数的方法,即泛型方法,泛型方法可以定义在泛型类中,也可以定义在普通类中。

    public classArrayHelper

    {public static T getMiddle(T[] array)

    {return array[array.length / 2];

    }

    }

    上述的泛型方法,参数是泛型数组,返回值是泛型变量,在修饰符后面跟有表示这是泛型方法。调用一个泛型方法时在方法名前的"<>"加入具体类型,如:ArrayHelper.getMiddle(new String[]{"left","middle","right"}) ,其实大多数情况下也可以省略。

    3. 类型变量的限定

    有些时候,我们希望能使用不同的类型,但又希望这类型能满足某些约束条件,这就要依靠对类型变量的限定。

    public classArrayHelper

    {public static T max(T[] array)

    {

    T max= array[0];for(int i = 1; i < array.length; i++)if(array[i].compareTo(max) > 0)

    max=array[i];

    return max;

    }

    }

    我们希望使用compareTo方法来比较泛型数组中的每个元素,从而选择出最大的那个元素,而这就要求类型必须实现了Comparable接口,我们就可以对类型变量T作出如下限定:

    public classArrayHelper

    {public static T max(T[] array)

    {

    T max= array[0];for(int i = 1; i < array.length; i++)if(array[i].compareTo(max) > 0)

    max=array[i];

    return max;

    }

    }

    一个类型变量可以有多个限定,如: 。

    4. 类型擦除

    我们定义一个泛型类型后,就可以适配多种不同的类型,然而实际上虚拟机只知道一个原始类型,例如,对于上面定义个Pair,其对应的原始类型如下:

    public classPair

    {privateObject first;privateObject second;publicPair(Object first,Object second)

    {this.first =first;this.second =second;

    }publicObject getFirst()

    {returnfirst;

    }publicObject getSecond()

    {returnsecond;

    }public voidsetFirst(Object first)

    {this.first =first;

    }public voidsetSecond(Object second)

    {this.second =second;

    }

    }

    即将T替换成了Object类,实际上是将T替换成限定的类型。假设,则就会将T替换成Comparable,如果有多个限定类型,则替换成第一个限定类型。如果没有限定类型,就替换成Object类,这个过程即类型擦除。

    所以泛型类编译成字节码后就是一个普通的类。

    5. 泛型类的继承规则

    (1)假设有一个print方法打印雇员对,参数是Pari

    public void print(Pair)

    {

    .....

    }

    Manager类是Employee类的子类,那么Pair是Pair的子类么?,可以传入print方法么?答案是不行,Pair不是Pair的子类。

    (2) 永远可以将参数化类型转换成原始类型,如:Pair  pair = new Pair("Jack","Mike"); 这是为了与泛型之前的遗留代码能够保持衔接。

    (3)泛型类可以像普通类一样继承其他类,实现接口。如: class Pair  implements Comparable 。

    6. 通配符类型

    Pair extends Fruit> 表示任何泛型Pair类型,它的类型参数是Fruit的子类。Pair和Pair都是Pair extends Fruit>的子类型。

    Pair super Apple>表示任何泛型Pair类型,它的类型参数是Apple的父类。Pair和Pair都是Pair super Apple>的子类型。

    这样,就可以利用参数多态了,修改上面的print方法:

    public void print(Pair extends Fruit>)

    {

    .....

    }

    现在就可以传入Pair和Pair等作为参数了。

    但是对于通配符类型的多态,使用父类变量引用子类实例时,需要注意以下的问题:

    Pair apples = new Pair(new Apple("apple1"),new Apple("apple2"));

    Pair extends Fruit> fruits =apples;//下面两句调用setFirst方法编译报错,因为编译器只知道Pair中保存类型的是Fruit的子类,但不知道具体是什么类型。

    fruits.setFirst(new Apple("apple3"));

    fruits.setFirst(new Banana("banana1"));//下面调用getFirst方法不会出错,因为编译器知道Pair中保存的类型一定是Fruit的子类,转换成Fruit类不会出错。

    Fruit first = frutis.getFirst();

    即对于 extends Type> 通配符类型,使用父类变量引用子类实例时,不能对子类实例进行写,只能读。

    Pair apples = new Pair(new Apple("apple1"),new Apple("apple2"));

    Pair super Apple> fruits =apples;

    fruits.setFirst(new GoodApple("apple3")); //这一句调用setFirst方法不会出错,因为编译器知道Pair中保存的类型一定是Apple类的父类,因此,传入Apple类对象或者Apple类的子类对象都是可以的

    fruits.setFirst(new Fruit("banana1"));//传入Apple类的父类对象就会编译错误

    Fruit first = frutis.getFirst(); //编译不通过,因为编译器知道Pair中保存的类型是Apple类的父类,但不知道具体是什么类,因此,只能赋值给Object类的变量

    即对于 super Type> 通配符类型,使用父类变量引用子类实例时,不能对子类实例进行读,只能写。

    参考资料 《Java核心技术》

    展开全文
  • JAVA泛型编程总结——by书生1介绍Java泛型编程是JDK1.5版本后引入的。泛型让编程人员能够使用类型抽象,通常用于集合里面。下面是一个不用泛型例子:List myIntList=new LinkedList(); //1myIntList.add(newInteger...

    JAVA泛型编程总结

    ——by书生

    1介绍

    Java泛型编程是JDK1.5版本后引入的。泛型让编程人员能够使用类型抽象,通常用于集合里面。下面是一个不用泛型例子:

    List myIntList=new LinkedList(); //1

    myIntList.add(newInteger(0)); //2

    Integer x=(Integer)myIntList.iterator().next(); //3

    注意第3行代码,但这是让人很不爽的一点,因为程序员肯定知道自己存储在List里面的对象类型是Integer,但是在返回列表中元素时,还是必须强制转换类型,这是为什么呢?原因在于,编译器只能保证迭代器的next()方法返回的是Object类型的对象,为保证Integer变量的类型安全,所以必须强制转换。

    这种转换不仅显得混乱,更可能导致类型转换异常ClassCastException,运行时异常往往让人难以检测到。保证列表中的元素为一个特定的数据类型,这样就可以取消类型转换,减少发生错误的机会,这也是泛型设计的初衷。下面是一个使用了泛型的例子:

    List myIntList=newLinkedList(); //1’

    myIntList.add(newInteger(0)); //2’

    Integerx=myIntList.iterator().next(); //3’

    在第1行代码中指定List中存储的对象类型为Integer,这样在获取列表中的对象时,不必强制转换类型了。

    2定义简单的泛型

    下面是一个引用自java.util包中的接口List和Iterator的定义,其中用到了泛型技术。

    public interface List {

    void add(E x);

    Iterator iterator();

    }

    public interface Iterator {

    E next();

    boolean hasNext();

    }

    这跟原生类型没有什么区别,只是在接口后面加入了一个尖括号,尖括号里面是一个类型参数(定义时就是一个格式化的类型参数,在调用时会使用一个具体的类型来替换该类型)。

    也许可以这样认为,List表示List中的类型参数E会被替换成Integer。

    public interface IntegerList {

    void add(Integer x)

    Iterator iterator();

    }

    类型擦除指的是通过类型参数合并,将泛型类型实例关联到同一份字节码上。编译器只为泛型类型生成一份字节码,并将其实例关联到这份字节码上,因此泛型类型中的静态变量是所有实例共享的。此外,需要注意的是,一个static方法,无法访问泛型类的类型参数,因为类还没有实例化,所以,若static方法需要使用泛型能力,必须使其成为泛型方法。类型擦除的关键在于从泛型类型中清除类型参数的相关信息,并且再必要的时候添加类型检查和类型转换的方法。在使用泛型时,任何具体的类型都被擦除,唯一知道的是你在使用一个对象。比如:List和List在运行事实上是相同的类型。他们都被擦除成他们的原生类型,即List。因为编译的时候会有类型擦除,所以不能通过同一个泛型类的实例来区分方法,如下面的例子编译时会出错,因为类型擦除后,两个方法都是List类型的参数,因此并不能根据泛型类的类型来区分方法。

    /*会导致编译时错误*/

    public class Erasure{

    public void test(List ls){

    System.out.println("Sting");

    }

    public void test(List li){

    System.out.println("Integer");

    }

    }

    那么这就有个问题了,既然在编译的时候会在方法和类中擦除实际类型的信息,那么在返回对象时又是如何知道其具体类型的呢?如List编译后会擦除掉String信息,那么在运行时通过迭代器返回List中的对象时,又是如何知道List中存储的是String类型对象呢?

    擦除在方法体中移除了类型信息,所以在运行时的问题就是边界:即对象进入和离开方法的地点,这正是编译器在编译期执行类型检查并插入转型代码的地点。泛型中的所有动作都发生在边界处:对传递进来的值进行额外的编译期检查,并插入对传递出去的值的转型。

    3.泛型和子类型

    为了彻底理解泛型,这里看个例子:(Apple为Fruit的子类)

    List apples = new ArrayList(); //1

    List fruits = apples; //2

    第1行代码显然是对的,但是第2行是否对呢?我们知道Fruit fruit = new Apple(),这样肯定是对的,即苹果肯定是水果,但是第2行在编译的时候会出错。这会让人比较纳闷的是一个苹果是水果,为什么一箱苹果就不是一箱水果了呢?可以这样考虑,我们假定第2行代码没有问题,那么我们可以使用语句fruits.add(new Strawberry())(Strawberry为Fruit的子类)在fruits中加入草莓了,但是这样的话,一个List中装入了各种不同类型的子类水果,这显然是不可以的,因为我们在取出List中的水果对象时,就分不清楚到底该转型为苹果还是草莓了。

    通常来说,如果Foo是Bar的子类型,G是一种带泛型的类型,则G不是G的子类型。这也许是泛型学习里面最让人容易混淆的一点。

    4.通配符

    4.1通配符?

    先看一个打印集合中所有元素的代码。

    //不使用泛型

    void printCollection(Collection c) {

    Iterator i=c.iterator();

    for (k=0;k < c.size();k++) {

    System.out.println(i.next());

    }

    }

    //使用泛型

    void printCollection(Collection c) {

    for (Object e:c) {

    System.out.println(e);

    }

    }

    很容易发现,使用泛型的版本只能接受元素类型为Object类型的集合如ArrayList();如果是ArrayList,则会编译时出错。因为我们前面说过,Collection并不是所有集合的超类。而老版本可以打印任何类型的集合,那么如何改造新版本以便它能接受所有类型的集合呢?这个问题可以通过使用通配符来解决。修改后的代码如下所示:

    //使用通配符?,表示可以接收任何元素类型的集合作为参数

    void printCollection(Collection> c) {

    for (Object e:c) {

    System.out.println(e);

    }

    }

    这里使用了通配符?指定可以使用任何类型的集合作为参数。读取的元素使用了Object类型来表示,这是安全的,因为所有的类都是Object的子类。这里就又出现了另外一个问题,如下代码所示,如果试图往使用通配符?的集合中加入对象,就会在编译时出现错误。需要注意的是,这里不管加入什么类型的对象都会出错。这是因为通配符?表示该集合存储的元素类型未知,可以是任何类型。往集合中加入元素需要是一个未知元素类型的子类型,正因为该集合存储的元素类型未知,所以我们没法向该集合中添加任何元素。唯一的例外是null,因为null是所有类型的子类型,所以尽管元素类型不知道,但是null一定是它的子类型。

    Collection> c=new ArrayList();

    c.add(newObject()); //compile time error,不管加入什么对象都出错,除了null外。

    c.add(null); //OK

    另一方面,我们可以从List> lists中获取对象,虽然不知道List中存储的是什么类型,但是可以肯定的是存储的类型一定是Object的子类型,所以可以用Object类型来获取值。如for(Object obj: lists),这是合法的。

    4.2边界通配符

    1)?extends通配符

    假定有一个画图的应用,可以画各种形状的图形,如矩形和圆形等。为了在程序里面表示,定义如下的类层次:

    public abstract class Shape {

    public abstract void draw(Canvas c);

    }

    public class Circle extends Shape {

    private int x,y,radius;

    public void draw(Canvas c) { ... }

    }

    public class Rectangle extends Shape {

    private int x,y,width,height;

    public void draw(Canvasc) { ... }

    }

    为了画出集合中所有的形状,我们可以定义一个函数,该函数接受带有泛型的集合类对象作为参数。但是不幸的是,我们只能接收元素类型为Shape的List对象,而不能接收类型为List的对象,这在前面已经说过。为了解决这个问题,所以有了边界通配符的概念。这里可以采用public void drawAll(List extends Shape> shapes)来满足条件,这样就可以接收元素类型为Shape子类型的列表作为参数了。

    //原始版本

    public void drawAll(List shapes) {

    for (Shapes:shapes) {

    s.draw(this);

    }

    }

    //使用边界通配符的版本

    public void drawAll(List shapes) {

    for (Shapes:shapes) {

    s.draw(this);

    }

    }

    这里就又有个问题要注意了,如果我们希望在List shapes中加入一个矩形对象,如下所示:

    shapes.add(0,new Rectangle()); //compile-time error

    那么这时会出现一个编译时错误,原因在于:我们只知道shapes中的元素时Shape类型的子类型,具体是什么子类型我们并不清楚,所以我们不能往shapes中加入任何类型的对象。不过我们在取出其中对象时,可以使用Shape类型来取值,因为虽然我们不知道列表中的元素类型具体是什么类型,但是我们肯定的是它一定是Shape类的子类型。

    2)?super通配符

    这里还有一种边界通配符为?super。比如下面的代码:

    List shapes = new ArrayList();

    List super Cicle> cicleSupers = shapes;

    cicleSupers.add(new Cicle()); //OK, subclass of Cicle also OK

    cicleSupers.add(new Shape()); //ERROR

    这表示cicleSupers列表存储的元素为Cicle的超类,因此我们可以往其中加入Cicle对象或者Cicle的子类对象,但是不能加入Shape对象。这里的原因在于列表cicleSupers存储的元素类型为Cicle的超类,但是具体是Cicle的什么超类并不清楚。但是我们可以确定的是只要是Cicle或者Circle的子类,则一定是与该元素类别兼容。

    3)边界通配符总结

    如果你想从一个数据类型里获取数据,使用? extends通配符

    如果你想把对象写入一个数据结构里,使用? super通配符

    如果你既想存,又想取,那就别用通配符。

    5.泛型方法

    考虑实现一个方法,该方法拷贝一个数组中的所有对象到集合中。下面是初始的版本:

    static void fromArrayToCollection(Object[]a, Collection> c) {

    for (Object o:a) {

    c.add(o); //compile time error

    }

    }

    可以看到显然会出现编译错误,原因在之前有讲过,因为集合c中的类型未知,所以不能往其中加入任何的对象(当然,null除外)。解决该问题的一种比较好的办法是使用泛型方法,如下所示:

    static void fromArrayToCollection(T[] a, Collectionc){

    for(T o : a) {

    c.add(o);// correct

    }

    }

    注意泛型方法的格式,类型参数需要放在函数返回值之前。然后在参数和返回值中就可以使用泛型参数了。具体一些调用方法的实例如下:

    Object[] oa = new Object[100];

    Collection co = new ArrayList();

    fromArrayToCollection(oa, co);// T inferred to be Object

    String[] sa = new String[100];

    Collection cs = new ArrayList();

    fromArrayToCollection(sa, cs);// T inferred to be String

    fromArrayToCollection(sa, co);// T inferred to be Object

    Integer[] ia = new Integer[100];

    Float[] fa = new Float[100];

    Number[] na = new Number[100];

    Collectioncn = new ArrayList();

    fromArrayToCollection(ia, cn);// T inferred to be Number

    fromArrayToCollection(fa, cn);// T inferred to be Number

    fromArrayToCollection(na, cn);// T inferred to be Number

    fromArrayToCollection(na, co);// T inferred to be Object

    fromArrayToCollection(na, cs);// compile-time error

    注意到我们调用方法时并不需要传递类型参数,系统会自动判断类型参数并调用合适的方法。当然在某些情况下需要指定传递类型参数,比如当存在与泛型方法相同的方法的时候(方法参数类型不一致),如下面的一个例子:

    publicvoidgo(T t) {

    System.out.println("generic function");

    }

    publicvoidgo(String str) {

    System.out.println("normal function");

    }

    publicstaticvoidmain(String[] args) {

    FuncGenric fg = newFuncGenric();

    fg.go("haha");//打印normal function

    fg.go("haha");//打印generic function

    fg.go(newObject());//打印generic

    function

    fg.go(newObject());//打印generic

    function

    }

    如例子中所示,当不指定类型参数时,调用的是普通的方法,如果指定了类型参数,则调用泛型方法。可以这样理解,因为泛型方法编译后类型擦除,如果不指定类型参数,则泛型方法此时相当于是public void go(Object t)。而普通的方法接收参数为String类型,因此以String类型的实参调用函数,肯定会调用形参为String的普通方法了。如果是以Object类型的实参调用函数,则会调用泛型方法。

    6.其他需要注意的小点

    1)方法重载

    在JAVA里面方法重载是不能通过返回值类型来区分的,比如代码一中一个类中定义两个如下的方法是不容许的。但是当参数为泛型类型时,却是可以的。如下面代码二中所示,虽然形参经过类型擦除后都为List类型,但是返回类型不同,这是可以的。

    /*代码一:编译时错误*/

    public class Erasure{

    public void test(int i){

    System.out.println("Sting");

    }

    public int test(int i){

    System.out.println("Integer");

    }

    }

    /*代码二:正确*/

    public class Erasure{

    public void test(List ls){

    System.out.println("Sting");

    }

    public int test(List li){

    System.out.println("Integer");

    }

    }

    2)泛型类型是被所有调用共享的

    所有泛型类的实例都共享同一个运行时类,类型参数信息会在编译时被擦除。因此考虑如下代码,虽然ArrayList和ArrayList类型参数不同,但是他们都共享ArrayList类,所以结果会是true。

    Listl1 = new ArrayList();

    Listl2 = new ArrayList();

    System.out.println(l1.getClass() == l2.getClass()); //True

    3)instanceof

    不能对确切的泛型类型使用instanceOf操作。如下面的操作是非法的,编译时会出错。

    Collection cs = new ArrayList();

    if (cs instanceof Collection){…}// compile error.如果改成instanceof Collection>则不//会出错。

    4)泛型数组问题

    不能创建一个确切泛型类型的数组。如下面代码会出错。

    List[] lsa = new ArrayList[10];//compile error.

    因为如果可以这样,那么考虑如下代码,会导致运行时错误。

    List[] lsa = new ArrayList[10]; // 实际上并不允许这样创建数组

    Object o = lsa;

    Object[] oa = (Object[]) o;

    Listli = new ArrayList();

    li.add(new Integer(3));

    oa[1] = li;// unsound, but passes run time store check

    String s = lsa[1].get(0); //run-time error - ClassCastException

    因此只能创建带通配符的泛型数组,如下面例子所示,这回可以通过编译,但是在倒数第二行代码中必须显式的转型才行,即便如此,最后还是会抛出类型转换异常,因为存储在lsa中的是List类型的对象,而不是List类型。最后一行代码是正确的,类型匹配,不会抛出异常。

    List>[] lsa = new List>[10]; // ok, array of unbounded wildcard type

    Object o = lsa;

    Object[] oa = (Object[]) o;

    Listli = new ArrayList();

    li.add(new Integer(3));

    oa[1] = li; //correct

    String s = (String) lsa[1].get(0);// run time error, but cast is explicit

    Integer it = (Integer)lsa[1].get(0); // OK

    参考资料

    http://www.aqee.net/java-generics-quick-tutorial/

    http://www.infoq.com/cn/articles/cf-java-generics

    http://blog..net/daniel_h1986/article/details/5708605

    http://www..com/stephen-liu74/archive/2012/01/20/2228938.html

    sun官方文档:generics-tutorial.pdf

    展开全文
  • 泛型编程

    2010-08-16 23:06:00
    泛型编程让你编写完全一般化并可重复使用的算法,其效率与针对某特定数据类型而设计的算法相同。泛型编程的代表作品STL是一种高效、泛型、可交互操作的软件组件。所谓泛型(Genericity),是指具有在多种数据...

    泛型编程让你编写完全一般化并可重复使用的算法,其效率与针对某特定数据类型而设计的算法相同。泛型编程的代表作品STL是一种高效、泛型、可交互操作的软件组件。所谓泛型(Genericity),是指具有在多种数据类型上皆可操作的含意,与模板有些相似。STL巨大,而且可以扩充,它包含很多计算机基本算法和数据结构,而且将算法与数据结构完全分离,其中算法是泛型的,不与任何特定数据结构或对象类型系在一起。STL以迭代器 (Iterators)和容器(Containers)为基础,是一种泛型算法(Generic Algorithms)库,容器的存在使这些算法有东西可以操作。STL包含各种泛型算法(algorithms)、泛型指针(iterators)、泛型容器(containers)以及函数对象(function objects)。STL并非只是一些有用组件的集合,它是描述软件组件抽象需求条件的一个正规而有条理的架构。

      泛型的第一个好处是编译时的严格类型检查。这是集合框架最重要的特点。此外,泛型消除了绝大多数的类型转换。如果没有泛型,当你使用集合框架时,你不得不进行类型转换。

      关于泛型的理解可以总结下面的一句话,它是把数据类型作为一种参数传递进来。

     

     

     

    泛型编程(generic programming通用编程/类属编程)和面向过程以及面向对象一起,是混合型程序设计语言C++所包含的三种编程风范(paradigm范例/范型)。

    传统C++的泛型编程,仅仅局限于简单的模版技术。标准C++新引入了容器(container)、迭代器(iterator)、分配器(allocator)和STL(Standard Template Library标准模板库)等概念和内容,才真正进入泛型编程的广阔天地。

    1)引言                                                     
    面向过程的编程,可以将常用代码段封装在一个函数中,然后通过函数调用来达到目标代码重用的目的。面向对象的方法,则可以通过类的继承来实现(对象的目标)代码的重用。

    如果需要编写一个可用于不同数据类型的算法,可以采用的方法有:

    <!--[if !supportLists]-->l     <!--[endif]-->面向过程——对源代码进行复制和修改,生成不同数据类型版本的算法函数,调用时需要对数据类型进行手工的判断;

    <!--[if !supportLists]-->l     <!--[endif]-->面向对象——可以在一个类中,编写多个同名函数,它们的算法一致,但是所处理数据的类型不同,当然函数的输入参数类型也不同,可通过函数重载来自动调用对应数据类型版本的函数。

    显然,这两种方法都需编写了多个相同算法的不同函数,不能做到代码重用。它们二者之间的主要差别,只是调用的方便与否。

    如果采用泛型编程(例如可采用以类型作为参数的传统C++的模板技术),就可以做到源代码级的重用:

    <!--[if !supportLists]-->l     <!--[endif]-->泛型编程——编写以类型作为参数的一个模板函数,在调用时再将参数实例化为具体的数据类型。

    (注意,模版的实例化,是在编译阶段完成的,属于静态性质的方法,与C/C++的宏替代方式非常相似,但是更为安全、更易理解、且更加有效。)

    为了实现一个,在具有不同组织结构(如数组、链表、队列、堆栈等)、含同一类型(如char、int、float、struct S或class C等)的数据或对象的集合(容器)上的,与具体数据类型无关的参数化通用算法(如排序、检索、复制、合并等)。光有模版是远远不够的,还需要能够表示这种集合的容器、能够在容器中遍历的迭代器、能够为算法和容器实现抽象存储的分配器、能够在不同容器之间进行转换的适配器等等。这些正是泛型编程的研究内容,也是STL要实现的目标。

    2)泛型编程与STL
    泛型编程是一种面向算法的多态技术,STL是它的一种具体实现。

    (1)泛型编程
    在计算机科学中,泛型(generic)是一种允许一个值取不同数据类型(所谓多态)的技术,强调使用这种技术的编程风格被称为泛型编程(generic programming通用编程/类属编程)。

    泛型编程研究对软件组件的系统化组织。目标是推出一种针对算法、数据结构和内存分配机制的分类方法,以及其他能够带来高度可重用性、模块化和可用性的软件工具。

    与针对问题和数据的面向对象的方法不同,泛型编程中强调的是算法。是一类通用的参数化算法,它们对各种数据类型和各种数据结构都能以相同的方式进行工作,从而实现源代码级的软件重用。

    例如,不管(容器)是数组、队列、链表、还是堆栈,不管里面的元素(类型)是字符、整数、浮点数、还是对象,都可以使用同样的(迭代器)方法来遍历容器内的所有元素、获取指定元素的值、添加或删除元素,从而实现排序、检索、复制、合并等各种操作和算法。

    泛型编程的通用化算法,是建立在各种抽象化基础之上的:利用参数化模版来达到数据类型的抽象化、利用容器和迭代器来达到数据结构的抽象化、利用分配器和适配器来达到存储分配和界面接口的抽象化。

    (2)STL
    STL(Standard Template Library标准模板库)是泛型编程思想的实际体现和具体实现,它是一种为泛型组件建立大型标准库的可扩展架构。STL本身,与面向对象无关,也与具体的程序设计语言无关。

    STL的目标是,在不损失效率的基础上,进行抽象。这里的不损失效率,是指尽最大努力来保证其所有的泛型算法是最优的,并且和手工编码具有同样的运行效率。抽象的基础是数学和冯·诺依曼计算模型(存储程序体系结构)。

    如果用数学语言来描述,STL的本质就是:不同的数据结构,对应于不同的地址代数结构、以及不同的地址连接方式。从数据结构的一个地址,转向下一个地址的一些列操作,就对应于迭代器。在数据结构中添加和删除地址的操作,就对应于容器。

    STL将容器看作是结构的泛化,它们都拥有成员,可以描述整体和局部这一现实世界事物的关键属性。STL使用了赋值的算法,要求采用面向值的语义。STL还假定对容器中的数据和对象,定义了全序。

    STL的主要内容是6种组件:容器、泛型算法、迭代器、函数对象、分配器和适配器等。在标准C++中,STL是作为C++标准库的一部分而出现的。

    展开全文

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 15,654
精华内容 6,261
关键字:

泛型编程