类(I)

是一个扩大的概念 的数据结构 :类似的数据结构,它们可以包含数据成员,但是它们也可以包含作为成员。

一个 对象是类的一个实例。在变量而言,一个类将是类型,并且一个目的将是可变的。

班使用的是关键字定义 class或关键字 struct,语法如下:

CLASS_NAME类{
  access_specifier_1:
    member1中;
  access_specifier_2:
    member2;
  ...
} object_names;

哪里 class_name是类的有效识别, object_names是名字对于这个类的对象的可选列表。声明的主体可以包含 的成员,这可以是数据或函数声明,和可选的 访问说明

类具有相同的格式作为纯 数据结构,所不同的是它们也可以包括功能和具有这些新事物称为 访问说明。一个 访问说明符是以下三个关键字之一: privatepublicprotected。这些说明修改该跟着他们的成员访问权限:

  • private一类的成员(或从他们只能从同一类的其他成员中访问“朋友”)。
  • protected成员分别来自(或从同一个类中的其他成员访问“朋友”),但也从他们的派生类的成员。
  • 最后,public成员均来自该对象是可见的任何地方访问。

默认情况下,该声明的类的所有成员 class的关键字有其所有成员的私人通道。因此,在任何其他之前声明任何成员 访问说明符自动拥有私人通道。例如:

1
2
3
4
5
6
class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area (void);
} rect;
 


声明了一个名为类(即A型) Rectangle和这个类的一个对象(即变量),称为 rect。这个类包含四个成员:类型的两个数据成员 int(成员 width和成员 height)与 私有访问(因为私人是默认的访问级别)和两个成员函数与 公共访问:功能 set_valuesarea,这对于现在的我们只包括他们的声明,但不是他们的定义。

注意到的区别 类名对象名称:在前面的例子中, Rectangle类名(即,类型),而 rect为类型的对象 Rectangle。这是相同的关系 int,并 a在以下声明:

 
int a;
 


其中, int是的类型名称(类)和 a是变量名(对象)。

的声明之后 Rectanglerect任何对象的公共成员 rect可以如同它们是正常功能或正常变量进行访问,通过简单地插入一个点( .之间) 对象名称成员名称。在此之前,相同的语法访问普通数据结构的成员。例如:

1
2
rect.set_values (3,4);
myarea = rect.area(); 
 


唯一的成员 rect不能从类外部访问是 widthheight,因为他们有私人通道,他们只能从同一类的其他成员中提到。

下面是类Rectangle的完整的例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// classes example
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area() {return width*height;}
};

void Rectangle::set_values (int x, int y) {
  width = x;
  height = y;
}

int main () {
  Rectangle rect;
  rect.set_values (3,4);
  cout << "area: " << rect.area();
  return 0;
}
面积:12


这个例子再次引入了 范围操作符::,两个冒号),在有关的命名空间前面的章节中看到。在这里它是在功能定义中使用 set_values定义类本身以外的类的成员。

请注意,成员函数的定义 area已经直接包含在类的定义中 Rectangle给予其极致简约。相反, set_values它仅仅与类内它的原型声明的,但其定义是它之外。在此之外的定义,范围操作符( ::)被用于指定所定义的功能是类的一个成员 Rectangle,而不是常规的非成员函数。

范围操作符( ::)指定该成员被宣布所属授予完全相同范围相同的属性,如果这个函数的定义直接包含在类定义中的类。例如,该功能 set_values在前面的例子中有权访问变量 widthheight,它们是类的私有成员 Rectangle,因此只能从类的其他成员,如此访问。

完全定义在类定义中的成员函数,或者只是在函数的声明,以后再定义它的类之外的唯一区别是,在第一种情况下的功能被自动认为是 内联由编译器的成员函数,而在第二个它是一个正常的(不是内联)类成员函数。这会导致行为没有差异,但只有在可能的编译器优化。

会员 widthheight私人的访问(请记住,如果指定没有别的,用关键字定义一个类的所有成员 class具有私有访问权限)。通过声明为私有,从类的外部访问是不允许的。这是有道理的,因为我们已经定义了一个成员函数为对象范围内的成员设定值:成员函数 set_values。因此,该程序的其余部分并不需要必须将它们直接访问。也许在一个这样简单的例子,因为这,就很难看到如何限制访问这些变量可能是有用的,但在更大的项目可能是非常重要的值不能以意想不到的方式(从图的点意外被修改物体)。

一类的最重要的特性是,它是一个类型,因此,我们可以声明它的多个对象。例如,用类的前面的例子下面 Rectangle,我们可以声明的对象 rectb除对象 rect

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// example: one class, two objects
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    void set_values (int,int);
    int area () {return width*height;}
};

void Rectangle::set_values (int x, int y) {
  width = x;
  height = y;
}

int main () {
  Rectangle rect, rectb;
  rect.set_values (3,4);
  rectb.set_values (5,6);
  cout << "rect area: " << rect.area() << endl;
  cout << "rectb area: " << rectb.area() << endl;
  return 0;
}
矩形面积:12
rectb面积:30  


在这种特殊情况下,类(类对象)是 Rectangle,其中有两个实例(即对象): rectrectb。他们每个人都有自己的成员变量和成员函数。

请注意,调用 rect.area()不给相同的结果调用 rectb.area()。这是因为类的每个对象 Rectangle都有其自己的变量 widthheight,因为他们-in一些方式-也有他们自己的函数成员 set_value,并 area在对象自己的成员变量进行操作。

类允许编程使用面向对象的范式:数据和函数是对象的成员,减少了需要通过并执行处理或其他状态变量作为函数的参数,因为它们的成员称为对象的一部分。请注意,没有论据的调用传递 rect.arearectb.area。这些成员函数直接使用其各自的对象的数据成员 rectrectb

构造函数

在前面的例子中会发生什么,如果我们所谓的成员函数 area已经调用之前 set_values?一个不确定的结果,因为成员 widthheight从未被赋值。

为了避免这种情况,一个类可以包含一个特殊的函数调用它的 构造函数,只要创建这个类的一个新对象,该对象被自动调用,允许类初始化成员变量或分配存储空间。

此构造函数声明就像一个普通的成员函数,但与相匹配的类名,没有任何返回类型的名称; 甚至没有 void

Rectangle级以上可以很容易地通过实现一个构造进行改进:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// example: class constructor
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle (int,int);
    int area () {return (width*height);}
};

Rectangle::Rectangle (int a, int b) {
  width = a;
  height = b;
}

int main () {
  Rectangle rect (3,4);
  Rectangle rectb (5,6);
  cout << "rect area: " << rect.area() << endl;
  cout << "rectb area: " << rectb.area() << endl;
  return 0;
}
矩形面积:12
rectb面积:30  


本实施例的结果是相同的前面的例子的。但现在,类 Rectangle没有成员函数 set_values,并拥有而不是执行类似行动的构造函数:它初始化的值 width,并 height与传递给它的参数。

请注意这些参数是如何传递给在提出这一类的对象被创建的时刻的构造:

1
2
Rectangle rect (3,4);
Rectangle rectb (5,6);
 


构造可以好像他们是普通成员函数不能被显式调用。创建该类的一个新对象时,它们只执行一次。

请注意如何既不是构造原型声明(类中),也不是后者的构造器的定义,有返回值; 连 void:构造永远不会返回值,他们只是初始化对象。

重载构造函数

像任何其他功能,构造也可以与不同的版本采取不同的参数重载:具有不同数目的参数和/或不同类型的参数。编译器会自动调用其参数匹配的参数之一:

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
// overloading class constructors
#include <iostream>
using namespace std;

class Rectangle {
    int width, height;
  public:
    Rectangle ();
    Rectangle (int,int);
    int area (void) {return (width*height);}
};

Rectangle::Rectangle () {
  width = 5;
  height = 5;
}

Rectangle::Rectangle (int a, int b) {
  width = a;
  height = b;
}

int main () {
  Rectangle rect (3,4);
  Rectangle rectb;
  cout << "rect area: " << rect.area() << endl;
  cout << "rectb area: " << rectb.area() << endl;
  return 0;
}
矩形面积:12
rectb面积:25  


在上面的例子中,类的两个对象 Rectangle构造: rectrectbrect构造有两个参数,就像之前的例子。

但这个例子也引入了一种特殊的构造函数:在 默认构造函数。该 默认构造函数是不带参数的构造,这是特殊的,因为当一个对象被声明,但不与任何参数初始化的叫法。在上面的例子中, 默认的构造函数被调用 rectb。注意,如何 rectb不即使空括号的构建-事实上,空括号不能用来调用默认的构造函数:

1
2
Rectangle rectb;   // ok, default constructor called
Rectangle rectc(); // oops, default constructor NOT called 
 


这是因为空括号会使得 rectc一个函数声明,而不是一个对象声明:这将是一个函数,它没有参数和返回类型的值 Rectangle

统一初始化

用括号括它们的参数调用构造函数,如上图所示,的方式被称为 函数形式。:但构造也可以用其他的语法被称为

第一,用一个参数的构造函数可以使用变量初始化语法(等号后面的参数)被称为:

class_name object_name = initialization_value; 

最近,C ++引入构造的可能性,使用被称为 统一初始化,它本质上是相同的函数形式,但使用括号( {})代替括号( ()):

class_name object_name { value, value, value, ... } 

任选地,这个最后的语法可以包括括号前一个等号。

下面是四种方法构造了一类的构造函数只有一个参数的对象的例子:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// classes and uniform initialization
#include <iostream>
using namespace std;

class Circle {
    double radius;
  public:
    Circle(double r) { radius = r; }
    double circum() {return 2*radius*3.14159265;}
};

int main () {
  Circle foo (10.0);   // functional form
  Circle bar = 20.0;   // assignment init.
  Circle baz {30.0};   // uniform init.
  Circle qux = {40.0}; // POD-like

  cout << "foo's circumference: " << foo.circum() << '\n';
  return 0;
}
Foo的围:62.8319


统一在初始化函数形式的优点是,与括号,括号不能混为一谈与函数声明,因而可以用来显式调用默认构造:

1
2
3
Rectangle rectb;   // default constructor called
Rectangle rectc(); // function declaration (default constructor NOT called)
Rectangle rectd{}; // default constructor called 
 


语法的选择调用构造函数主要是风格问题。大多数现有的代码目前使用功能的形式,一些较新的风格指南建议选择在他人统一初始化,尽管它也有其潜在的缺陷对其的偏好 initializer_list作为其类型。

在构造函数初始化成员

当一个构造函数是用来初始化其他成员,这些其他成员可以直接初始化,而不诉诸于它的身体语句。这是通过将完成的,在构造的身体之前,冒号( :)和初始化类成员的列表。例如,考虑以下声明一个类:

1
2
3
4
5
6
class Rectangle {
    int width,height;
  public:
    Rectangle(int,int);
    int area() {return width*height;}
};
 


这个类的构造函数可以被定义,像往常一样,如:

 
Rectangle::Rectangle (int x, int y) { width=x; height=y; }
 


但是,它也使用被定义 成员初始化为:

 
Rectangle::Rectangle (int x, int y) : width(x) { height=y; }
 


甚至:

 
Rectangle::Rectangle (int x, int y) : width(x), height(y) { }
 


注意如何在后一种情况下,构造确实没有什么比初始化它的成员一样,因此它有一个空的函数体。

对于基本类型的成员,这使得它的构造上述方式被定义,因为它们不是默认情况下初始化没有区别,但对于成员对象(那些类型是一个类),如果他们没有冒号后初始化,他们是缺省构造。

缺省构造一个类的所有成员可能会或可能总是不便利:在某些情况下,这是一种浪费(当构件随后在构造重新初始化以其他方式),但在某些其他情况下,默认施工甚至是不可能(当类没有默认构造函数)。在这种情况下,成员应在成员初始化列表来初始化。例如:

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
// member initialization
#include <iostream>
using namespace std;

class Circle {
    double radius;
  public:
    Circle(double r) : radius(r) { }
    double area() {return radius*radius*3.14159265;}
};

class Cylinder {
    Circle base;
    double height;
  public:
    Cylinder(double r, double h) : base (r), height(h) {}
    double volume() {return base.area() * height;}
};

int main () {
  Cylinder foo (10,20);

  cout << "foo's volume: " << foo.volume() << '\n';
  return 0;
}
Foo的量:6283.19


在这个例子中,类 Cylinder有一个成员对象,其类型是另一个类( base的类型是 Circle)。由于类的对象 Circle只能通过参数来构建, Cylinder的构造需要调用 base的构造函数,而要做到这一点的唯一方法是在 成员初始化列表

这些初始化也可以使用统一的初始化语法,使用大括号 {}代替括号 ()

 
Cylinder::Cylinder (double r, double h) : base{r}, height{h} { }
 


指针类

对象也可以通过指针指向:一旦声明,一类成为一个有效的类型,因此它可以作为类型所指向的指针使用。例如:

 
Rectangle * prect;
 


是指向类的对象 Rectangle

类似地,与普通的数据结构,对象的成员可以直接从一个指针通过使用箭头操作符访问( ->)。下面是一些可能的组合,例如:

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
// pointer to classes example
#include <iostream>
using namespace std;

class Rectangle {
  int width, height;
public:
  Rectangle(int x, int y) : width(x), height(y) {}
  int area(void) { return width * height; }
};


int main() {
  Rectangle obj (3, 4);
  Rectangle * foo, * bar, * baz;
  foo = &obj;
  bar = new Rectangle (5, 6);
  baz = new Rectangle[2] { {2,5}, {3,6} };
  cout << "obj's area: " << obj.area() << '\n';
  cout << "*foo's area: " << foo->area() << '\n';
  cout << "*bar's area: " << bar->area() << '\n';
  cout << "baz[0]'s area:" << baz[0].area() << '\n';
  cout << "baz[1]'s area:" << baz[1].area() << '\n';       
  delete bar;
  delete[] baz;
  return 0;
}	


这个例子利用几个经营者的对象和指针操作(运营商 *&.->[])。他们可以解释为:

表达可以理解为
*x指向 x
&x地址 x
x.y成员y对象x
x->y成员y对象的指向x
(*x).yy对象的指向x(相当于以前的一个)
x[0]第一个对象指向 x
x[1]第二个对象指向 x
x[n]n+1)个对象指向x

大多数这些表达式已经在前面的章节被引入。最值得注意的是,关于阵列章介绍了偏移运算符( [])和有关(介绍了箭头操作符纯数据结构的一章 ->)。

与struct和union定义的类

类可以定义不仅与关键字 class,而且还与关键字 structunion

关键字 struct,通常用于声明普通的数据结构,还可以用于声明有成员函数的类,用相同的语法与关键字 class。两者之间的唯一差别是,与关键字声明的类的成员 struct具有 public默认访问,而与关键字声明的类的成员 class具有 private默认的访问。对于所有其他目的,两个关键字可以在这方面等同的。

相反,这个概念 工会是从与声明的类不同 structclass,由于工会只存储一次一个数据成员,但尽管如此,它们也是类,因此可以还容纳构件的功能。工会类的默认访问 public
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值