zoukankan      html  css  js  c++  java
  • [GeekBand] C++ 高级编程技术 (1)

    一、类型转换

    class Fraction

    {

    public:

     

    explicit Fraction(int num, int den=1)

    : m_numerator(num), m_denominator(den)

    { cout << m_numerator << ' ' << m_denominator << endl; }

            ......

    operator double() const {

    return (double)m_numerator / m_denominator;

    }

     

            ......

    private:

    int m_numerator; //

    int m_denominator; //

    };

        

    1. 转出去——利用转换函数(Conversion Function)

      从一个类中转出去的方法是对int(),doube()等"操作符"进行重载。

    值得注意的一点是,所有的转换函数都不需要声明返回参数类型,也不需要输入参数。可以通过(double) obj_of_Fraction 进行调用。

    除了显式使用转换函数以外,转换函数也会进行隐式调用,例如:

    Fraction f(3,5);

    double d = 4 + f;

    在这种情况下,首先,编译器会检查是否存在+运算符的合适的函数重载。如果没有合适的函数重载被定义,则会先将f隐式转换为double,再进行相加。

    1. 转进来——合理使用explicit关键字避免二义性

      构造函数可以实现"转进来"的目的。Fraction(4)可以创造一个分数,其分母为1,分子为4。

      同样地,"转进来"的构造函数也可以被隐式调用。

      例如对于语句 Fraction d2 = f + 4,第一步,编译器仍然会检查是否存在+运算符的合适的重载Fraction::operator +(double);如果没有合适的函数重载被定义,则会将4转换为分数,然后再进行相加。

      不过,这种隐式转换有时反而是不利的,可能会导致二义性。考虑下面这种情况,如果在类的定义中存在下面的函数:

    Fraction operator+(const Fraction& f) {

    cout << "operator+(): " << f.m_numerator << ' ' << f.m_denominator << endl;

    //... plus

    return f;

    }

    那么,对于d2 = f + 4;有以下两种路径,产生了二义性从而不能通过。

    1. f转double->double 相加->结果转回Fraction->赋值
    2. 4转Fraction->Fraction相加->赋值

    为了消除这一二义性,在构造函数前面加了explicit关键字。Explicit关键字的意义是,这个函数只可以被显示调用,而不能被任何形式地隐式调用。 (由于这种声明,Fraction a = 1也不能被调用了。)

    如果给拷贝构造函数加explicit关键字,则Fraction B(A) 可以使用,Fraction B = A也不能使用了。

     

    二、pointer-like class

    1) 智能指针

    template <class T>

    class shared_ptr{

    public :

    T& operator* () const { return *px; }

    //*运算符作为成员函数默认的重载方式为操作符在前,注意返回值为引用、

    T* operator->() const { return px; }

            //注意返回值为指针类型

    shared_ptr( T *p ):px(p){}

         //以上三个函数是几乎所有智能指针都需要的。

     

    private:

    T* px;

    ......

    }

    这里需要特别说明一下对于->操作符重载的调用,考虑如下代码:

    shared_ptr<Foo> sp(new Foo);

    Foo f(*sp);

    sp->method();

     

    将会自动转化为px->method();这里涉及到了'->'的特别行为。'->'运算符的一个特点是,它不会再运算过程中被消耗。'sp->'的运算结果是px,又由于'->'没有被消掉,因此其转换为px->method()。

    2)迭代器——一种特别的智能指针

    其和智能指针基本一致,只是还需要对++和—运算符进行重载。

    T& operator *() const{ return (*node).data; }

    T* operator->() const{ return &(operator*();)}

    需要注意的一点是,这里显式调用了operator*(),这种形式是非常方便的。

    三、Function-like class(仿函数)

    即重载()运算符(又名函数调用操作符,其重载时的默认调用位置就在中间),在标准库中被广泛使用。

    template <class pair>

    struct select1st: public uniary_func<.......>{

    const typename pair::first_type&

    operator()(const pair &x) const{

    return x.first;

    }

    }

    这样就能够像函数一样使用这个类,select1st(pair(1,2));

    四、泛型编程初步

    泛型编程和面向对象编程是C++的两个重要方面。

    • 类模板和函数模板的作用

      通常来讲,类模板可以被用来做容器、迭代器等;而函数模板则是被用来做泛型算法。

    只需要使用template <class/typename T>进行声明

    类模板在使用时需要指明参数类型,但函数模板可以自动进行实参推导。

    • 类的成员模板

       

      template <class T1, class T2>

      struct pair {

      typedef T1 first_type;

      typedef T2 second_type;

       

      T1 first;

      T2 second;

      pair() : first(T1()), second(T2()) {}

      pair(const T1& a, const T2& b) : first(a), second(b) {}

       

      template <class U1, class U2>

      pair(const pair<U1, U2>& p) : first(p.first), second(p.second) {}

      };

      注意其中的成员函数——拷贝构造函数。通过这种成员模板方式,可以实现拷贝构造。

    • 模板的特化

      模板是一种泛化操作,而特化则是一种逆向的操作,其语法是:

      template<>

      struct hash<int>{

      size_t operator() (int x) const {return x;}

      }

    通过这种形式,可以给int参数类型单独制定方法,同样的语法也可以使用与函数模板。

    cout<<hash<int>()(1000);

    其中的hash<int>() 是声明了一个无参临时对象。

    • 模板偏特化

    4.1个数上的偏特化

    与全特化类似,这种偏特化也是固定参数进行特化。

    泛化声明:

    template<class T1,class T2>

    class vector

    {......}

     

    偏特化声明:

    template<class T2>

    class vector <bool,T2>

    {......}

    注意偏特化声明中的T2虽然仍写为T2,但与全泛化的T2并没有关系。

    4.2范围上的偏特化

    这种偏特化是指对于 const * p类型、*p类型、p类型的限制,偏特化就是按照范围划分不同的方法,例如如下的代码:

    template <class T>

    class C{......}

     

    template <class T>

    class C<T*>{......}

    当仅有第一种定义时,会自动调用第一种类型的模板;然而当有上两种定义时,对于指针类型的模板参数,可以使用特别的方法定义,这种分类是十分必要的。

    • 模板模板参数

    template<typename T,

    template <typename T>

    class Container

    >

    class XCls

    {

    private:

    Container<T> c;

    public:

    XCls()

    {

    for(long i=0; i< 100; ++i)

    c.insert(c.end(), T());

    }

    };

     

    注意第二个模板参数template <typename T> class SmartPtr ,其本身又是一个模板

    使用方法:

    template <class T>

    using Lst = list<T, allocator<T>>;

    XCls<string, Lst> mylist;

     

    注意:下面的用法不是模板模板参数。

    template< class T ,class Sequence = deque<T>>

    class stack{

    protected:

    Sequence C;

    }

    其中第二个参数制定了stack要依靠什么基本容器类型进行重载,其具有默认值deque。

    使用方式为: stack<int,list<int>> s2; 这里的list已经不是模板了,它在具体化之前已经变成了普通的模板参数。

    • 数量不定的模板参数(Since C++ 11,使用包)

       

      void print(){}

      template< typename T , typename... Types >

      void print(const T& firstArg,const Types &... args){

      cout<<firstArg<<endl;

      print(args...);

      }

    …是C++11中的新类型,…代表包(package),也就是以容器存储的数量不定的参数。对于这种数量不定的模板参数,始终采取将参数分为一个和一包的办法,这个包可以作为模板参数,也可以作为函数参数。在print()中,其采用了递归调用方法。也可以将第二参数其作为容器使用。

    五、C++的两个常用语法糖

    1. auto类型

      list<string> C;

      auto ite = find(C.begin(),C.end(),target);

      其中的auto类型会自动被推导成 list<string>::iterator

    2. 新的for语法

      for(variable:container){

      ......

      }

    变量会在容器中遍历,直到变量结尾。

    e.g.

    for(auto i:{1,2,3,4}){

    ......

    }

    这种语法搭配引用可以修改容器中的值。

    for(auto &elem:vector_obj){

    elem *= 3;

    }

  • 相关阅读:
    在日本被禁止的コンプガチャ設計
    Starling常见问题解决办法
    Flixel引擎学习笔记
    SQLSERVER中修复状态为Suspect的数据库
    T4 (Text Template Transformation Toolkit)实现简单实体代码生成
    创建Linking Server in SQL SERVER 2008
    Linq to Sql 与Linq to Entities 生成的SQL Script与分页实现
    Linq to Entity 的T4 模板生成代码
    在VisualStudio2008 SP1中调试.net framework 源代码
    使用HttpModules实现Asp.net离线应用程序
  • 原文地址:https://www.cnblogs.com/shawnChi/p/5740625.html
Copyright © 2011-2022 走看看