• C++中模板的使用


    为什么使用模板:

      C++重载(overloading),重载函数一般含有不同的参数个数或不同的参数类型。如果使用MAX函数对不同类型的求最大值,就要定义不同的重载函数:

      

    int max(int x,int y)
    {
        if(x>y)
            return x;
        else 
            return y;
    }
    
    float max(float x,float y)
    {
        if(x>y)
            return x;
        else 
            return y;
    }

    但是如果要用double型,又需要重新定义。。。

    模板就是为了解决重载函数定义不全的问题,它可以实现类型参数化,即把类型定义为参数,从而实现代码的可重用性。模板分为两类:函数模板和类模板

    函数模板:

    Template<class 或 typename T>

    template是一个声明的模板的关键字,表示声明一个模板关键字class不能省。

    template <class T>
    T min(T x,T y)
    
    {
        return(x<y)?x:y;
    }

    类模板定义:

    template <class T>

    class 类名{

    };

    template <class T1,class T2>
    class student{
        T1 a;
        T2 b;
    public:
        student(T1 a,T2 b);//constructor
        void show();
    };
    
    template<class T1, class T2>
    student<T1,T2>::student(T1 a,T2 b):a(a),b(b){}
    template<class T1,class T2>
    void student<T1,T2>::show()
    {
        cout<<a<<b<<endl;
    }
    int main()
    {
        student<char ,char> s('a','b');
        s.show();
        return 0;
    }

    把类的属性定义为private,保护数据,不能外部随意访问,但是可以定义一个访问内部数据的函数接口。也可以定义一个修改内部属性的函数(那这与外部访问数据和修改数据有什么区别,这样可以在修改函数里加各种限制。哈哈)

    array < int > a(10);

    array < int >表明用int类型来代替模板类中的类参数“T”,编译器会将模板类array中所有的类参数T都用int来代替。例如类中的私有成员变量“T * num;”会被替换为“int * num;”。对类中的成员函数也会进行相同的替换,如“T & operator[]( int );”将会被替换为“int & operator[]( int );”。

    using namespace std;
    template< class T >
    class array
    {
    public:
        array( int );
        T & operator[]( int );//操作符[]取第int个数,返回类型为T。。
        const T & operator[] ( int )const;
        int getlen() const { return length; }
        ~array();
    private:
        array(){};
        int length;
        T * num;
    };
    template < class T >
    array< T >::array( int n )
    {
        num = new T[n];
        length = n;
    }
    template < class T >
    array< T >::~array()
    {
        delete[] num;
    }
    template< class T >
    T & array< T > ::operator[] ( int i )
    {
        if( i < 0 || i >= length )
            throw string( "out of bounds" );
        return num[i];
    }
    template< class T >
    const T & array< T > ::operator[] (int i) const
    {
        if( i < 0 || i >= length)
            throw string( "out of bounds" );
        return num[i];
    }
    template< class T >
    ostream & operator<<( ostream & out, const array <T> & A )
    {
        for(int i=0; i < A.getlen(); i++)
            out<< A[i] << " ";
        return out;
    }
    int main()
    {
        array< int > A(10);
        for(int i = 0; i < 10; i++)
        {
            A[i] = 2*i;       
        }
        cout<<A<<endl;
        return 0;
    }

    C++编程技巧:对象与实例的区别,new与不用new的区别:

      class A a; a 在栈里;

      class A a=new A; A 在堆里;

      new创建类对象需要指针接收,一处初始化,多处使用;     CTest* p= new CTest();//new申请的对象,则只有调用到delete时再会执行析构函数 ; 不用new   CTest mTest;//不需要手动释放,该类析构函数会自动执行

      new需要delete销毁

      new创建对象在堆空间

      频繁调用不适合用new,要申请和释放内存

    用new 生成对象,上面的例子写为:

    int main()
    {
        student<char ,char>* s=new student<char ,char >('a','b');
        s->show();
        delete(s);
        return 0;
    }

    (java中不同:JAVA:  

      A a = new A();  

      为A对象创建了一个实例,但在内存中开辟了两块空间:一块空间在堆区,存放new A()这个对象;  

      另一块空间在堆栈,也就是栈,存放a,a的值为new A()这个对象的内存地址。因为java在JVM中运行,  

      所以a 描述的内存地址不一定是这个对象真实内存的地址。 

      Object o; // 这是声明一个引用,它的类型是Object,他的值为null,还没有指向任何对象,该引用放在内存的栈区域

      o = new Object(); // new Object()句,实例化了一个对象,就是在堆中申请了一块连续空间用来存放该对象。 

      = // 运算符,将引向o指向了对象。也就是说将栈中表示引用o的内存地址的内容改写成了Object对象在堆中的地址。

      

      创建对象:student stu1 = new  student();

      通常把这条语句的动作称之为创建一个对象,其实,它包含了四个动作。

      1)右边的"new  student",是以student类为模板,在堆空间里创建一个student类的对象(也简称为student对象)。

      2)末尾的()意味着,在对象创建后,立即调用student类的构造函数,对刚生成的对象进行初始化。

          构造函数是肯定有的。如果你没写,Java会给你补上一个默认的构造函数。

      3)左边的"student stu1"创建了一个student类引用变量。所谓student类引用,就是以后可以用来指向某个 

          student对象的对象引用,它指向的是某个student对象的内存地址(有点C语言中指针的味道)。

      4)"="操作符使对象引用指向刚创建的那个student对象。

          我们可以把这条语句拆成两部分:student  stu1;                 (1)

                                                                 stu1 = new student();     (2)

            效果是一样的。

      C++ 如果直接定义类,如classA a; a 存在栈上(也意味着复制了对象a在栈中),如果classA  a = new classA就存在堆

    非类型模板参数:

    C++中:

    Student  student(20) ;  //这里student是引用 对象分配在 栈空间中,这里只是我的理解

    Student *student = new  Student(20);  //这里student是指针,new Student(20)是分配在堆内存空间的

    1. Student stu;//对象 stu 在栈上分配内存,需要使用&获取它的地址
    2. Student *pStu = &stu;//创建对象指针

           在堆上创建对象,这个时候就需要使用前面讲到的new关键字

    1. Student *pStu = new Student;

    在栈上创建出来的对象都有一个名字,比如 stu,使用指针指向它不是必须的。但是通过 new 创建出来的对象就不一样了,它在堆上分配内存,没有名字,只能得到一个指向它的指针,所以必须使用一个指针变量来接收这个指针,否则以后再也无法找到这个对象了,更没有办法使用它。也就是说,使用 new 在堆上创建出来的对象是匿名的,没法直接使用,必须要用一个指针指向它,再借助指针来访问它的成员变量或成员函数。栈内存是程序自动管理的,不能使用 delete 删除在栈上创建的对象;堆内存由程序员管理,对象使用完毕后可以通过 delete 删除。在实际开发中,new 和 delete 往往成对出现,以保证及时删除不再使用的对象,防止无用内存堆积。

    new A以及new A()的区别:

    调用new分配的内存有时候会被初始化,而有时候不会,这依赖于A的类型是否是POD(Plain old data)类型,或者它是否是包含POD成员、使用编译器生成默认构造函数的类。POD class没有用户定义的析构函数、拷贝构造函数和非静态的非POD类型的数据成员。没有用户定义的构造函数,没有私有的或者保护的非静态数据,没有基类或虚函数。它只是一些字段值的集合,没有使用任何封装以及多态特性。

    #include <iostream>  
      
    using namespace std;  
      
    struct A { int m; }; // POD  
    struct B { ~B(){}; int m; }; // non-POD, compiler generated default ctor  
    struct C { C() : m() {}; ~C(){}; int m; }; // non-POD, default-initialising m  
      
    int main()  
    {  
        A *aObj1 = new A;  
        A *aObj2 = new A();  
        cout << aObj1->m << endl;  
        cout << aObj2->m << endl;  
      
        B *bObj1 = new B;  
        B *bObj2 = new B();  
        cout << bObj1->m << endl;  
        cout << bObj2->m << endl;  
      
        C *cObj1 = new C;  
        C *cObj2 = new C();  
        cout << cObj1->m << endl;  
        cout << cObj2->m << endl;  
      
        delete aObj1;  
        delete aObj2;  
        delete bObj1;  
        delete bObj2;  
        delete cObj1;  
        delete cObj2;  
      
        return 0;  
    }

    new A:不确定的值

    new A():value-initialize A,由于是POD类型所以是zero initialization

    new B:默认构造(B::m未被初始化)

    new B():value-initialize B,zero-initialize所有字段,因为使用的默认构造函数

    new C:default-initialize C,调用默认构造函数

    new C():value-initialize C,调用默认构造函数

    在所有C++版本中,只有当A是POD类型的时候,new A和new A()才会有区别。而且,C++98和C++03会有区别。

    但是在Java中

    Student  student(20) ;  //注意:java中没有这样实例化对象的, 要想得到一个对象 必须要new出来.

    Student student ; //这个只是定义了一个引用 ,没有指向任何对象

    Student student = new Student(20);   //定义了一个引用,指向堆内存中的student对象

    常数,或指向外部链接对象的指针。

    template <class T,MAXSIZE>
    class Stack{
        T elemsp[MAXSIZE];
    };
    Stack<int, 20> intStack;
    Stack<int ,10> intStack2;
  • 相关阅读:
    Array
    自底向上的单元测试&&程序设计思想
    如何从 100 亿 URL 中找出相同的 URL?
    个人博客开发之数据库设计
    SpringBoot 优雅整合Swagger Api 自动生成文档
    SpringBoot Validation参数校验 详解自定义注解规则和分组校验
    SpringBoot Validation优雅的全局参数校验
    Spring Boot 无侵入式 实现RESTful API接口统一JSON格式返回
    SpringBoot优雅的全局异常处理
    SpringBoot2.4.x整合Mybatis-Plus3.4.x(详细教程)
  • 原文地址:https://www.cnblogs.com/fanhaha/p/7055345.html
Copyright © 2020-2023  润新知