美文网首页Cpp/C++
C++ 类与泛型

C++ 类与泛型

作者: Jimmy_L_Wang | 来源:发表于2018-10-18 19:39 被阅读17次

    关键字this

    关键字this表示指向正在执行其成员函数的对象的指针。它在类的成员函数中用于引用对象本身。

    它的一个用途是检查传递给成员函数的参数是否是对象本身。例如:

    // example on this
    #include <iostream>
    using namespace std;
    
    class Dummy {
      public:
        bool isitme (Dummy& param);
    };
    
    bool Dummy::isitme (Dummy& param)
    {
      if (&param == this) return true;
      else return false;
    }
    
    int main () {
      Dummy a;
      Dummy* b = &a;
      if ( b->isitme(a) )
        cout << "yes, &a is b\n";
      return 0;
    }
    

    它也经常用于operator=通过引用返回对象的成员函数。以下是前面看到的笛卡尔向量的例子,它的operator=函数可以定义为:

    CVector& CVector::operator= (const CVector& param)
    {
      x=param.x;
      y=param.y;
      return *this;
    }
    

    实际上,this函数与编译器为类隐式生成的operator=重载操作符代码一致。

    static members 静态成员

    类可以包含静态成员,数据或函数。

    类的静态数据成员也称为“类变量”,因为同一类的所有对象只有一个公共变量,共享相同的值:,其值与这个类的一个对象之间没有区别。

    例如,它可以用于类中的一个变量,该变量可以包含一个计数器,计数器中包含该类当前分配的对象的数量,如下例所示:

    // static members in classes
    #include <iostream>
    using namespace std;
    
    class Dummy {
      public:
        static int n;
        Dummy () { n++; };
    };
    
    int Dummy::n=0;
    
    int main () {
      Dummy a;
      Dummy b[5];
      cout << a.n << '\n';
      Dummy * c = new Dummy;
      cout << Dummy::n << '\n';
      delete c;
      return 0;
    }
    

    实际上,静态成员具有与非成员变量相同的属性,但是它们具有类作用域。因此,为了避免多次声明它们,不能在类中直接初始化它们,但需要在类之外的某个地方初始化它们。如前一个例子所示:

    int Dummy::n=0;
    

    因为它是同一个类的所有对象的通用变量值,所以它可以被引用为该类的任何对象的成员,甚至可以直接引用类名(当然,这只对静态成员有效):

    cout << a.n;
    cout << Dummy::n;
    

    以上两个调用引用的是同一个变量: 所有对象共享Dummy类中的静态变量n。

    同样,它类似于非成员变量,但其名称需要像类(或对象)的成员那样被访问。

    类也可以有静态成员函数。它们表示的是相同的:类的成员对该类的所有对象都是公共的,完全充当非成员函数,但像类的成员一样被访问。因为它们类似于非成员函数,所以它们不能访问类的非静态成员(既不是成员变量,也不是成员函数)。他们都不能使用关键字this

    Const成员函数

    当类的对象被限定为const对象时:

    const MyClass myobject;
    

    从类外部对其数据成员的访问被限制为只读,就好像它的所有数据成员都是const从类外部访问它们的那些成员一样。但请注意,构造函数仍然可被调用,并且允许初始化和修改这些数据成员:

    // constructor on const object
    #include <iostream>
    using namespace std;
    
    class MyClass {
      public:
        int x;
        MyClass(int val) : x(val) {}
        int get() {return x;}
    };
    
    int main() {
      const MyClass foo(10);
    // foo.x = 20;            // not valid: x cannot be modified
      cout << foo.x << '\n';  // ok: data member x can be read
      return 0;
    }
    

    const只有当对象的成员函数被指定为const成员时,才能调用它们的成员函数; 在上面的示例中,无法从中调用成员get(未指定为const)foo。要指定成员是const成员,const关键字应遵循函数原型,在其参数的右括号之后:

    int get() const {return x;}
    

    请注意,const可用于限定成员函数返回的类型。修饰返回类型的const与指定成员的那个const不同。两者都是独立的,位于函数原型的不同位置:

    int get() const {return x;}        // const member function
    const int& get() {return x;}       // member function returning a const&
    const int& get() const {return x;} // const member function returning a const&
    

    指定的成员函数const不能修改非静态数据成员,也不能调用其他非const成员函数。实质上,const成员不得修改对象的状态。

    const对象仅限于访问标记为的成员函数const,但非const对象不受限制,因此可以同时访问constconst成员函数和非成员函数。

    您可能认为无论如何您很少会声明const对象,因此标记所有不将对象修改为const的成员都不值得努力,但const对象实际上非常常见。以类为参数的大多数函数实际上都是通过const引用来获取它们,因此,这些函数只能访问它们的const成员:

    // const objects
    #include <iostream>
    using namespace std;
    
    class MyClass {
        int x;
      public:
        MyClass(int val) : x(val) {}
        const int& get() const {return x;}
    };
    
    void print (const MyClass& arg) {
      cout << arg.get() << '\n';
    }
    
    int main() {
      MyClass foo (10);
      print(foo);
    
      return 0;
    }
    

    如果在此示例get中未指定为const成员,在print函数中无法调用arg.get(),因为const对象只能访问const成员函数。

    成员函数可以在其使用非const进行重载:即一个类可能有两个具有相同签名的成员函数,一个是const修饰,而另一个不是const修饰:在这种情况下,const修饰的函数仅当对象本身是const时调用,而非const修饰的函数是当对象本身不是时调用。

    // overloading members on constness
    #include <iostream>
    using namespace std;
    
    class MyClass {
        int x;
      public:
        MyClass(int val) : x(val) {}
        const int& get() const {return x;}
        int& get() {return x;}
    };
    
    int main() {
      MyClass foo (10);
      const MyClass bar (20);
      foo.get() = 15;         // ok: get() returns int&
    // bar.get() = 25;        // not valid: get() returns const int&
      cout << foo.get() << '\n';
      cout << bar.get() << '\n';
    
      return 0;
    }
    

    类模板

    就像我们可以创建函数模板一样,我们也可以创建类模板,允许类拥有使用模板参数作为类型的成员。例如:

    template <class T>
    class mypair {
        T values [2];
      public:
        mypair (T first, T second)
        {
          values[0]=first; values[1]=second;
        }
    };
    

    我们刚刚定义的类用于存储任何有效类型的两个元素。例如,如果我们想要声明这个类的对象来存储两个类型int的值为115和36的整数值,我们会写:

    mypair<int> myobject (115, 36);
    

    同一个类也可用于创建存储任何其他类型的对象,例如:

    mypair<double> myfloats (3.0, 2.18); 
    

    构造函数是前一个类模板中唯一的成员函数,它已在类定义本身内联定义。如果成员函数是在类模板的定义之外定义的,那么它前面应该有template <...>前缀:

    // class templates
    #include <iostream>
    using namespace std;
    
    template <class T>
    class mypair {
        T a, b;
      public:
        mypair (T first, T second)
          {a=first; b=second;}
        T getmax ();
    };
    
    template <class T>
    T mypair<T>::getmax ()
    {
      T retval;
      retval = a>b? a : b;
      return retval;
    }
    
    int main () {
      mypair <int> myobject (100, 75);
      cout << myobject.getmax();
      return 0;
    }
    

    注意上面成员函数定义的语法getmax:

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

    模板特殊化

    当特定类型作为模板参数传递时,可以为模板定义不同的实现。这称为模板特殊化。

    例如,让我们假设我们有一个非常简单的类mycontainer,它可以存储任何类型的一个元素,并且只有一个成员函数increase,会增加它的值。但是我们发现,当它存储char类型的元素时,使用函数成员uppercase的完全不同的实现会更方便,因此我们决定为该类型声明一个类模板特殊化:

    // template specialization
    #include <iostream>
    using namespace std;
    
    // class template:
    template <class T>
    class mycontainer {
        T element;
      public:
        mycontainer (T arg) {element=arg;}
        T increase () {return ++element;}
    };
    
    // class template specialization:
    template <>
    class mycontainer <char> {
        char element;
      public:
        mycontainer (char arg) {element=arg;}
        char uppercase ()
        {
          if ((element>='a')&&(element<='z'))
          element+='A'-'a';
          return element;
        }
    };
    
    int main () {
      mycontainer<int> myint (7);
      mycontainer<char> mychar ('j');
      cout << myint.increase() << endl;
      cout << mychar.uppercase() << endl;
      return 0;
    }
    

    这是用于类模板特殊化的语法:

    template <> class mycontainer <char> { ... };
    

    首先,请注意我们在类名前面加上template<>,包括一个空参数列表。这是因为所有类型都是已知的,并且此特殊化不需要模板参数,但仍然是类模板的特化,因此需要注意。

    但是比这个前缀更重要的是<char>类模板名称后面的指定的参数。此特殊化的参数本身标识模板类专用的类型char。请注意泛型类模板和特殊化之间的差异:

    template <class T> class mycontainer { ... };
    template <> class mycontainer <char> { ... };
    

    第一行是泛型模板,第二行是模板特殊化。

    当我们声明模板类的特殊化时,我们还必须定义它的所有成员,甚至是那些与泛型模板类相同的成员,因为没有从泛型模板到模板特殊化成员的“继承”关系。

    相关文章

      网友评论

        本文标题:C++ 类与泛型

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