美文网首页
2.类模板与操作符重载

2.类模板与操作符重载

作者: 偷天神猫 | 来源:发表于2015-10-22 12:10 被阅读207次

    项目地址

    C++类模板(1)

    • 与函数模板类似,类也可以通过参数泛化,从而可以构建出一组不同型别的类实例(对象)
    • 类模版实参可以是某一型别或常量(仅限int或enum)

    C++类模板(2)

    • 一个类模版的例子:<code>Stack<T></code>
    
    const std::size_t DefaultStackSize = 1024;
    template <typename T, std::size_t n = DefaultStackSize > class Stack {
    
    public:
        void Push(const T const& element);
        int Pop(T& element);
        int Top(T& element) const;
    
    private:
        std::vector<T> m_Members;
        std::size_t m_nMaxSize = n;
    };
    
    
    • T可以是任意型别
    • 模板实参也可以是一个int或enum型别的常量(此处是size_t,本质是int型别)
    • n是编译时定义的常量
    • n可以有默认值
    • size_t型别的成员变量可以用n初始化

    C++类模板(3)

    • 类模板的声明
    • 声明类模板与声明函数模板类似
    • 关键字class和typename都可以用,但还是倾向于使用typename
      template <typename T, std::size_t n> class Stack {...};
      template <class T, std::size_t n> class Stack {...};
    • 在类模板内部,T可以像其他型别一样(比如int,char等)定义成员变量和成员函数
      void Push(const T const& element);
      int Pop(T& element);
      int Top(T& element) const;
      std::vector<T> m_Members;};

    C++类模板(4)

    • 类模板的声明(续)
    • 除了Copy constructor之外,如果在类模板中需要使用到这个类本身,比如定义operator=,那么应该使用其完整的定义(Stack<T>),而不是省略型别T。如下面的例子所示:
    
    template <typename T, std::size_t n> class Stack
    {
    public:
        ...
        Stack(Stack<T, n> const&);                //copy constructor
        Stack<T>& operator = (Stack<T,n> const&); //assignment operator
    }
    
    

    C++类模板(5)

    • 类模板的实现
    • 要定义一个类模板的成员函数,则要指明其是一个模板函数,例如,Push函数的定义应当如下:
    
    template <typename T, std::size_t nMaxSize>
    void Stack<T, nMaxSize>::Push(const T const& element)
    {
        if(m_Members.size() >= m_nMaxSize) {
            //error handing...
            return;
        }
    
        m_Members.push_back(element);
    }
    
    

    C++类模板(6)

    • 类模板的实现(续)
    • Pop函数:从Stack中弹出顶部元素
    
    template <typename T, std::size_t nMaxSize>
    int Stack<T, nMaxSize>::Pop(T& element)
    {
        if (m_Members.empty())
            return 0;
    
        element = m_Members.back(); //we have to first store the back element
        m_Members.pop_back();       //because pop_back of a vector removes
        return 1;                   //the last element but doesn't return it!
    }
    
    

    C++类模板(7)

    • 类模板的实现(续)
    • GetTop函数:获取Stack顶部元素,但没有Pop出该元素
    
    template <typename T, std::size_t nMaxSize>
    int Stack<T, nMaxSize>::GetTop(T& element) const
    {
        if (m_Members.empty())
            return 0;
    
        element = m_Members.back();
        return 1;
    }
    
    

    C++类模板(8)

    • 使用类模板
    • <code>Stack<int> stack:</code>定义了一个型别为int的Stack,大小为默认值
    • <code>Stack<int, 100> Stack:</code>定义了一个型别为int、大小为100的Stack
    • 将100个元素Push到Stack中
      for (int i = 0; i < 100; i+)
      stack.Push(i);
    • Pop出Stack顶部元素:
      int element;
      stack.Pop(element);
    • 获取Stack顶部元素:
      stack.GetTop(element);
    • Stack的Stack定义:
      Stack<Stack<int> > intStackStack
      Stack<Stack<int>> intStackStack; //ERROR:>> is not allowed

    C++类模板(9)

    • 类模板特化(specializations)
    • 允许对一个类模板的某些模板参数型别做特化
    • 特化的作用或好处在于:
    • 对于某种特殊的型别,可能可以做些特别的优化或提供不同的实现
    • 避免在实例化类的时候引起一些可能产生的诡异行为
    • 特化一个类模板的时候也意味着需要特化其所有参数化的成员函数
    • 如果要特化一个类,那么做法是:
    • 声明一个带template<>的类,即空参数列表
    • 在类名称后面紧跟的尖括号中显式指明型别,例如:
      template<>
      class Stack<std::wstring> {
      ...
      }

    C++类模板(10)

    • 类模板特化(specializations)(续)
    • 特化后的具体实现可以和主模板的实现不一样,比如一下的特化增加了一个成员函数,并采用list作为元素存取的实现
    
    template <> class Stack<std::wstring> {
    
    public:
        void SetStackSize(const std::size_t n) { m_nMaxSize = n; }//添加了一个新的成员函数
        std::size_t CurrentSize() const { return m_Members.size(); }
    
        void Push(const std::wstring const& element);
        int Pop(std::wstring& element);
        int GetTop(std::wstring& element) const;
    
    private:
        std::size_t m_nMaxSize;
        std::list<std::wstring> m_Members;//采用list作为Stack的内部实现,取代了主模板中用vector实现的方式
    }
    
    

    C++类模板(11)

    • 偏特化(Partial specialization)
    • 类模板也可以被偏特化,比如主模板如果定义为:
      template <typename T1, typename T2> class MyClass {...}; //Primary————1
    • 可能产生以下几种对于主模板的偏特化:
      • 将模板参数偏特化为同样型别:
        template <typename T> class MyClass<T, T> {...}; ————2
      • 将第二个模板参数偏特化为int型别,不再是泛型的T
        template <typename T> class MyClass<T, int> {...}; ————3
      • 将两个型别偏特化为指针:
        template <typename T1, typename T2> class MyClass<T1, T2> {...}; ————4

    C++类模板(12)

    • 偏特化(Partial specialization)(续)
    • 使用示例:
    使用 原型
    MyClass<int, float> obj; MyClass<T1, T2> ————1
    MyClass<float, float> MyClass<T, T> ————2
    MyClass<float, int> MyClass<T, int> ————3
    MyClass<int, float> MyClass<T1, T2> ————4
    • 如果有不止一个偏特化同等程度地能够匹配某个调用,那么该调用具有二义性。编译器不会通过编译:
    Usage Prototype
    MyClass<int, int> obj; ERROR:matches MyClass<T, T> and MyClass<T, int>
    MyClass<int, int> obj; ERROR:matches MyClass<T, T> and MyClass<T1, T2>

    C++类模板(13)

    • 默认模板实参
    • 类似函数的默认参数,对于类模板而言也可以定义其模板参数的默认值,这些值就叫做默认模板实参
      template <typename T, typename TContainer = std::vector<T> >//使用std::vector作为默认实参
      class Stack {
      private: TContainer m_Container;
      ...
      };
    • Stack<int> intStack:使用默认的vector作为实参
    • Stack<std::wstring, std::list<std::wstring> > wstrStack:指定使用list作为容器而非默认的vector

    C++类模板(14)

    • 总结
    • 模板类的性质是,有一个或多个型别未被指定
    • 要使用一个模板类,就传入具体的型别作为实参;编译器会基于该型别来实例化类模板
    • 对于类模板而言,只有被调用到的成员函数才会被实例化
    • 类模板可以用特定的型别特化(specialization)
    • 类模板也可以用特定的型别偏特化(partial specialization)
    • 类模板参数可以有默认值

    C++操作符重载(1)

    • 关键字operator定义了一种特殊的函数,该函数的行为是将操作符应用于某一特定的型别,使之能够通过该操作符进行操作。比如,如果定义了string型别的operator+,那么连接两个字符串a和b的行为就可以用a+b进行操作
    • 操作符重载给出了操作符的不同函数
    • 编译器通过具体型别来识别某个操作符在该型别上的意义
    • 本质上operator重载就是函数,即如果定义了string型别的Append函数,那么string型别的a+b和a.Append(b)是等价的
    • 大多数内置的操作符支持重载,比如:!,!=,%,%=,&,&=,&&,||,(),,=,+,+=,-,-=,/,/=,,=,|,|=,<,<=,>,>=,=,==,<<,<<=,>>,>>=,~,[],new,delete

    C++操作符重载(2)

    • 操作符重载的一般规则
    • 不可以用operator定义的一种新的操作符,比如**
    • 对于内置型别(built-in type),不能再用operator重载
    • 操作符重载的两种情况:
      • 非静态成员函数,或
      • 静态全局函数(如果该全局函数需要访问类的private或protected成员,则须声明为friend成员)
    
    class ComplexType {
    public:
        //non-static member
        ComplexType operator<(ComplexType&);
    
        //global functions
        friend ComplexType operator+(int, ComplexType&);
    }
    
    ``
    # C++操作符重载(3)
    
    - 操作符重载的一般规则(续)
     - 一元操作符(Unary operator)如果声明为成员函数,则没有参数;如果声明为全局函数则有一个参数
     - 二元操作符(Binary operator)如果声明为成员函数,则有一个参数;如果声明为全局啊还是农户,则有两个参数
     - 如果一个操作符既能够用作一元操作,又能够用作二元操作(比如:&,*,+,-),则可以分别被重载
     - 操作符重载不能带有默认参数值
     - 除了operator=,所有其他操作符重载均可以被子类继承

    相关文章

      网友评论

          本文标题:2.类模板与操作符重载

          本文链接:https://www.haomeiwen.com/subject/ehjthttx.html