类(II)

重载运营商

类,基本上,定义新类型的C ++代码中使用。和类型在C ++不仅通过代码结构和分配方式进行交互。他们还通过运营商的方式进行交互。例如,采取基本类型如下操作:

1
2
int a, b, c;
a = b + c;
 


这里,一个基本类型(不同的变量 int)被施加在加法运算,然后赋值运算符。对于一个基本算术式,这样的操作的意思是一般明显和明确的,但它可能不适合某些类类型是这样。例如:

1
2
3
4
5
struct myclass {
  string product;
  float price;
} a, b, c;
a = b + c;
 


这里,它并不明显什么加法运算的该结果 bc不。事实上,仅此代码会导致编译错误,因为类型 myclass有增加没有定义的行为。然而,C ++允许重载大多数运营商,使他们的行为可以为几乎任何类型的,包括类来定义。这里是可以被重载的所有操作符的列表:

重载操作
+    -    *    /    =    <    >    +=   -=   *=   /=   <<   >>
<<=  >>=  ==   !=   <=   >=   ++   --   %    &    ^    !    |
~    &=   ^=   |=   &&   ||   %=   []   ()   ,    ->*  ->   new 
delete    new[]     delete[]

运营商借助重载 operator他们的名字开始的:功能,这是常规的功能具有特殊名称 operator的关键字后面的 运营商标志的过载。语法是:

type operator sign (parameters) { /*... body ...*/ } 
例如, 笛卡尔向量是一组两个坐标: xy。的两个加法运算 笛卡尔矢量被定义为在加入这两种 x坐标一起,两个 y坐标在一起。例如,添加的 笛卡尔矢量  (3,1)(1,2)一起将导致 (3+1,1+2) = (4,3)。这可以用C ++实现用下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// overloading operators example
#include <iostream>
using namespace std;

class CVector {
  public:
    int x,y;
    CVector () {};
    CVector (int a,int b) : x(a), y(b) {}
    CVector operator + (const CVector&);
};

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

int main () {
  CVector foo (3,1);
  CVector bar (1,2);
  CVector result;
  result = foo + bar;
  cout << result.x << ',' << result.y << '\n';
  return 0;
}
4,3


如果感到困惑的这么多出场 CVector,考虑到其中一些引用类名(即类) CVector和其他一些与该名称功能(即,构造函数,它必须具有相同的名称作为类)。例如:

1
2
CVector (int, int) : x(a), y(b) {}  // function name CVector (constructor)
CVector operator+ (const CVector&); // function that returns a CVector  
 


功能 operator+类的 CVector重载加法运算符( +)为该类型。一旦声明,该功能可以使用隐式操作,或明确使用其功能的名字叫:

1
2
c = a + b;
c = a.operator+ (b);
 


两个表达式是等价的。

该操作符重载是可以有任何的行为只是普通的功能; 实际上存在着由该过载执行操作带有一个相对于操作者的数学或通常含义没有要求,但它强烈推荐。例如,重载一类 operator+实际减去或重载 operator==填充对象用零,是完全有效的,尽管使用这样的类可以是具有挑战性的。

预期一个成员函数过载如操作参数 operator+是自然的操作数的操作者的右手侧。这是共同的所有二进制运算符(那些带有一个操作数到它的左和一个操作数到它的右边)。但是,运营商可以进来形式多样。这里有一个表,需要为每个不同的运营商带来了可以被重载的参数的摘要(请更换 @由每种情况下的运营商):

表达操作者成员函数非成员函数
@a+ - * & ! ~ ++ --A::operator@()operator@(A)
a@++ --A::operator@(int)operator@(A,int)
a@b+ - * / % ^ & | < > == != <= >= << >> && || ,A::operator@(B)operator@(A,B)
a@b= += -= *= /= %= ^= &= |= <<= >>= []A::operator@(B)-
a(b,c...)()A::operator()(B,C...)-
a->b->A::operator->()-
(TYPE) aTYPEA::operator TYPE()-
其中, a是类的一个对象 Ab是类的一个对象 B,并 c是类的对象 CTYPE只是任何类型(即运营商重载转换输入 TYPE)。

请注意,一些运营商可能以两种形式被重载:无论是作为一个部件的功能或作为非成员函数:第一种情况已经在上面的例子中已经使用 operator+。但一些运营商也可以被重载的非成员函数; 在这种情况下,操作函数采用适当的类作为第一个参数的对象。

例如:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// non-member operator overloads
#include <iostream>
using namespace std;

class CVector {
  public:
    int x,y;
    CVector () {}
    CVector (int a, int b) : x(a), y(b) {}
};


CVector operator+ (const CVector& lhs, const CVector& rhs) {
  CVector temp;
  temp.x = lhs.x + rhs.x;
  temp.y = lhs.y + rhs.y;
  return temp;
}

int main () {
  CVector foo (3,1);
  CVector bar (1,2);
  CVector result;
  result = foo + bar;
  cout << result.x << ',' << result.y << '\n';
  return 0;
}
4,3


关键字此

关键字 this表示的指针,其成员功能正在执行的对象。它是一个类的成员函数中用来指对象本身。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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;
}
是的,&A是B


它也经常用于 operator=通过引用返回对象的成员函数。与这些示例如下 笛卡尔矢量见过的,它的 operator=功能可能被定义为:

1
2
3
4
5
6
CVector& CVector::operator= (const CVector& param)
{
  x=param.x;
  y=param.y;
  return *this;
}
 


实际上,这个功能是非常类似于编译器隐式生成用于此类的代码 operator=

静态成员

一个类可以包含静态成员,无论是数据或功能。

一类的静态数据成员也被称为“类变量”,因为只有一个用于同一类的所有对象共同变量,共享相同的值:即,其值不从该一个目的不同类到另一个。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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;
}
6
7


事实上,静态成员具有相同的属性为非成员变量,但他们享受类范围。出于这个原因,为了避免这些被宣布数次,它们不能直接在类进行初始化,而是需要它之外的某处被初始化。如前面的例子中:

 
int Dummy::n=0;
 


因为它是同一类的所有对象共同的变量值,它可以被称作该类的任何对象或甚至直接通过类名的一个成员(当然这是仅适用于静态成员):

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


这两个以上的呼叫是指同一个变量:静态变量 n类中 Dummy这个类的所有对象共享。

再次,这是一样的非成员变量,但是,需要象类的成员(或对象),以访问一个名称。

类也可以有静态成员函数。这些代表是相同的:一类是常见的该类的所有对象成员,正是作为非成员函数,但像类的成员被访问。因为他们像非成员函数,它们不能访问类的非静态成员(既不成员变量,也没有成员函数)。他们既可以使用关键字 this

const成员函数

当一个类的一个目的是限定为一个 const对象:

 
const MyClass myobject;
 


从类之外的数据成员的访问限制为只读,仿佛所有的数据成员 const为那些从类的外部访问它们。但请注意,该构造仍称并允许初始化和修改这些数据成员:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 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;
}
10


一个的成员函数 const的对象可以当它们本身指定为只能被称为 const成员; 在上面的例子中,构件 get(其没有被指定为 const)不能从调用 foo。要指定一个成员是一个 const成员, const关键字应遵循的函数原型,它的参数右括号后:

 
int get() const {return x;}
 


注意, const可以用于限定由成员函数的返回类型。这 const是不一样的其中之一指定的成员如 const。两者都是独立的,位于在函数原型不同的地方:

1
2
3
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对象没有限制,并且因此能够访问 const和非 const成员函数一样。

你可能会认为,反正你很少去申报 const对象,因而标记为const是不值得的努力,但const对象其实很普遍,不修改对象的所有成员。大多数功能服用类作为参数实际上把它们通过 const参考,因此,这些功能只能访问他们的 const成员:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 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;
}
10


如果在本实施例中, get没有被指定为一个 const部件,该呼叫到 arg.get()print功能将是不可能的,因为 const对象只能访问 const成员函数。

成员函数可以在其常量性超载:即,一类可具有除了一个相同的签名两个成员函数是 const和另一种是不:在此情况下, const版本称为仅当对象是本身常量,并且非-  const当对象是本身非版本称为 const

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 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;
}
15
20


类模板

就像我们可以创建功能模板,我们还可以创建类模板,允许类具有使用模板参数作为类型的成员。例如:

1
2
3
4
5
6
7
8
9
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 <...>前缀:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
// 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;
}
100


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

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


这么多困惑 T的?有三个 T的本宣言:第一个是模板参数。第二种 T是指由函数的返回类型。而第三 T(尖括号之间的)也是有要求的:它指定了该功能的模板参数也是类模板参数。

模板特

有可能当特定类型作为模板参数传递来定义不同的实现的模板。这就是所谓的 模板特殊化

例如,假设我们有一个叫做非常简单的类 mycontainer可以存储任何类型的一个元素,且只有一个叫做成员函数 increase,这增加了它的价值。但是,我们发现,当它存储类型的元素 char将是有一个完全不同的执行情况,函数成员更方便 uppercase,所以我们决定宣布该类型的类模板特殊化:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 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;
}
8
Ĵ


这是用于类模板专门的语法:

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


首先,注意到我们与前面的类名 template<>,包括空参数列表。这是因为所有类型是已知的并没有模板参数都需要这种专业化,不过,它是一类模板的专业化,因此它需要要注意这样。

但是,除了这个前缀更重要的,是 <char>类模板名称后面专业化参数。这种专业化参数本身标识该模板类被专业类型( char)。注意泛型类模板和专业化之间的区别:

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


第一行是通用模板,而第二个是专业化。

当我们为一个模板类声明专业,我们还必须定义它的所有成员,即使是那些相同的通用模板类,因为没有从通用模板向专业化成员的“继承”。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值